4. Git 回滚实战:reset、revert、checkout 的区别与使用场景

Git 回滚,说白了就是「后悔药」。但 Git 给了你三种不同的药,吃错了反而更麻烦。

我见过不少团队,一遇到回滚就慌。有人直接删仓库重来,有人用 git reset --hard 把同事的代码也丢了。嗯,今天咱们就把这三个命令掰开揉碎讲清楚。

核心结论先放这:

  • git reset —— 本地分支回退,适合还没推上去的提交
  • git revert —— 安全撤销,适合已经推送到远程的提交
  • git checkout —— 切换/恢复文件,不是真正的回滚

4.1 git reset:本地时光机

git reset 是移动 HEAD 指针的操作。它有三种模式,我习惯用一张表来记:

模式 工作区 暂存区 版本库 使用场景
--soft 不变 不变 回退 只想改 commit 信息,或合并多个提交
--mixed(默认) 不变 清空 回退 想重新整理暂存区的文件
--hard 重置 重置 回退 彻底丢弃本地修改(⚠️ 危险)

举个例子。假设你有三次提交:A ← B ← C(HEAD 指向 C)。

# 回退到 B,保留 C 的修改在工作区
git reset --soft HEAD~1

# 回退到 B,C 的修改放到工作区(未暂存)
git reset --mixed HEAD~1

# 回退到 B,彻底丢弃 C 的所有修改
git reset --hard HEAD~1

⚠️ 我曾经犯过的错:

有一次我在本地用 git reset --hard HEAD~3 回退了三个提交,结果发现里面有一个同事让我帮忙 review 的代码。嗯,那三个小时白干了。从那以后,我每次用 --hard 之前都会先 git stash 或者打个 tag 备份。

什么时候用 reset?

  • 提交还没推送到远程
  • 想合并多个本地提交(配合 --soft)
  • 想彻底清理本地实验性的修改

4.2 git revert:安全的远程回滚

git revert 不会删除历史,而是创建一个「反提交」来抵消之前的修改。说白了,它是在历史记录上加一笔,而不是抹掉一笔。

# 撤销最近一次提交
git revert HEAD

# 撤销某次具体的提交
git revert <commit-hash>

# 撤销多个提交(按顺序逐个创建 revert commit)
git revert HEAD~3..HEAD

为什么说它安全?因为 revert 不会改变已有的 commit hash。你想想看,如果团队里其他人已经基于你的提交做了开发,你用 reset 把历史改了,大家就全乱了。revert 就不会有这个问题。

💡 我的习惯:

只要提交已经推送到远程,我就只用 revert。哪怕只是推上去 5 分钟,我也用 revert。因为你不确定有没有人已经 pull 了你的代码。这个习惯帮我避免了好几次团队冲突。

revert 的常见问题:

  • revert 一个 merge commit 时,需要指定 -m 参数选择保留哪条分支
  • 连续 revert 会产生多个 commit,历史会变长,但很清晰
  • revert 后如果又想恢复,可以 revert 那个 revert commit

4.3 git checkout:文件级别的恢复

git checkout 严格来说不是回滚,而是「切换」或「恢复」。它主要做两件事:

  1. 切换分支:git checkout feature/login
  2. 恢复文件:git checkout -- index.html

恢复文件时,它会把工作区的文件重置到暂存区或某个提交的状态。

# 恢复工作区的 index.html 到暂存区的状态
git checkout -- index.html

# 恢复整个目录到某次提交的状态
git checkout HEAD~2 -- src/

# 创建一个新分支并切换过去(Git 2.23+ 推荐用 switch)
git checkout -b hotfix/bug-123

注意:

Git 2.23 之后,官方推荐用 git switch 切换分支,用 git restore 恢复文件。checkout 的职责太重了,容易混淆。但我个人还是习惯用 checkout,毕竟肌肉记忆改不了。

4.4 三者的核心区别

我画了一张图,帮你理清这三个命令的定位:

Git 回滚三兄弟:reset vs revert vs checkout git reset 移动 HEAD,修改历史 git revert 创建反提交,保留历史 git checkout 切换/恢复文件 适用场景对比 ✅ 本地未推送 合并多个提交 修改 commit 信息 丢弃实验性修改 ✅ 已推送远程 团队协作分支 撤销 merge commit 需要保留完整历史 ✅ 文件级别恢复 切换分支 恢复单个文件 查看历史版本 一句话总结 本地用 reset,远程用 revert,文件用 checkout

4.5 实战场景选择

我整理了几个真实场景,你看看自己会怎么选:

场景 推荐命令 原因
刚 commit 发现写错信息,还没 push git reset --soft HEAD~1 保留修改,重新 commit
push 后发现引入了 bug git revert HEAD 不破坏团队历史
想丢弃本地所有未提交的修改 git checkout -- . 只影响工作区,不影响版本库
想回到三天前的状态重新开发 git reset --hard HEAD~N 前提是没 push,且确定不要了
revert 之后又想恢复 git revert <revert-commit> revert 是可逆的

💡 避坑指南:

我曾经在 CI 流水线里写了一个自动回滚脚本,用的 git reset --hard。结果有一次触发条件写错了,把主分支回退了 10 个提交。还好我们做了保护分支策略,远程拒绝了这个 push。从那以后,我所有自动化脚本里只用 git revert。

4.6 总结

这三个命令,你只要记住一个原则:本地用 reset,远程用 revert,文件用 checkout。

reset 是「时光倒流」,适合自己玩;revert 是「打补丁」,适合团队协作;checkout 是「局部修复」,适合小范围调整。

嗯,下次再遇到回滚需求,先问自己三个问题:

  • 这个提交推上去了吗?
  • 别人基于它开发了吗?
  • 我是想改历史还是加历史?

想清楚再动手,Git 回滚其实没那么可怕。