git checkout 和 git reset 有时候会让新手感到困惑,因为它们的表现很相似,本文就简单分析一下两者的区别。
git checkout 和 git reset 主要有两种使用方式
1、命令后面单独接分支名称(不带文件名和目录)的情况
不接文件名的时候,执行 git checkout [branch]
与执行 git reset --hard [branch]
非常相似,都会更新所有的三棵树,不过有两点重要的区别。
- 首先不同于
reset --hard
,checkout
对工作目录是安全的,它会通过检查来确保不会将已更改的文件弄丢;而reset --hard
则会不做检查就全面地替换所有文件。 - 第二个重要的区别是如何更新
HEAD
。reset
会移动HEAD
分支的指向(即HEAD
指向分支的指向),而checkout
只会移动HEAD
自身来指向另一个分支。
例如,我们有 master
和 develop
分支,它们分别指向不同的提交;我们现在在 develop 上(所以 HEAD 指向它)。 如果我们运行 git reset master
,那么 develop
会和 master
指向同一个提交; 而如果我们运行 git checkout master
的话,develop
不会移动,HEAD 自身会移动,指向 master。如下图所示:
所以,虽然在这两种情况下我们都移动 HEAD
使其指向了提交 A
,但是方式是不同的。 reset
会移动 HEAD
分支的指向,而 checkout
则移动 HEAD
自身。
2、命令后面接文件名的情况
命令后面接文件或者目录名称的时候,执行 checkout
的效果和 reset
一样,不会移动 HEAD
。 git checkout [branch] file
等同 git reset --hard [branch] file
,不仅会用某次提交中的那个文件来更新索引,同时也会覆盖工作目录中对应的文件(这样对工作目录并不安全)!
注:这里的 [branch]
可以是分支名称,也可以是 commit
或者是 HEAD
一类的符号引用。本质上,它们最终都定位到某个提交。