Github 回滚代码
项目更新到最新版后异常报错,需要回到上一个版本 —— 这是最典型的回滚场景。原笔记只记了 reset --hard + push -f 这一条路,这次把几种方案补全,并说明什么时候不能用它。
一、先搞清三个区域
Git 的操作都绕不开这三个地方,理解它们,reset 就不再靠死记:
1 | 工作区(你编辑的文件) --git add--> 暂存区(Index) --git commit--> 版本库(HEAD) |
二、找到要回滚到哪一次提交
1 | git log # 查看提交历史(从新到旧) |
输出长这样:
1 | $ git log --oneline |
4ad24e8 就是 commit hash(版本号),回滚时用它指定目标位置。
git reflog 是后悔药。它记录了 HEAD 的每一次移动,包括你 reset --hard 掉的提交。只要操作过(commit 过),几乎都能用 reflog 找回来 —— 见第七节。
三、reset 的三种模式(关键区别)
git reset 把 HEAD 移到指定提交,但工作区和暂存区怎么处理,由参数决定:
| 参数 | 版本库 HEAD | 暂存区 | 工作区(你的文件) | 危险性 |
|---|---|---|---|---|
--soft |
回退 | 保留 | 保留 | 低:改动都还在,只是撤了 commit |
--mixed(默认) |
回退 | 重置 | 保留 | 中:撤了 commit 和 add |
--hard |
回退 | 重置 | 删除 ⚠️ | 高:改动直接丢失 |
1 | git reset --soft 8f3c1a2 # 只是撤销提交,改动保留在暂存区,可重新 commit |
--hard 会永久丢弃目标提交之后的所有改动(未 commit 的内容也会一起没)。执行前先确认没有需要保留的代码 —— 不确定就先 git stash 或新建分支备份。
四、场景一:回滚本地提交(还没 push)
最安全的情况,因为只影响你自己的仓库。
1 | # 1. 查看历史,找到目标版本 |
此时本地已经回到上一个版本。
如果 git reset --hard 之后发现回退错了,git log 里已经看不到那些提交了 —— 别慌,用 git reflog 找到原来的 hash,再 git reset --hard <那个hash> 就能回来。
五、场景二:回滚已推送到远程(GitHub)
git reset 只改本地,GitHub 上的代码不会跟着变。想让远程也回退,必须强制推送 —— 但这一步是团队协作中最危险的操作之一。
方案 A:reset + 强制推送(原笔记的做法)
1 | git reset --hard 8f3c1a2 # 本地回退 |
仅在满足全部以下条件时使用:
- ✅ 这是你个人的仓库 / 分支
- ✅ 确定没有别人基于这些提交继续开发
- ✅ 已通知可能受影响的人
-f(--force)会用你的本地历史直接覆盖远程,别人的提交会被抹掉。多人协作的主分支上这么做,同事下次 pull 会丢失工作。
更安全的写法是 git push --force-with-lease:推送前先检查远程是否有你不知道的新提交,有则拒绝推送,避免误覆盖。
方案 B:revert(推荐,不改写历史)
revert 的思路不是「删掉历史」,而是新建一次提交,把之前的改动反向抵消。历史完整保留,所有人正常 pull 即可同步。
1 | # 撤销某一次提交(会打开编辑器填写提交信息) |
revert 是团队协作下的标准做法。它不改写历史,因此可以安全地在 main / master 上使用,也不需要强制推送。
两个方案怎么选
| 对比项 | reset + push -f |
revert + push |
|---|---|---|
| 是否改写历史 | ✅ 删掉提交记录 | ❌ 新增一次反向提交 |
| 需要强制推送 | ✅ 需要 | ❌ 不需要 |
| 影响他人 | 可能丢失他人提交 | 无影响 |
| 能否撤销回滚 | 需靠 reflog | 再 revert 一次即可 |
| 适用场景 | 个人仓库、刚 push 的错误提交 | 团队协作、公共分支 |
六、撤销 merge 提交
revert 一个 merge 提交时需要指定保留哪条主线:
1 | git revert -m 1 <merge-commit-hash> |
-m 1 表示保留第一个父提交(通常是合入时所在的分支,如 main)。用错会回滚到错误的分支状态,执行前用 git log --graph --oneline 看清分叉结构。
七、误操作恢复:reflog 救命
1 | $ git reflog |
1 | git reset --hard 4ad24e8 # 回到误操作前的状态 |
reflog 默认保留 90 天(未被引用的提交 30 天后可能被 GC 清理)。所以要恢复就尽快,别拖。
八、其它常见撤销场景
1 | # 只撤销某一个文件到指定版本(不影响其它文件) |
九、删除中间某次提交(交互式 rebase)
想「去掉」历史上某一次提交(而不是反向抵消),用交互式 rebase:
1 | git rebase -i 8f3c1a2 # 对 8f3c1a2 之后的所有提交做整理 |
编辑器里把目标行前的 pick 改成:
| 指令 | 作用 |
|---|---|
pick |
保留这次提交 |
drop(或 d) |
删除这次提交 |
squash(或 s) |
合并到上一次提交 |
reword(或 r) |
只改提交信息 |
edit(或 e) |
停下来修改这次提交 |
rebase 同样改写历史,已推送的分支用完后必须 push -f,风险和第五节方案 A 一样。公共分支请改用 revert。
十、GitHub 网页端回滚
不想敲命令的话,GitHub 也提供了图形化入口:
- 进入仓库 → 点击 Commits 查看提交历史
- 点开要撤销的那次提交,右上角点 Revert
- GitHub 会自动创建一个 revert 的 PR,确认合并即可
这本质就是 git revert,安全且可追溯,适合不熟悉命令行的场景。
十一、安全准则
- 回滚前先备份:
git branch backup-20240308建个分支,随时能回来 - 公共分支只用 revert,不用 reset + push -f
- 强制推送一律用
--force-with-lease代替-f - 用
--hard前先git stash或确认工作区干净 - 重要分支在 GitHub 上开启 branch protection,禁止强制推送
一句话记忆:自己的分支、刚提交的错 → reset --hard;别人也在用的分支 → revert。拿不准就用 revert,它永远安全。



