基本功能
checkout 主要作用有两个:切换分支和重置文件。
切换分支
切换分支时,会使用新分支指向的结点更新暂存区与工作目录。
正常切换
可以使用 git checkout <branch> 切换分支。其中 branch 表示要切换到的分支名。
我们也可以使用 git reset <branch>。但它与 checkout 时的含义完全不同:reset 不会更改分支,只是修改当前分支指向的结点;而 checkout 会更新分支,但旧分支指向的结点不会变化。下图展示了 git reset master 与 git checkout master 的区别:
从上图可以发现:reset 之后,develop 分支指向的结点发生了变化,但 HEAD (HEAD 表示当前的分支)没有变;而 checkout 后,develop 指向的结点没变,但 HEAD 指向发生了变化。
匿名分支
checkout 后不但可以跟分支名,还可以跟某个提交结点的 SHA-1 值,此时相当于在指定的提交结点处建立一个匿名分支,然后将 HEAD 指向该分支。
之所以叫匿名分支,是因为在 refs/heads 目录中并没有新建一个分支文件,只是通过 .git/HEAD 文件保存了该结点的 SHA-1 值。
要注意:当从匿名分支中切出时,匿名分支中的所有提交结点将丢失,因为没有指针指向这些结点,也无法通过别的指针回溯到这些结点。
$ git checkout c9186325368de33dcfed7b4997eaab6a42f6e43b
Note: checking out 'c9186325368de33dcfed7b4997eaab6a42f6e43b'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at c918632... fda
$ git branch
* (HEAD detached at c918632)
dev
master
$ cat .git/HEAD
c9186325368de33dcfed7b4997eaab6a42f6e43b
$ ls .git/refs/heads/
dev master
在 checkout 的第一段提示中说:you can discard any commits you make in this
state without impacting any branches by performing another checkout(当你从当前分支中切走时,本分支所有的提交都将丢失)。
重置文件
checkout 执行重置文件工作时,并不会切换分支 ,即不会修改 HEAD 指向。
重置文件时,checkout 可以指定文件路径,此时类似于 git reset --hard -- paths,但 git 不允许后者的写法。
根据是否指定有提交结点, checkout 重置文件会有两种不同的效果:
-
指定有提交结点,会使用该结点对应的快照重置暂存区与工作目录。
$ cat a.txt 这是用于指定提交结点的 checkout 重置恢复 checkout -- a.txt test checkout this is update after addfd000000addfdasfas $ vim a.txt $ cat a.txt 这是后来修改的 这是用于指定提交结点的 checkout 重置恢复 checkout -- a.txt test checkout this is update after addfd000000addfdasfas $ git checkout HEAD -- a.txt $ cat a.txt 这是用于指定提交结点的 checkout 重置恢复 checkout -- a.txt test checkout this is update after addfd000000addfdasfas $ git status On branch dev nothing to commit, working directory clean
上述命令分为四步:
显示上次提交后 a.txt 的内容。
修改 a.txt,只是在文件的第一行中添加了 "这是后来修改的"。
使用 checkout HEAD 重置 a.txt。可以发现重置后 a.txt 恢复到修改之前的样子。
最后使用 git status 查看状态,可以发现 dev 分支是干净的,即没有需要暂存的,也没有需要提交的 —— 这表示本地仓库、暂存区、工作目录三者是一致的。
可以将重置时的 HEAD 替换成任意提交结点的 SHA-1 值。
-
未指定提交结点,会使用暂存区中的内容重置工作目录。
$ vim a.txt $ git status On branch dev Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: a.txt no changes added to commit (use "git add" and/or "git commit -a") $ git checkout -- a.txt $ git status On branch dev nothing to commit, working directory clean
可以看出,使用 git checkout -- a.txt 后,a.txt 并没有出现在待暂存列表中。这是因为 checkout 使用暂存区中的文件重置了工作目录中的 a.txt ,暂存区与工作目录中的 a.txt 完全一样,不需要再次暂存。
冲突文件
--conflict
通过指定 --conflict 选项的值,用于指定检出冲突文件的内容。
下表列出了 conflict 选项常用的值
属性值 | 含义 |
---|---|
merge | 列出本分支,被合并分支的冲突内容。默认值 |
diff3 | 在 merge 基础上再列出最近公共祖先结点的内容 |
--diff3 时的效果:
$ git checkout --conflict=diff3 a.txt
hufeng@hufengdeMacBook-Pro:~/Desktop/demo$ cat a.txt
<<<<<<< ours
from maste2
||||||| base
from maste
=======
from dev
>>>>>>> theirs
可以看出,与普通冲突文件的内容相比,多了 ||||||| base
行,而该行就是公共祖先结点在冲突处的内容。
--ours 与 --theirs
与 --conflict 类似,ours 与 theirs 也是作用于冲突时冲突文件的内容的选项。
--ours 是保留本分支的内容,抛弃被合并分支;
--theirs 保留被合并分支内容,抛弃本分支内容。