跳至主要内容

Git Revert 合并提交:示例指南

了解如何使用 `git revert` 安全撤销 Git 合并,在保留提交历史的同时解决潜在冲突。
· 7分钟 阅读

使用 AI 探索

无论是误将未完成的工作合入主分支,还是让关键缺陷逃过了自动化测试并被合并,合入有问题的代码都可能危及应用。

本文将带您使用 git revert 安全撤销一次合并,确保提交历史保持完整,同时保护项目的完整性。

git revert 的工作原理

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

git revert 的工作方式示意图

回滚哈希为 <commit_hash> 的提交,其语法为:

git revert <commit_hash>

我们可以使用 git log 命令列出提交及其哈希标识。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 需要基于某个提交来执行,在这种情况下无法使用它。Git 分支发生分叉的说明

当 main 上存在一些不是 feature 祖先的新增提交时,main 与 feature 就发生了分叉。换言之,feature 创建之后,main 上又产生了新提交。

如果分支没有分叉,那么在主分支运行 git merge feature 时,Git 会使用快进(fast-forward)方式合并。这意味着它会把 main 分支的 HEAD 移动到 feature 分支的 HEAD。

当我们移动 <code data-mce-selected=

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

如何识别合并使用了快进(Fast-forward)

要撤销这类合并,我们只需把 main 分支的 HEAD 移回到原来的位置。步骤如下:

  1. 使用 git reflog 找到先前的 HEAD
  2. 使用 git reset --hard <previous_head> 将 HEAD 重置到之前的位置,其中 <previous_head> 替换为先前的 HEAD。

git reflog 的输出类似如下:

查找旧的 HEAD 引用

我们可以通过包含 “checkout: moving from feature to main” 的那一行识别之前的 HEAD(这里显示 feature 和 main 是因为它们是我们的分支名)。

在本例中,先前的 head 是 fe59838。要将主分支的 HEAD 移回去并撤销合并,使用命令:

git reset --hard fe59838

将 HEAD 移回后会发生什么的示意图

撤销产生了提交的合并

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

合并提交示意图

合并提交会将一条分支的更改应用到另一条分支上。在这里,feature 中的更改被应用到了 main 分支。

要回滚 main 分支上的这些更改,我们对该合并提交执行 git revert。这会创建一个新提交,撤销通过合并带入 main 的修改,从而将主分支恢复到合并前的状态。

首先需要确定合并提交的哈希,可以用 git log 命令完成:

识别合并提交的哈希

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

git revert -m 1 b8dab2c8611e324ed0d273133987415350e6d10d

回滚 git 合并提交时会发生什么的示意图

回滚提交时的冲突解决

当回滚的提交与之后的代码变更发生冲突时,可能会出现冲突。在这种情况下:

  1. Git 会暂停回滚:我们需要手动解决冲突。Git 会标记冲突文件并要求人工处理。
  2. 解决冲突:打开每个冲突文件,处理 Git 标记的冲突并保存。
  3. 暂存已解决的文件:git add <file-path>
  4. 继续回滚: git revert --continue

结语

使用 git revert 撤销合并提交,能确保每一次变更与修正都记录在提交历史中。

此外,了解在何种场景下使用 git reset 与 git revert,有助于我们在协作流程或仅本地变更时做出更好的决策。

您可以在下方常见问题部分了解更多。如果您想进一步学习 Git,推荐以下资源:

常见问题

我能用 git revert 撤销已推送的合并吗?

可以,这正是对合并使用 git revert 的主要场景。如果更改仅在本地,使用 git reset 撤销合并会更简单。

若希望他人看到变更,需要将其推送到远程仓库。因此,要撤销已推送的合并,应执行:

git revert -m 1 <merge_commit_hash>
git push origin <branch_name>

将 <merge_commit_hash> 替换为该合并提交的哈希,将 <brach_name> 替换为执行合并的分支名称。

使用 git revert 后,会删除合并提交吗?

不能。合并提交会保留在历史中,但其更改会被新的回滚提交所撤销。使用 git revert 撤销合并的核心目的就是保留提交历史。

除了 git revert,还有其他方式撤销合并吗?

撤销合并的另一种方式是使用 git reset。该方法会将分支重置到合并提交之前的状态。此做法更具侵入性,且若其他协作者已基于将被重置的提交开展工作,则可能引发问题。因此,仅在确认不会影响他人时才建议使用。

git reset --hard <commit_hash>
git push --force

将 <commit_hash> 替换为您希望回退到的提交哈希。

我应该何时使用 git reset 来撤销合并?

我建议仅在合并只发生在本地、尚未推送时,使用 git reset 来撤销合并。

François Aubry

作者

François Aubry

全栈工程师,CheapGPT 创始人。我一直热爱教学。从学生时代起,我就积极寻找机会为同学提供辅导与帮助。这份热情促使我攻读博士学位,并在此期间担任助教,以支持我的学术追求。那些年里,我在传统课堂环境中收获了极大的成就感,既建立联系也促进学习。随着在线学习平台的兴起,我也看到了数字教育的变革潜力。事实上,我曾参与我们大学一款此类平台的建设。我致力于将传统教学理念与创新的数字方法相融合。我的热情在于打造既有吸引力、信息量充足,又能让当代学习者轻松获取的课程。

主题