Git:重置揭秘

本文详细讲述了Git中重置相关的git-reset、git-checkout、git-revert的命令。

前提知识

树名称 说明
Working Directory 工作区
Index 或 Stage 暂存区,预期的下一次要被提交的快照
HEAD HEAD 指向目前所在的本地分支,它是一个符号引用,所谓符号引用,表示它是一个指向其他引用的指针。并且它总是指向当前分支的最后一次提交。

git reset

示例数据准备:master分支分别进行3次修改、提交,形成如下提交拓扑图:

reset+commit

它的基本语义是:将HEAD指向的当前分支重置到指定的“提交”状态,其中根据指定的参数对暂存区和工作区进行相应的修改。

1步:移动HEAD

使用git reset --soft <commit>命令将HEAD指向的当前分支移动到指定的“提交”位置。

示例:

1
git reset --soft HEAD~

2步:移动HEAD+更新暂存区

使用git reset [--mixed] <commit>命令将HEAD指向的当前分支移动到指定的“提交”位置,并同步重置暂存区。

示例:

1
git reset HEAD~

3步:移动HEAD+更新暂存区+更新工作区

使用git reset --hard <commit>命令将HEAD指向的当前分支移动到指定的“提交”位置,并同步重置暂存区和工作区。

示例:

1
git reset --hard HEAD~

reset+path

它的基本语义是:将暂存区中的文件恢复到指定“提交”时的状态(注意,不会移动HEAD指针)。git reset <commit> [--] path命令中不能有--soft--hard参数。

示例:

1
git reset HEAD~ file.txt

如果使用git reset HEAD file.txt,它的效果就有“取消暂存文件”的效果,相当于撤销git add所做的事情。

git checkout

示例数据准备:master分支分别进行3次修改、提交;dev分支进行一次修改、提交,形成如下提交拓扑图:

checkout+branch

它的基本语义是:检出指定分支,使得HEAD指向被检出的分支,暂存区和工作区不是像reset操作那样进行直接覆盖更新,而是在检出时会进行智能化地合并切换前后对应区域中的内容。

命令形式为:git checkout <branch>

示例:

1
git checkout master

我们注意到,上例途中,checkout操作是将HEAD的指向进行变更,而reset是对HEAD指向的分支进行移动。下图来自Git官方文档给出的一个非常形象的关于checkout和reset的对比:

checkout+path

它的基本语义是:将特定“分支”的最后“提交”中对应的文件更新到暂存区,再将暂存区相应文件直接覆盖到工作区中的文件(注意,是直接覆盖工作区,不会做智能地合并)。

命令形式为:git checkout [tree-ish] -- path

示例:

1
git checkout HEAD~ -- file.txt

特例:git checkout -- path 实际对应的git checkout HEAD -- path

git revert

revert可以理解为撤销的意思,该命令的基本语义是:对当前分支,撤销某次提交所对应的变更,并使用一次新的提交来合并因撤销提交所造成的影响。理解该命令需要注意几点:

  1. 在执行该命令时,要求工作区是clean状态;
  2. 它不是真正撤销某次提交,而是撤销某次提交所对应的变更,直观上,在该命令执行完成后,被撤销的提交仍然在提交的拓扑图中;

示例数据准备:master分支分别进行3次新建文件、提交;dev分支进行一次修改、提交,形成如下提交拓扑图:

撤销后直接提交

使用git revert [--edit] <commit>git revert [-e] <commit>命令将指定的提交影响进行撤销,并且--edit-e选项允许在提交合并的结果之前来编辑提交信息(注意,如果在revert某次提交期间,导致在Git处理合并有冲突时,需要我们手动解决完冲突,然后再进行手动commit操作)。

示例:

1
git revert da814a2

当然,如果我们不想自己来编辑revert命令衍生新提交的提交信息,可以使用--no-edit选项,该选项会自动为新提交创建提交信息,格式为:Resert + 被撤销的信息。

撤销后不直接提交

使用git revert -n <commit>git revert --no-commit <commit>命令只将撤销合并后的结果放到工作区和暂存区,不会提交到仓库中。

示例:

1
git revert -n da814a2

参考

  1. https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E7%BD%AE%E6%8F%AD%E5%AF%86
  2. https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified
  3. https://git-scm.com/docs/git-reset
  4. https://git-scm.com/docs/git-checkout
  5. https://git-scm.com/docs/git-revert