前置知识: Git

git-revert与reset对比

00:00
4 min Intermediate 2026/6/14

git revert与git reset的深度对比:原理、适用场景与安全实践。

1. 核心原理

1.1 git revert — 反做提交

git revert 创建一个新提交,其变更内容是指定提交的逆向操作。历史记录中既有原始提交,也有撤销提交。

原始历史:  A──B──C──D
revert C:  A──B──C──D──C'(C' 是 C 的逆向变更)
# 撤销最近一个提交
git revert HEAD

# 撤销指定提交
git revert abc1234

# 撤销多个提交(按顺序创建多个 revert 提交)
git revert abc1234 def5678

# 撤销多个提交(合并为一个 revert 提交)
git revert --no-commit HEAD~3..HEAD
git commit -m "revert: undo last 3 commits"

1.2 git reset — 移动指针

git reset 将 HEAD 指针移动到指定提交,根据模式决定如何处理工作区和暂存区。

原始历史:  A──B──C──D(HEAD → D)
reset B:   A──B(HEAD → B,C 和 D 从分支历史中消失)
# --soft: 仅移动 HEAD,保留暂存区和工作区
git reset --soft HEAD~1

# --mixed(默认): 移动 HEAD,重置暂存区,保留工作区
git reset --mixed HEAD~1
# 等同于
git reset HEAD~1

# --hard: 移动 HEAD,重置暂存区和工作区
git reset --hard HEAD~1

2. 三种 reset 模式详解

2.1 模式对比

模式HEAD暂存区(Index)工作区(Working Tree)
--soft移动不变不变
--mixed移动重置不变
--hard移动重置重置

2.2 图示理解

假设当前状态:

HEAD → D
暂存区: D 的快照
工作区: D 的文件

执行 git reset --soft B

HEAD → B
暂存区: D 的快照(D 的变更已暂存,可直接重新提交)
工作区: D 的文件

执行 git reset --mixed B

HEAD → B
暂存区: B 的快照(D 的变更变为未暂存状态)
工作区: D 的文件

执行 git reset --hard B

HEAD → B
暂存区: B 的快照
工作区: B 的文件(D 的变更完全丢失!)

2.3 典型应用

--soft:重新组织提交

# 撤销最近3个提交,但保留所有变更在暂存区
git reset --soft HEAD~3
git commit -m "feat: complete user module"
# 将3个零碎提交合并为一个

--mixed:重新选择暂存

# 撤销提交,变更回到工作区
git reset HEAD~1
# 重新选择要暂存的文件
git add src/important-file.js
git commit -m "feat: add important feature"

--hard:彻底丢弃

# 丢弃所有未提交的变更
git reset --hard HEAD

# 回到指定提交的状态(危险!)
git reset --hard abc1234

3. revert 与 reset 对比

3.1 核心差异

维度git revertgit reset
历史改写不改写,新增撤销提交改写,丢弃提交
安全性安全,可推送共享分支危险,不应推送已共享的分支
提交哈希产生新哈希之前的哈希消失
冲突可能性可能冲突(如果后续提交修改了相同不冲突(直接移动指针
协作友好友好,他无需特殊操作不友好,他需要重新同步
精确可撤销任意提交只能从 HEAD 往回数
可逆性可再次 revert 恢复需 reflog 找回

3.2 适用场景

使用 revert

  • 推送远程共享分支提交
  • 需要保留审计追踪
  • CI/CD 环境中需要记录回滚操作
  • 项目中维护透明的变更历史

使用 reset

  • 仅在本地推送提交
  • 需要重新组织提交(squash、reorder)
  • 提交后立即撤销
  • 清理本地实验性提交

3.3 revert 的特殊情况

revert 一个 merge 提交

# merge 提交有两个父提交,需要指定撤销哪个方向
git revert -m 1 abc1234
# -m 1 表示保留第1个父提交(通常是主分支)的方向
# -m 2 表示保留第2个父提交(通常是特性分支)的方向

revert 一个 revert

# 先 revert
git revert abc1234  # 创建 C'

# 后来需要恢复,revert 那个 revert
git revert C'       # 创建 C''
# C'' 的效果等同于重新应用 abc1234 的变更

4. reflog — 丢失提交的救星

4.1 reflog 记录

git reset --hard 后,提交看似丢失,但 reflog 仍保留记录:

git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~2
# d4e5f6g HEAD@{1}: commit: feat: add feature D
# g7h8i9j HEAD@{2}: commit: feat: add feature C

4.2 恢复丢失的提交

# 通过 reflog 找到丢失提交的哈希
git reflog

# 恢复到该提交
git reset --hard d4e5f6g
# 或者创建新分支指向它
git branch recovered d4e5f6g

4.3 reflog 过期

reflog 条目默认保留 90 天,过期后提交可能被 GC 回收

# 查看过期设置
git config --get gc.reflogExpire
# 默认: 90 days

# 手动触发 GC(会清除不可达对象)
git gc --prune=now

5. 实践建议

5.1 安全操作清单

  1. 推送提交:只用 git revert
  2. 推送提交:可用 git reset
  3. reset 前先备份git branch backup
  4. 使用 --force-with-lease替代 --force 推送
  5. 定期检查 reflog确认重要提交可找回

5.2 常见错误与修复

# 错误:reset --hard 后想恢复
git reflog                    # 找到之前的 HEAD
git reset --hard HEAD@{1}     # 恢复

# 错误:revert 了错误的提交
git revert HEAD               # revert 那个 revert

# 错误:reset 后发现还需要那些提交
git reflog
git cherry-pick abc1234       # 逐个捡回需要的提交

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式