git工具-6-checkout
检出与重置的区别
和 reset
一样,checkout
也操纵三棵树,不过它有一点不同,这取决于你是否传给该命令一个文件路径。
不带路径检出
运行 git checkout [branch]
与运行 git reset --hard [branch]
非常相似,它会更新所有三棵树使其看起来像 [branch]
,不过有两点重要的区别:
-
首先不同于
reset --hard
,checkout
对工作目录是安全的,它会通过检查来确保不会将已更改的文件吹走。 其实它还更聪明一些。它会在工作目录中先试着简单合并一下,这样所有_还未修改过的_文件都会被更新。 而reset --hard
则会不做检查就全面地替换所有东西。 -
第二个重要的区别是如何更新
HEAD
。reset
会移动HEAD
分支的指向,而checkout
只会移动HEAD
自身来指向另一个分支。
例如,假设我们有 master
和 develop
分支,它们分别指向不同的提交;我们现在在 develop
上(所以 HEAD
指向它)。
1 | $ git log --all --oneline --graph |
如果我们运行 git reset master
,那么 develop
自身现在会和 master
指向同一个提交:
1 | MINGW64 /d/tmp/git-playground1 (develop) |
而如果我们运行 git checkout master
的话,develop
不会移动,HEAD
自身会移动。 现在 HEAD
将会指向 master
:
1 | MINGW64 /d/tmp/git-playground2 (develop) # develop分支 |
所以,虽然在这两种情况下我们都移动 HEAD
使其指向了提交 A,但_做法_是非常不同的。 reset
会移动 HEAD
分支的指向(比如上面的develop
指针),而 checkout
则移动 HEAD
自身,不移动develop
指针。
带路径检出
运行 checkout
的另一种方式就是指定一个文件路径,这会像 reset
一样不会移动 HEAD
。
它就像 git reset [branch] file
那样用该次提交中的那个文件来更新索引(Index
),但是它也会覆盖工作目录中对应的文件。 它就像是 git reset --hard [branch] file
(如果 reset
允许你这样运行的话)- 这样对工作目录并不安全,它也不会移动 HEAD
。
此外,同 git reset
和 git add
一样,checkout
也接受一个 --patch
选项,允许你根据选择一块一块地恢复文件内容。
总结
希望你现在熟悉并理解了 reset
命令,不过关于它和 checkout
之间的区别,你可能还是会有点困惑,毕竟不太可能记住不同调用的所有规则。
下面的速查表列出了命令对树的影响。 “HEAD
” 一列中的 “REF
” 表示该命令移动了 HEAD 指向的分支引用,而HEAD
则表示只移动了 HEAD
自身。 特别注意 WD Safe? 一列 - 如果它标记为 NO,那么运行该命令之前请考虑一下。
HEAD | Index | Workdir | WD Safe? | |
---|---|---|---|---|
Commit Level | ||||
reset --soft [commit] |
REF | NO | NO | YES |
reset [commit] |
YES | |||
reset --hard [commit] |
YES | NO | ||
checkout [commit] |
HEAD | YES | ||
File Level | ||||
reset (commit) [file] |
NO | YES | NO | YES |
checkout (commit) [file] |
YES | NO |