建议先阅读 Git 工作区、暂存区和版本库、Git 分支本质及与 commit、HEAD、tag 的关系。
简介
git reset
命令是用来将当前 branch 重置到另外一个 commit 的,也就是用来回退到指定 commit,根据不同的参数,这个动作可能同时影响到 Index 以及 Working Directory。
先举个例子,来一个感性的认识。下面这两条命令让 hotfix 分支向后回退两个提交。
git checkout hotfix
git reset HEAD~2
hotfix 分支末端的两个提交现在变成了孤儿提交。下次 Git 执行垃圾回收的时候,这两个提交会被删除。如果你的提交还没有共享给别人,可以用git reset
撤销这些提交。
reset 的作用
假设仓库历史是这样:
现在,假设我们运行git reset HEAD~
(后面可能会跟不同的参数)。
1、移动 HEAD(--soft)
使用--soft
参数时,reset 会移动 HEAD 的指向。这与改变 HEAD 自身不同(checkout 所做的),reset 移动 HEAD 指向的分支。这意味着如果 HEAD 设置为 master 分支(例如,你正在 master 分支上),运行git reset --soft 9e5e6a4
将会使 master 指向 9e5e6a4。
使用reset --soft
,仅仅会改变分支指向。
结合上图,我们理解一下发生的事情:它本质上是撤销了上一次 git commit 命令。当你在运行 git commit 时,Git 会创建一个新的提交,并移动 HEAD 所指向的分支来使其指向该提交。当你将它 reset 回 HEAD~(HEAD 的父结点)时,其实就是把该分支移回原来的位置,而不会改变索引和工作目录。
2、更新索引(--mixed)
git reset
默认参数为--mixed
,也就是git reset 9e5e6a4
等价于git reset --mixed 9e5e6a4
。
如果运行git reset --mixed 9e5e6a4
,除了像git reset --soft 9e5e6a4
那样改变分支指向外,还会用 HEAD 指向的当前快照的内容来更新索引。
3、更新工作目录(--hard)
如果使用 --hard 选项,reset 将会在 --mixed 的基础上,让工作目录和索引保持一致(不考虑忽略文件之类)。
现在让我们回想一下刚才发生的事情:你撤销了最后的提交(git commit)、git add 和工作目录中的所有工作。
必须注意,--hard 标记是 reset 命令唯一的危险用法,它也是 Git 会真正地销毁数据的仅有的几个操作之一。其他任何形式的 reset 调用都可以轻松撤消,但是 --hard 选项不能,因为它强制覆盖了工作目录中的文件。
总结
reset 命令会以特定的顺序重写这三棵树,在你指定以下选项时停止:
- 移动 HEAD 指向的分支(若指定了
--soft
,则到此停止); - 重置 Index 以便和 HEAD 相匹配(若未指定
--hard
,则到此停止); - 使工作目录和索引保持一致。