git revert 的工作原理
可以将 git revert 理解为 Git 版的“撤销”命令。不过,git revert 并不会删除提交,或将分支跳回到先前状态;相反,它会创建一个新的提交,用以回滚某个特定提交引入的更改。

回滚哈希为 <commit_hash> 的提交,其语法为:
git revert <commit_hash>
我们可以使用 git log 命令列出提交及其哈希标识。git log 的输出会按时间从新到旧列出提交,如下所示:

例如,要回滚实现 subtract 函数的提交,我们将使用以下命令:
git revert 7ba24a3e62d4d37182428ccfaa070baa222b1151
使用 git revert 可以在不影响提交历史的情况下撤销特定提交的更改。
请注意,git revert 并非“魔法”。根据提交历史的不同,它可能会产生需要手动解决的冲突。
git revert 相比手动改动的优势
既然有时仍需手动解决冲突,git revert 有何用处?直接手动撤销不更简单吗?优势如下:
- 保留历史:
git revert会创建一个新提交来撤销指定提交的更改,同时保留完整的提交历史。这有助于维护清晰透明的变更与回滚记录。 - 原子回滚:确保回滚操作原子且一致。手动删除并提交更改容易出现人为失误。
- 冲突感知:若存在对原提交有依赖的集成或改动,它会通过冲突提示我们。这也许不便,却能防止意外的连带影响。
- 元数据:git revert 创建的新提交包含元数据和提交信息,能在语境中说明回滚内容,便于后续理解。若不使用
git revert,这些上下文可能丢失。
在不同场景下撤销合并
本节我们学习如何撤销一次合并。为便于说明,假设我们将名为 feature 的分支合并到 main 分支,并在 main 分支上执行命令:
git merge feature
此处的方法适用于任意两条分支,只需相应替换名称即可。
撤销没有产生提交的合并
git merge 并不总会创建新提交。只有当 main 分支相对 feature 分支发生分叉时才会创建提交。由于 git revert 需要基于某个提交来执行,在这种情况下无法使用它。
当 main 上存在一些不是 feature 祖先的新增提交时,main 与 feature 就发生了分叉。换言之,feature 创建之后,main 上又产生了新提交。
如果分支没有分叉,那么在主分支运行 git merge feature 时,Git 会使用快进(fast-forward)方式合并。这意味着它会把 main 分支的 HEAD 移动到 feature 分支的 HEAD。

我们可以从 git merge 的输出观察到这一点:

要撤销这类合并,我们只需把 main 分支的 HEAD 移回到原来的位置。步骤如下:
- 使用
git reflog找到先前的HEAD - 使用
git reset --hard <previous_head>将HEAD重置到之前的位置,其中<previous_head>替换为先前的HEAD。
git reflog 的输出类似如下:

我们可以通过包含 “checkout: moving from feature to main” 的那一行识别之前的 HEAD(这里显示 feature 和 main 是因为它们是我们的分支名)。
在本例中,先前的 head 是 fe59838。要将主分支的 HEAD 移回去并撤销合并,使用命令:
git reset --hard fe59838

撤销产生了提交的合并
如果 main 分支与 feature 分支已经发生分叉,那么在合并两条分支时会生成一个新的提交,即合并提交(merge commit)。

合并提交会将一条分支的更改应用到另一条分支上。在这里,feature 中的更改被应用到了 main 分支。
要回滚 main 分支上的这些更改,我们对该合并提交执行 git revert。这会创建一个新提交,撤销通过合并带入 main 的修改,从而将主分支恢复到合并前的状态。
首先需要确定合并提交的哈希,可以用 git log 命令完成:

由于合并提交有两个父提交,git revert 的语法会稍有不同。我们需要使用 -m 1 选项,指定希望相对于 main 分支来回滚更改:
git revert -m 1 b8dab2c8611e324ed0d273133987415350e6d10d

回滚提交时的冲突解决
当回滚的提交与之后的代码变更发生冲突时,可能会出现冲突。在这种情况下:
- Git 会暂停回滚:我们需要手动解决冲突。Git 会标记冲突文件并要求人工处理。
- 解决冲突:打开每个冲突文件,处理 Git 标记的冲突并保存。
- 暂存已解决的文件:
git add <file-path> - 继续回滚:
git revert --continue
结语
使用 git revert 撤销合并提交,能确保每一次变更与修正都记录在提交历史中。
此外,了解在何种场景下使用 git reset 与 git revert,有助于我们在协作流程或仅本地变更时做出更好的决策。
您可以在下方常见问题部分了解更多。如果您想进一步学习 Git,推荐以下资源:
常见问题
我能用 git revert 撤销已推送的合并吗?
使用 git revert 后,会删除合并提交吗?
不能。合并提交会保留在历史中,但其更改会被新的回滚提交所撤销。使用 git revert 撤销合并的核心目的就是保留提交历史。
除了 git revert,还有其他方式撤销合并吗?
我应该何时使用 git reset 来撤销合并?
我建议仅在合并只发生在本地、尚未推送时,使用 git reset 来撤销合并。

无论是误将未完成的工作合入主分支,还是让关键缺陷逃过了自动化测试并被合并,合入有问题的代码都可能危及应用。
本文将带您使用
git revert安全撤销一次合并,确保提交历史保持完整,同时保护项目的完整性。