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可以理解为撤销的意思,该命令的基本语义是:对当前分支,撤销某次提交所对应的变更,并使用一次新的提交来合并因撤销提交所造成的影响。理解该命令需要注意几点:
- 在执行该命令时,要求工作区是
clean状态; - 它不是真正撤销某次提交,而是撤销某次提交所对应的变更,直观上,在该命令执行完成后,被撤销的提交仍然在提交的拓扑图中;
示例数据准备: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 |