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 严格来说不是回滚,而是「切换」或「恢复」。它主要做两件事:
- 切换分支:
git checkout feature/login - 恢复文件:
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 三者的核心区别
我画了一张图,帮你理清这三个命令的定位:
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 回滚其实没那么可怕。