跳至主要内容

Git 撤销上一次提交:如何在 Git 中安全回退变更

学习如何使用 git revert 在不重写历史的情况下撤销您最新的 Git 提交。理解其影响、与 git reset 的比较,并处理真实世界中的边界情况。
· 8分钟 阅读

使用 AI 探索

在软件开发中,犯错在所难免,但如何处理错误尤其是在团队协作中至关重要。

当您在共享代码仓库中使用 Git 时,您不仅要撤销一次不良提交,更要以安全的方式进行。这也是我结合自身经验撰写这篇关于 git revert 的原因。

与 git reset 会抹去历史不同,git revert 会创建一个新的提交,用来撤销先前提交引入的更改。这种做法能保留提交历史。

如果您对这些概念还不熟悉,可以学习我们的 Git Fundamentals 技能路径,全面了解代码仓库与版本控制。

什么是 Git Revert?它如何工作?

可以把 git revert 理解为 Git 在说:“我们安全地撤销它吧。”当某次提交混入了问题——可能是一个 bug、拼写错误,或是仓促的修改——您或许会想直接删掉它。但在共享仓库中,删除历史可能带来比原始错误更大的麻烦。这就是 git revert 存在的意义。它不抹去提交,而是新增一个提交来抵消那次更改。

那么,git revert 到底做了什么?

  • 它会新增一个提交:正如我之前所说,git revert 不会清除任何东西,而是创建一个新的提交来撤销您要回退的那次提交中的具体更改,从而保持项目历史一致。

  • 它会立即应用更改:如我尚未提到的,一旦您运行 git revert 命令,被撤销的更改会立刻出现在工作区中。您能马上看到修复效果。

  • 它不会影响暂存区(除非产生冲突):还有一点需要了解:Git 会在内部处理 revert。如果一切顺利,无需手动暂存文件。不过,如果 Git 无法确定如何逆转某些更改(例如复杂合并的情况),它会请求您介入协助。

撤销提交之后会发生什么

当您运行了 git revert,到底发生了什么?Git 并不会删除任何内容,而是创建一个新提交,来逆转目标提交所做的更改。这个新提交会被添加到已有历史的顶部,这意味着原始提交保持不变且完全可见。历史不会被重写或移除;Git 只是加了一层“撤销该更改”的记录。同时,这次逆转带来的文件修改会立即应用到您的工作区。您会立刻看到修复生效,就像其他提交一样。

这一过程使 git revert 成为对已推送到共享分支的错误进行修复的最安全方式。团队中的每个人都能清楚看到哪些内容被修改、哪些被撤销,以及原因何在,不会干扰正在进行的工作。

git revert 与 git reset 对比

虽然 git revert 与 git reset 都能用来撤销提交,但它们的工作方式本质不同。git reset 适合本地、私有的修复,可用于清理凌乱的提交并按您的意愿重置工作区。但它有个风险:如果您已经推送了工作内容,然后又使用 git reset,通过更改共享的提交历史,可能会给队友带来麻烦。

相反,git revert 专为共享或公共分支设计。它不会删除任何东西,而是创建一个新提交来抵消较早提交引入的更改,因此在共享项目中更安全。

下面的并排对比可以帮助您更清楚:

特性

git revert

git reset

动作

新增一个提交以逆转先前的提交

移动分支指针,并可选择修改文件

提交历史

保留,不删除任何内容

被重写的提交可能丢失

最佳用途

在公共/共享分支上撤销更改

在推送前清理本地提交

对他人的影响

安全,不会干扰协作者

有风险,如共享历史会导致冲突

工作区

更改会立即应用

可能重置文件状态(--soft、--mixed、--hard)

理想场景

回退已推送的不良提交

在本地功能分支中重写提交历史

回退多个提交

有时问题不止一个提交,您可能需要撤销一整串提交。幸运的是,Git 允许您使用一次命令并通过提交范围来回退多个提交。

语法如下:

git revert OLDEST_HASH^..NEWEST_HASH

这会告诉 Git 从最旧提交之前(这就是 ^ 的含义)开始,依次回退直到并包含最新的那个提交。例如:

git revert a1b2c3d^..d4e5f6g

该命令会为该范围内的每一个原始提交生成单独的回退提交。

注意,一次性回退多个提交往往会触发合并冲突,尤其当这些提交修改了同一文件或同一行时。Git 会尝试按顺序逆转每个提交,但可能需要您协助解决重叠的更改。

您也可以进一步了解 Git 如何处理回退操作(包括更棘手的情况,如合并提交)请参阅 Git Revert Merge Commit: A Guide With Examples。

在回退过程中处理合并冲突

回退听起来很简单,直到 Git 给您“出难题”——合并冲突。通常在以下情况下出现:

  • 您要回退的提交与其他近期提交修改了相同的代码行。
  • 您正在回退一个合并提交,或一组存在重叠变更的提交。

Git 会尽力干净地逆转更改,但一旦存在歧义,它会暂停并请求您的帮助。

处理方法如下:

git revert HEAD

另外,要查看哪些文件存在冲突,请使用:

git status

在代码编辑器中手动解决冲突,查找 <<<<<<<、======= 和 >>>>>>> 标记。

接下来,我将其标记为已解决:

git add <resolved-file>

然后继续回退:

git revert-- continue

如果您改变主意,想要彻底取消这次回退:

git revert --abort

这种流程让您完全掌控。您可以决定是继续回退还是彻底退出。

如果您想了解 Git 如何在此类操作中跟踪引用日志,请查看 Git Reflog: Understanding and Using Reference Logs,获取处理历史混乱的实用技巧。

特殊情况:回退合并提交

回退普通提交相对简单,但合并提交通常更棘手。因为合并提交有两个或更多父提交,Git 需要知道应将哪条路径(即“主线”)视为基线。

要回退合并提交,您需要显式使用 -m 标志。

git revert -m 1 <merge-commit-hash>

以下是 -m 标志的作用:

  • -m 代表 mainline,告诉 Git 保留哪一个父提交。

  • 1 通常指第一个父提交,即执行合并时您所在的分支。

  • 2 指第二个父提交,通常是被合并进来的分支。

何时使用 1 或 2?

  • 如果您想撤销合入变更的影响,但保留主分支上的工作,请使用 -m 1。

  • 如果您想移除主线的更改,但保留功能分支(或被合并分支)的内容,请使用 -m 2。这种情况较为少见。回退合并提交容易引发冲突,并常常导致复杂局面,尤其当后续提交依赖于它时。如果拿不准,先在测试分支尝试回退。

使用 git revert 的最佳实践

想要更好地使用 git revert 并避免常见错误,请牢记以下最佳实践:

  • 在回退前核对提交哈希:始终使用 git log 或 git log-- oneline 检查提交历史,确保瞄准正确的更改。您可以在 Foundations of Git 课程中巩固这些命令,或借助我们的 Git Cheat Sheet 随时查阅要点。

  • 自定义提交信息:不要只用 Git 的默认“Revert”信息。添加上下文——例如为何要回退此提交——以便协作者(以及未来的您)理解这次变更。

  • 在共享分支回退时及时沟通:如果您在公共分支(如 main 或 develop)上回退更改,请先通知团队。回退会影响所有人,最好协调进行。进一步了解协作工作流,请参阅我们的 GitHub Concepts 和 Intermediate GitHub Concepts 课程。

  • 避免“回退的回退”,除非您非常确定:回退此前的回退,可能带回非预期更改或引发冲突。如果需要重新应用某项更改,考虑使用 git cherry-pick 替代。

  • 当出现意外时使用 reflog 进行恢复:在回退过程中犯了错?使用 git reflog 查看最近的操作并恢复丢失的状态。

结语

在专业协作环境中,使用 git revert HEAD 将 git revert 到上一次提交,既能保持透明度,又能降低意外数据丢失的风险。无论是在清理失误还是回滚缺陷,revert 都能让您的仓库对所有参与者来说保持稳定且易于理解。

Oluseye Jeremiah

作者

Oluseye Jeremiah

专注于人工智能、机器学习与数据科学的技术写作者,擅长将复杂概念讲解得清晰易懂。

主题