AI 改代码后怎么安全回滚:3 个原因 + 修复路径

三条命令覆盖 99% 场景:stash / restore / reflog。

让 Claude Code 跑一晚上自主任务,早上起来发现它把 12 个文件改得面目全非,新功能没做完,旧功能挂了。最危险的时刻:你想”全部撤销”——这时候打错一条 git checkout .git reset --hard 就把可能有用的部分一起扔了。

本文按”撤销范围从小到大”列出 6 种回滚命令的使用场景,配上每种命令的具体语法和”撤销了什么、保留了什么”对照表,最后教你怎么用 git reflog 在 90 天内救回任何”以为永远丢了”的改动。

常见原因

按需要回滚的紧急程度排序。

1. AI 做得太多,需要部分撤销

最常见。Agent 自主跑了 20 分钟改了 8 个文件,3 个是你想要的,5 个是它自己脑补的。你想保留 3 个、扔掉 5 个,但不知道怎么按文件 / 按 hunk 精确撤销。

如何判断git diff --stat 显示一大堆文件,逐文件 review 后能明确分出”想留 / 想扔”两堆。

2. 本地测试过,CI 挂了

本地 npm test 全绿,push 上去 CI 红。怀疑 AI 的某次改动引入了环境相关 bug(路径大小写、CRLF、依赖版本)。需要回滚到上一个 CI 绿的 commit。

如何判断:本地 git log --oneline 有几个 AI commit;最近一次绿色 CI 的 commit 你能找到(CI 仪表盘 / GitHub Actions 历史)。

3. 想要 AI 的”计划”但不要它的”代码”

Agent 输出了一个很合理的执行计划(重命名 X、抽出 Y、补测试 Z),但实际写出来的代码质量差。你想保留 commit message 和计划,扔掉所有代码改动重新让另一个模型写。

如何判断:commit message / agent transcript 比 code diff 价值高。

4. AI 改动已经 push 但还没 merge

PR 已经 push 到远程,但 reviewer 还没 approve。你想强制 reset 自己的分支,但又怕 force push 把别人的并行 commit 弄丢。

如何判断git log @{u}..HEAD 列出还没在远程的 commit;git log HEAD..@{u} 列出远程领先你的 commit。

5. AI 改动已经 merge 到 main

最棘手。改动已经在主分支,可能有人基于它 pull 了。不能 reset,只能 git revert 生成反向 commit。

如何判断git log main --grep="<AI commit>" 显示该 commit 在 main 上。

6. AI 用 git commit --amendrebase 改了历史

Agent 自己执行了 git commit --amendgit rebase -i,把你想保留的 commit 改没了。这时候只有 reflog 能救

如何判断git log 看不到你记得的 commit,但你确定昨天还在。

最短修复路径

Step 1:先 snapshot 当前状态——任何回滚之前

# 创建安全分支,即使所有操作都搞砸也能回到这里
git branch backup/before-rollback-$(date +%Y%m%d-%H%M)

# 把 working tree 的脏改动也存一份
git stash push -u -m "before rollback $(date)"

git stash-u 包含 untracked 文件;-m 加 message 方便之后从 git stash list 找回来。

Step 2:按”撤销范围”选对命令

下表覆盖 99% 场景。先确认当前状态,再选命令

想撤销什么命令撤销了什么保留了什么
单个文件的未 stage 改动git restore <file>该文件回到 HEAD 状态其他文件、staged 区
全部未 stage 改动git restore .所有 unstaged 改动staged 区、untracked
已 stage 但未 commitgit restore --staged <file>取消 stage(保留改动)文件内容
最近一次 commit(保留改动)git reset --soft HEAD~1commit 记录所有改动在 staged 区
最近一次 commit(改动放 working tree)git reset HEAD~1commit + stage改动在 unstaged
最近一次 commit(彻底丢)git reset --hard HEAD~1commit + 所有改动
部分 hunkgit restore -p <file>你选中的 hunk其他 hunk
已 push 已 merge 的 commitgit revert <sha>生成反向 commit历史完整

关键区别

  • --soft = 改动到 staged;--mixed(默认)= 改动到 unstaged;--hard = 改动消失(但能从 reflog 救回)

Step 3:用 reflog 救回”消失的”改动

reset --hard--amend 之后的”消失”其实没消失——reflog 保留 90 天。

git reflog              # 列出所有 HEAD 移动历史
# c9d3e5a HEAD@{0}: reset: moving to HEAD~1
# 8b4f2a1 HEAD@{1}: commit: AI: refactor user service
# 2e1c8d3 HEAD@{2}: commit: WIP

git checkout 8b4f2a1    # 直接跳回那个状态
git branch rescue 8b4f2a1  # 或者建分支保存

git reflog --since="2 hours ago" 限定时间窗减少噪音。

Step 4:已 push 已 merge 的场景——用 revert

如果 commit 已经在 main 上,不要 force push。生成反向 commit:

git revert <bad-sha>           # 单个 commit
git revert <bad-sha>^..<bad-sha2>   # 一段范围
git revert -m 1 <merge-sha>    # revert 一个 merge commit

revert 会生成新 commit,安全 push。

Step 5:从 stash 恢复你之前保险的状态

如果 rollback 做错了想回到 Step 1 的 snapshot:

git stash list
# stash@{0}: On main: before rollback 2026-05-22

git stash apply stash@{0}    # 应用但保留 stash
git stash pop stash@{0}      # 应用并删除 stash

# 或者直接 checkout 安全分支
git checkout backup/before-rollback-20260522-1430

预防建议

  • 让 AI 工作前 working tree 必须是干净的:git status 必须 nothing to commit,脏的先 stash
  • 在 CLAUDE.md / AGENTS.md 写:“每完成一个原子改动就 git commit,绝不一次 commit 多个无关改动”——commit 越小,回滚损失越小
  • 让 agent commit 一律加前缀 AI:git config commit.template),方便之后 git log --grep="^AI:" --since="1 day" 一次找出所有 AI commit
  • 高风险任务(删大量文件、改 lockfile、改 migration)让 agent 在执行前先 git tag wip/before-<task>,回滚一行命令
  • 禁止 agent 直接执行 git reset --hard / git push --force / git rebase -i——这些命令必须人来按
  • 关键分支开启 GitHub branch protection,禁止 force push,确保 reflog 之外还有远端历史可恢复

相关阅读

标签: #AI 编程 #排查 #排查