Git 使用指南

xigua 2019-06-28

git init

初始化

git init

git clone

克隆

git clone [远端仓库] [目标文件夹名称(默认:远端仓库的名字)]

git log

查看历史提交

  • 当前分支
  • 按时间先后顺序显示到[校验和]为止
git log [校验和(默认:最新提交的校验和)]

选项:

  • --oneline:精简至单行版本
  • --stat:增加文件修改信息
  • -p:忽略空格引起的不同
  • -w:忽略空格引起的不同
  • --all --graph:用分支图显示所有历史提交
  • --author="[作者]":显示作者包含[文本]的历史提交
  • --grep="[文本]":显示内容包含[文本]的历史提交

希望选项--all --graph成为命令git log的默认参数?

在文件.bashrc中加入:

git() {
    if [ "$1" = "log" ]
    then
        command git log --all --graph "${@:2}";
    else
        command git "$@";
    fi;
}

Enabling git log parameters by default

git shortlog

查看历史提交:按作者分类显示

git shortlog

git show

查看[校验和]提交的所有信息

git show [校验和(默认:最新提交的校验和)]

查看附注标签(Annotated Tag)的内容

git tag [标签名]

选项同命令git log

git status

查看当前状态

git status

git diff

查看尚未加入暂存区的改动

git diff

git stash

Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the git stash command.

经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作。问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。解决这个问题的办法就是git stash命令。

保存工作区的更改到暂存,恢复工作区至上次提交。

git stash

查看所有暂存

git stash list

恢复暂存到工作区

git stash apply [暂存记录(默认:最新暂存记录)]

恢复暂存到工作区,同时删除暂存记录。

git stash pop [暂存记录(默认:最新暂存记录)]

git add

将文件从工作区添加到暂存区

git add [文件名1 文件名2 文件名3 ...]

将所有文件(文件.gitignore指示的除外)从工作区添加到暂存区

git add .

git rm

将文件从暂存区移回到工作区

git rm --cached [文件名1 文件名2 文件名3 ...]

git commit

将暂存区提交到本地仓库

  • 会进入一个 vim 窗口,在此输入提交信息。
  • 所有以#开头的行都会被忽略。
  • 对于空行的问题无需纠结,因为 git 会忽略所有不必要的空行。
git commit

键入提交信息并提交

git commit -m [提交信息]

更新最近的提交(内容和信息)

git commit --amend

如修改了提交内容,建议先执行:

git add .

提交的本质是区块链的节点,每个节点存储了上一个提交(父提交)的校验和。父提交可能是1个(通常),也可能是2个(分支的合并提交)。

提交信息的书写规范(Udacity

The goal is that each commit has a single focus. Each commit should record a single-unit change. Now this can be a bit subjective (which is totally fine), but each commit should make a change to just one aspect of the project.

Conversely, a commit shouldn't include unrelated changes - changes to the sidebar and rewording content in the footer. These two aren't related to each other and shouldn't be included in the same commit. Work on one change first, commit that, and then change the second one. That way, if it turns out that one change had a bug and you have to undo it, you don't have to undo the other change too.

The best way that I've found to think about what should be in a commit is to think, "What if all changes introduced in this commit were erased?". If a commit were erased, it should only remove one thing.

DO:

  • do keep the message short (less than 60-ish characters)
  • do explain what the commit does (not how or why!)

DO NOT:

  • do not explain why the changes are made (more on this below)
  • do not explain how the changes are made (that's what git log -p is for!)
  • do not use the word "and"

    • if you have to use "and", your commit message is probably doing too many changes - break the changes into separate commits
    • e.g. "make the background color pink and increase the size of the sidebar"

完整版文档:Udacity Git Commit Message Style Guide

git tag

查看所有标签

git tag

添加轻便标签(Lightweight Tag)

git tag [标签名] [校验和(默认:最新提交的校验和)]

添加附注标签(Annotated Tag)

  • 会进入一个 vim 窗口,在此输入标签内容。
  • 所有以#开头的行都会被忽略。对于空行的问题无需纠结,
  • 因为 git 会忽略所有不必要的空行。
git tag -a [标签名] [校验和(默认:最新提交的校验和)]

键入附注标签的内容并提交

git tag -a [标签名] [校验和(默认:最新提交的校验和)] -m [附注标签的内容]

删除本地标签

git tag -d [标签名1 标签名2 标签名3 ...]

删除指定远端标签

git push [远端名] -d refs/tags/[标签名]
git push [远端名] :refs/tags/[标签名]

推送所有本地标签到远端

git push [远端名] --tag

从远端拉取所有标签到本地

git fetch [远端名] --tag

在远端唯一的情况下,上述[远端名]可以省略。

标签的本质是引用(ref),指向一个提交或一个分支。

git checkout

切换

git checkout [校验和 / 标签名 / 分支名]

创建新分支并切换

git checkout -b [分支名]

恢复工作区中的某个文件到上次提交的状态

git checkout -- [文件名]

恢复工作区中的全部文件到上次提交的状态

git checkout -- .

git branch

查看所有本地分支

git branch

查看所有远端分支

git branch -r

新建分支

git branch [分支名] [校验和(默认:最新提交的校验和)]

删除分支

git branch -d [分支名1 分支名2 分支名3 ...]

删除分支的前提条件是:

  • 该分支不是当前分支。
  • 该分支上的最新提交已经合并到另一分支上(如需忽略本条件,请使用-D选项。警告:尚未合并的所有提交将会全部丢失!)。

分支的本质是引用,指向一个提交。Git 将分支理解为从首个提交到指向提交的序列

git merge

[源分支]合并到当前分支,通常有两种情况:

  • [源分支]是当前分支的更新:直接移动当前分支的 ref。
  • 创建[源分支]后,又当前分支上新建了提交:创建一个合并提交,将[源分支]和当前分支的 ref 指向这个合并提交。
git merge [源分支]

合并操作操作会因为冲突(conflict)而中止,条件是:参与合并的两个分支上,各存在一个提交,它们修改了同一个行范围。

如需撤销合并操作,请使用命令git merge --abort

冲突的处理方法(Udacity

Merge Conflict Output Explained

The output that shows in the Terminal is:

$ git merge heading-update
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

Notice that right after the git merge heading-update command, it tries merging the file that was changed on both branches (index.html), but that there was a conflict. Also, notice that it tells you what happened - "Automatic merge failed; fix conflicts and then commit the result".

Remember our good friend git status? Well he'll come in really handy when working with merge conflicts.

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file> ..." to mark resolution)

        both modified:   index.html

The git status output tells us to that the merge conflict is inside index.html. So check out that file in your code editor!

Git 使用指南

Merge Conflict Indicators Explanation

  • <<<<<<< HEAD everything below this line (until the next indicator) shows you what's on the current branch
  • ||||||| merged common ancestors everything below this line (until the next indicator) shows you what the original lines were
  • > > > > > > > heading-update is the ending indicator of what's on the branch that's being merged in (in this case, the heading-update branch)

Resolving A Merge Conflict

Git is using the merge conflict indicators to show you what lines caused the merge conflict on the two different branches as well as what the original line used to have. So to resolve a merge conflict, you need to:

  • choose which line(s) to keep
  • remove all lines with indicators

For some reason, I'm not happy with the word "Crusade" right now, but "Quest" isn't all that exciting either. How about "Adventurous Quest" as a heading?!?

Git 使用指南

Commit Merge Conflict

Once you've removed all lines with merge conflict indicators and have selected what heading you want to use, just save the file, add it to the staging index, and commit it! Just like with a regular merge, this will pop open your code editor for you to supply a commit message. Just like before, it's common to use the provided merge commit message, so after the editor opens, just close it to use the provided commit message.

And that's it! Merge conflicts really aren't all that challenging once you understand what the merge conflict indicators are showing you.

If a merge conflict occurs in a file and you edit the file, save it, stage it, and commit it but forget to remove the merge conflict indicators, Git will commit the lines with the merge conflict indicators, because they're just regular characters.

git revert

git revert [校验和]

[校验和]提交的前一个提交为PREVIOUS,修改的行范围是range

该命令的作用是回滚range范围内的修改,将会产生一个新的提交

回滚操作操作会因为冲突而中止,条件是:如果range范围内的某一部分在[校验和]提交的某个后续提交中被修改。

如需撤销回归操作,请使用命令git revert --abort

冲突的处理方法与命令git merge相同。

git reset

git reset [校验和]

该命令的作用是回到[校验和]提交的状态,即撤销[校验和]提交的后续提交的所有修改modification

选项:

  • --mixed:将modification移入工作区(默认)
  • --soft:将modification移入暂存区
  • --hard:删除modification

执行命令git reset之后,[校验和]提交的后续提交未被删除。

可以通过下列命令查看这些不属于任何分支的提交

  • git relog
  • git fsck --unreachable --no-reflog

(具体使用方法详见后文。)

可以通过切换到提交、新建分支、合并到现有分支的方法恢复这些提交。

相对引用(Udacity

Relative Commit References

You already know that you can reference commits by their SHA, by tags, branches, and the special HEAD pointer. Sometimes that's not enough, though. There will be times when you'll want to reference a commit relative to another commit. For example, there will be times where you'll want to tell Git about the commit that's one before the current commit...or two before the current commit. There are special characters called "Ancestry References" that we can use to tell Git about these relative references. Those characters are:

  • ^ – indicates the parent commit
  • ~ – indicates the first parent commit

Here's how we can refer to previous commits:

  • the parent commit – the following indicate the parent commit of the current commit

    • HEAD^
    • HEAD~
    • HEAD~1
  • the grandparent commit – the following indicate the grandparent commit of the current commit

    • HEAD^^
    • HEAD~2
  • the great-grandparent commit – the following indicate the great-grandparent commit of the current commit

    • HEAD^^^
    • HEAD~3

The main difference between the ^ and the ~ is when a commit is created from a merge. A merge commit has two parents. With a merge commit, the ^ reference is used to indicate the first parent of the commit while ^2 indicates the second parent.

  • The first parent is the branch you were on when you ran git merge
  • The second parent is the branch that was merged in.

git remote

查看所有远端仓库的 URL

git remote -v

关联到远端仓库:[远端仓库的别名]通常被设为origin

git remote add [远端仓库的别名] [远端仓库的URL]

取消与远端仓库的关联

git remote remove [远端仓库的别名]

重命名远端仓库

git remote rename [旧名称] [新名称]

git push

将本地分支推送到远端

git push [远端名] [本地分支] [远端分支]

省略[远端分支]的情形:将[本地分支]推送到同名的远程分支,如果该远程分支不存在,则会被新建。

git push [远端名] [本地分支]

可以为该命令添加-u参数,表示在推送的同时,设定远端名中的远端分支[本地分支]的 upstream branch。同样,同名的远端分支可以省略。

git push -u [远端名] [本地分支] [远端分支]
git push -u [远端名] [本地分支]

在执行上述命令后,就可以切换到相应本地分支(命令git checkout)并直接使用命令git push了。

git push

如果出现各种原因导致命令git push不能成功执行(例如:删除并重建本地分支、修改本地分支上的历史提交),可以使用选项-f进行覆盖(强行)推送。不要在多人合作的远端分支中使用!!!原因参见:

  • 团队开发里频繁使用 git rebase 来保持树的整洁好吗?
  • git 如何撤销一次remote的master commit?
git push -f

删除远端分支

git push [远端名] -d [远端分支]
git push [远端名] :[远端分支]

git pull

将远端分支拉取到本地:取回远程主机某个分支的更新,再与本地的指定分支合并

git pull [远端名] [远端分支]:[本地分支]

在满足下列条件的前提下,可以直接使用命令git pull

  • 已使用命令git checkout将该对应的本地分支切换为当前分支。
  • 该分支的 upstream branch 已设置。有两种方法:

    • 执行命令git branch --set-upstream-to=[远端名]/[远端分支] [本地分支]
    • 借助带选项-u的命令git push
git pull

git fetch

将远端分支拉取到本地:取回远程主机某个分支的更新,但是不与本地分支合并

git fetch [远端名] [远端分支]:[本地分支]

在满足下列条件的前提下,可以直接使用命令git fetch

  • 已使用git checkout将该对应的本地分支切换为当前分支。
  • 该分支的 upstream branch 已设置。有两种方法:

    • 命令git branch --set-upstream-to=[远端名]/[远端分支] [本地分支]
    • 借助git push命令的选项-u
git fetch

区别于命令git pull

  • 命令git fetch让用户决定怎样合并远端分支和本地分支。
  • 命令git pull自动进行分支合并,在某些情况下可能无法成功执行。

problem

本地分支和远端分支被同时更新。

更新前:

master
                                    |
remote     a -- 3 -- d -- f -- e -- 7

============================================================

                              origin/master
                                    |
local      a -- 3 -- d -- f -- e -- 7
                                    |
                                  master

更新后:

master
                                          |
remote      a -- 3 -- d -- f -- e -- 7 -- 8

============================================================

                               origin/master
                                     |
local       a -- 3 -- d -- f -- e -- 7 -- b
                                          |
                                        master

Solution

首先先使用命令git fetch,获取远端分支更新。

master
                                          |
remote      a -- 3 -- d -- f -- e -- 7 -- 8

============================================================

                                    origin/master
                                          |
                                          8
                                        /
local       a -- 3 -- d -- f -- e -- 7 -- b
                                          |
                                        master

再使用命令git merge origin/master,将远端分支合并到本地分支。

master
                                          |
remote      a -- 3 -- d -- f -- e -- 7 -- 8

============================================================

                                    origin/master
                                          |
                                          8
                                        /   \
local       a -- 3 -- d -- f -- e -- 7 -- b -- 4
                                               |
                                             master

最后使用命令git push,推送本地分支到远端分支。

master
                                               |
                                          8    |
                                        /   \  |
remote      a -- 3 -- d -- f -- e -- 7 -- 8 -- 4

============================================================

                                         origin/master
                                               |
                                          8    |
                                        /   \  |
local       a -- 3 -- d -- f -- e -- 7 -- b -- 4
                                               |
                                             master

git rebase

将当前分支的修改应用到目标分支,步骤是:

  1. 找出同时位于两个分支最新的提交最新提交(最新公共提交)。
  2. 将当前分支上最新公共提交后的所有提交,移动到目标分支后。
  3. 将当前分支指向最新提交。
git rebase [目标分支]

参考资料:

[校验和]后的提交进行交互式修改(vim)。

git rebase -i [校验和]

参考资料:

中止上述操作

git rebase --abort
Do not rebase commits that exist outside your repository.

If you follow that guideline, you’ll be fine. If you don’t, people will hate you, and you’ll be scorned by friends and family.

不要对在你的仓库外有副本的分支执行变基。

如果你遵循这条金科玉律,就不会出差错。 否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。

其他

参考资料:https://www.worldhello.net/go...

操作记录

查看90天内的操作记录

git reflog

立即清除所有操作记录(不推荐)

git reflog expire --expire=now --all

查看无法显示的对象

查看无法显示(unreachable)的对象,包括但不限于:

  • 暂存区操作时引入的临时对象(以unreachable blob开头)
  • 不在分支上的提交(以unreachable commit开头)
git fsck --unreachable --no-reflog

清除对象

清除对象,通常是暂存区操作时引入的临时对象。操作记录中的对象不在清理范围。

git prune

优化仓库

优化仓库

git gc

相关推荐