Skip to content

git revert 详解

git revert 是撤销已提交内容的安全方式。与 git reset 不同,revert 不修改历史,而是通过创建新的提交来撤销指定提交的改动,因此适用于已推送到共享仓库的提交。

revert 的核心原理

git revert 分析指定提交引入的改动,然后创建一个"反向"提交(anti-commit)来撤销这些改动:

revert 前:
A ← B ← C  (HEAD)

git revert B 后:
A ← B ← C ← B'  (HEAD)
(B' 撤销了 B 的改动,历史保留完整)

这与 reset 的区别:

reset(删除提交):
A ← B ← C  变为  A ← C(或 A ← B)

revert(撤销改动但保留历史):
A ← B ← C  变为  A ← B ← C ← B'(B' 撤销了 B 的改动)

revert 普通提交

bash
# revert 最近一次提交
git revert HEAD

# revert 指定提交
git revert abc123

# revert 会打开编辑器填写提交信息(默认是 "Revert ...")
# 可以直接使用默认信息,也可以修改

默认提交信息格式:

Revert "feat: 添加用户登录功能"

This reverts commit abc123456789abcdef...

revert 合并提交(-m 1 / -m 2)

合并提交有两个父提交,revert 时需要指定保留哪一侧(即"主线"是哪一侧):

合并前:
A ← B ← C (main)  ← 第一父提交(-m 1)
          \
           D ← E (feature)  ← 第二父提交(-m 2)

合并后(合并提交 M):
A ← B ← C ← M  (main)
          \   /
           D ← E
bash
# revert 合并提交 M,保留 main 的代码(撤销 feature 引入的改动)
git revert -m 1 <merge-commit-sha>

# revert 合并提交 M,保留 feature 的代码(撤销 main 原有的改动)
git revert -m 2 <merge-commit-sha>

# 通常情况下用 -m 1(保留第一父提交,即主线)

什么时候用 revert 合并提交:

  • 发现某次合并引入了严重 bug,需要紧急回退
  • 已经 push 到共享分支,无法使用 reset

revert 多个提交

bash
# revert 多个独立提交(创建多个 revert 提交)
git revert abc123 def456 ghi789

# revert 一个范围内的提交(注意顺序:从新到旧)
git revert HEAD~3..HEAD
# 等价于:
git revert HEAD
git revert HEAD~1
git revert HEAD~2

# revert 指定范围(不包含 abc123,包含 def456)
git revert abc123..def456

--no-commit 模式

--no-commit(或 -n)执行 revert 但不自动创建提交,让你先检查或合并多个 revert 再一次性提交:

bash
# revert 多个提交,但不立即提交
git revert --no-commit HEAD~3..HEAD

# 检查 revert 效果
git diff --cached
git status

# 满意后手动提交
git commit -m "revert: 撤销最近 3 次提交"

# 如果不满意,取消所有 revert
git revert --abort

revert 与 reset 的选择

对比项git revertgit reset
历史处理保留历史,新增撤销提交删除/修改历史
适用范围已推送的公共提交未推送的本地提交
安全性高,不影响他人低,影响他人的仓库
可再次撤销可以 revert revert一旦 reset 不易恢复
历史清晰度有额外的 revert 提交历史简洁

决策规则:

  • 已 push 到共享分支 → 用 git revert
  • 只在本地,未 push → 可以用 git reset
  • push 到个人功能分支 → 用 git reset 后 force push

实际场景

场景1:线上发现 bug,需要紧急回滚

bash
# 找到引入 bug 的提交
git log --oneline
# abc123 feat: 添加新的结算逻辑  ← 这个引入了 bug
# def456 feat: 更新商品列表
# ...

# revert 这个提交(生成一个新提交撤销其改动)
git revert abc123

# 推送到远程(安全,不需要 force push)
git push

场景2:撤销一次错误的合并

bash
# 找到合并提交
git log --oneline --merges
# ghi789 Merge branch 'feature/wrong-feature'

# revert 合并提交(-m 1 保留主线)
git revert -m 1 ghi789

# 推送
git push

场景3:撤销多个连续提交

bash
# 将最近 3 个提交都撤销,但合成一个 revert 提交
git revert --no-commit HEAD~3..HEAD
git commit -m "revert: 回退上周的功能迭代(功能 A/B/C 存在严重问题)"

revert 后再次合并的问题

这是一个重要的陷阱:

场景:
1. feature 分支合并到 main(M1)
2. revert M1(R1),main 恢复到合并前状态
3. feature 继续开发,再次合并到 main(M2)

问题:M2 合并时,Git 认为 feature 的旧提交(在 M1 之前的)
已经合并过了,不会再次引入。只有 M1 之后的新提交会被合并。

解决方案:

bash
# 方案1:revert 掉 revert(即恢复 feature 的改动)
git revert R1
# 这会重新引入 feature 的原始改动

# 方案2:在 feature 分支上重建提交
git switch feature
git rebase main    # 变基到当前 main
# 现在的提交是新的,不会有"已合并"的问题

总结

操作命令
revert 最近提交git revert HEAD
revert 指定提交git revert <sha>
revert 合并提交git revert -m 1 <merge-sha>
revert 多个提交git revert <old>..<new>
revert 不自动提交git revert --no-commit <sha>
放弃 revert 操作git revert --abort

git revert 是在共享代码库中撤销改动的正确方式,它保留了完整的历史记录,让所有协作者都能清晰了解发生了什么。