你在 src/billing.ts 改了两小时——debug 日志、做一半的重构、一段写着推理的 TODO 注释——都没提交。让 Claude Code 跑了个”小修”。新的 diff 把整个文件替换了,你的改动从 working tree 里消失,而且你从没 commit 过。
先说最快的修法: 如果文件是在还开着的那个 Claude Code session 里被覆盖的,运行 /rewind(或在空的输入框里按两下 Esc),选这个 session 的第一个 checkpoint,再选 Restore code。Claude Code 会在每次改文件之前先捕一份快照,所以跑 agent 之前的内容通常还在。如果那个 session 已经关了,直接跳到编辑器 local history(Step 2),它能救回剩下的大部分情况。
这比你感觉的更经常可恢复。Claude Code 留 per-session checkpoint、编辑器留 local history、git 留 reflog,即便文件被完全覆盖,编辑器 buffer / swap 文件 / 备份 snapshot 里也常还有一份。下面是接下来 10 分钟的抢救路径,以及分层的「以后不再发生」设置。
先判断你属于哪种情况
| 症状 | 最可能的原因 | 去哪一步 |
|---|---|---|
| 覆盖发生在还开着的那个 session 里 | Claude 重写了文件,存在 checkpoint | Step 1(/rewind) |
| session 关了,但编辑器还开着 | buffer / local history 里还留着你的改动 | Step 2-3 |
| agent 在覆盖之后自动 commit 了 | commit 捕的是覆盖之后的状态 | Step 2 + Step 4(reflog) |
| 你切分支前 stash 过 | stash 在错的分支或根本没 pop | Step 4(git stash list) |
| 两个 agent(或 Claude + Cursor)改了同一个文件 | 后写者赢 | Step 2,再用 Step 6(worktree)预防 |
常见原因
按命中率从高到低排列。
1. 跑 agent 时 working tree 是脏的
Agent 的 Read 捕到了你脏的状态,随后的 Write 用它自己版本的改动替换了整个文件。你的未提交改动没进 diff,因为 agent 没有保留它们。如果 session 处在 acceptEdits 模式(改动自动应用、不再弹确认),这种情况会更常见——而这恰恰也是你最不容易注意到整文件 write 的时候。
如何判断:看 agent 的 tool 调用。如果它用的是 Write(不是 Edit)并替换了整个文件,你的未提交改动就被静默覆盖了。
2. 多个 agent 同时动同一个文件
两个 Claude Code session,或者一个 Claude 加一个 Cursor,同改一个文件。后写者赢,先写者的改动消失。
如何判断:git log src/billing.ts 看不出异常,但文件内容只匹配一个 agent 的 intent——另一个 agent 的活儿没了。
3. Agent 用了整文件 write 而非 Edit/patch
大改动时 agent 有时会整文件重写。你在文件里加的 inline debug log 在 Claude 重写时,如果没被当成任务的一部分,就不会被保留。
如何判断:diff 显示整个文件都改了(每行都动),而不是局部 hunk——这就是整文件重写。
4. 你以为保存了,其实没保存
改动还在未保存的 buffer 里。关闭或重启编辑器丢掉了 buffer,磁盘上是 agent 保存的版本。
如何判断:你不记得 agent 跑之前按过 Cmd+S——磁盘上没有改动,但编辑器 recovery 里可能还有。
5. Agent commit 了一个 checkpoint,把它的覆盖也一起提交了
Claude 被 prompt 要求干完活后 commit。它的 commit 捕的是覆盖之后的状态。你的改动没进 git——但可能还在 /rewind checkpoint 或编辑器 local history 里。
如何判断:git log -1 --stat 显示近期有个 agent commit,而你的改动不在任何 commit 里。
6. Stash 应用错了,或在错的分支 pop 了
你 stash 了改动,为了 agent 任务切了分支,stash 在错的分支被 pop 了——或者根本没 pop。
如何判断:git stash list 显示有你没意识到还挂着的 stash,或者你的分支缺了你记得 stash 过的改动。
最短修复路径
按紧迫度排列。在核完 checkpoint 和 local history 之前,不要关 Claude Code session 或编辑器。
Step 1:先试 Claude Code 的 /rewind(如果 session 还开着)
从 2025 年底起,Claude Code 会自动给你的代码打 checkpoint。按官方 checkpointing 文档的说法,它会”在每次改动之前捕获代码状态”,每个 prompt 都会创建一个 checkpoint,checkpoint 跨 session 保留、30 天后自动清理。因为 agent 改的正是你未提交工作所在的那个文件,改动之前的内容通常就在第一个 checkpoint 里。
1. 在 Claude Code session 里运行 /rewind
(或在输入框为空时按两下 Esc)
2. 菜单会列出本 session 你发过的每个 prompt——选覆盖任务之前的那个点
3. 选 "Restore code"(只回退文件,保留对话)
- "Restore code and conversation" 会连聊天一起回退
- "Restore conversation" 保留当前代码——这里不要选它
文档里有两个重要限制:
- Bash 改动不被追踪。 如果 Claude 是用
mv、cp或重定向(> file)这类命令覆盖文件,而不是它的文件编辑工具,/rewind救不了,要走 Step 2-4。 - 外部改动不被追踪,除非它们改的是本 session 也改过的文件。你手动改的
src/billing.ts之所以能被捕到,只是因为 Claude 后来也改了同一个文件。一个你改过、但 Claude 从没碰过的文件不会出现在 checkpoint 里。
如果 /rewind 里显示的内容不对,或者 session 已经关了,往下走。Checkpoint 是本地的 undo,不能替代 git。
Step 2:查编辑器 local history
现代编辑器都保留独立于 git 的文件历史,在 Claude session 已经没了的时候,这是回收率最高的恢复手段:
VS Code / Cursor / Windsurf:
Explorer 里文件右键 -> Open Timeline
或:View -> Timeline
里面有「Saved 15 min ago」之类的条目——点进去 diff 或 restore。(VS Code 的 Timeline 汇总了内置的 Local History provider,即便文件在磁盘上被覆盖,条目也还在。)
JetBrains(IntelliJ、WebStorm、PyCharm):
文件右键 -> Local History -> Show History
Sublime Text: 装「Local History」插件,没有内置等价功能。
这一步能救回大多数「agent 覆盖了我的改动」的情况。在做任何破坏性操作之前先试它。
Step 3:用 Cmd+Z 检查未保存的 buffer
如果文件还在编辑器里开着、且你没重启过,buffer 的 undo 历史可能还留着你的改动:
1. 在编辑器里打开受影响的文件
2. 点击文件,聚焦 buffer
3. 反复按 Cmd+Z(或 Ctrl+Z)——看 diff 一步步回退到你的改动
4. 改动恢复了就停
5. 立刻保存(Cmd+S)
如果 Cmd+Z 跳过了你的改动,或者 undo 历史被一次 reload 清掉了,退回 local history(Step 2)。
Step 4:查 git reflog、stash 和备份文件
即便你的改动从没正式 commit,也可能存在 checkpoint commit 或备份:
# 找丢失的 commit,包括 Claude Code 的自动 commit
git reflog --all | head -30
# 找所有分支上的 stash
git stash list
# 编辑器 swap / 备份文件(vim、emacs)和 merge 备份
ls -la .*.sw[opq] 2>/dev/null
find . -name "*.orig" -o -name "*~" 2>/dev/null
如果某个 reflog 条目里有你的改动,只把那个文件取回来,不动 working tree 的其余部分:
git checkout <hash> -- src/billing.ts
Step 5:真丢了就重写,并立「跑 agent 前 commit」规矩
如果所有恢复路径都试过还没回来,把改动重写一遍,然后立规矩,让它不再复发:
# 跑 agent 前的仪式
git status # 确认有哪些未提交内容
git add -A && git commit -m "wip" # 给所有改动打 checkpoint
# 现在再跑 agent
一个之后再 squash 的 wip commit,远比丢两小时的工作便宜。
Step 6:多 agent / 并行工作用 git worktree
如果你经常一边跑 agent 一边手动改,给每个 agent 一个独立的 working tree,写操作就撞不到一起:
# 给 agent 工作在独立分支上建一个 worktree
git worktree add ../project-agent -b agent-task
# 或者让 Claude Code 帮你创建并管理:
claude -w
之后在 project/ 里手改,在 project-agent/ 里跑 agent。各自有独立的 working tree,agent 的写永远盖不掉你手动的改动。截至 2026 年中,团队普遍能稳定跑每人 4-8 个并行 worktree。agent 完事后,按你的节奏把它的分支 merge 回来。
怎么确认已经修好
恢复之后,继续干活之前先验证:
git diff src/billing.ts # 你的改动回到 working tree 里了
git status # 确认文件按预期处于 staged / modified 状态
打开文件,对一下你记得的几个标记(那段 TODO 注释、那行 debug log)。然后立刻打一个 wip commit,让恢复出来的活儿进到 git 里,而不只是停在磁盘上。
预防建议
-
「跑 agent 前 commit」是硬规矩。 你在乎的脏 working tree,绝不要直接跑 agent。
-
探索性 wip 不想 commit,就用
git stash,之后用git stash list复核。 -
并行的「agent + 手动编辑」用 git worktree(或
claude -w)——working tree 分开就不会撞。 -
高风险任务用 Plan Mode。 让 session 在
plan权限模式下启动(用 Shift+Tab 循环切换模式,或--permission-mode plan);Claude 会探索并给出方案,但在你批准之前不改源文件。 -
用 deny 规则保护特定文件,而不是只在 prompt 里好言相劝。在
.claude/settings.json里,deny 规则遵循 gitignore 规范,由 Claude Code(而非模型)强制执行:\{ "permissions": \{ "deny": ["Edit(/src/billing.ts)"] \} \}Edit(...)规则覆盖 Claude 所有的文件编辑工具(以及它识别的 Bash 文件命令,比如sed)。对于任意脚本,再配一个 PreToolUse「block edits to protected files」hook 或 OS 沙箱。 -
在 agent prompt 里也点名 Claude 不能动的文件:「不要改
src/billing.ts,里面有未提交的活儿。」(这只塑造意图、不构成强制边界——要强制就用 deny 规则。) -
编辑器开自动保存并设短 debounce,让未保存的 buffer 状态尽量少出现。
-
在
CLAUDE.md里要求 Claude 对局部改动优先用Edit式 patch,不要整文件Write。
常见问题
/rewind 能撤销 agent 通过 shell 命令做的改动吗?
不能。Checkpoint 只追踪通过 Claude 文件编辑工具改的文件。任何由 Bash 命令改动的东西——rm、mv、cp、重定向,或某个脚本——都不会被捕获,也不能用 /rewind 回退。要退回 git reflog、local history 或备份。
我的 session 关了,还能用 checkpoint 吗?
Checkpoint 跨 session 保留 30 天,所以恢复同一个 session(claude --resume)能把 rewind 菜单调回来。但如果 working tree 之后又往前走了,restore 可能会盖掉更新的工作——先 diff。拿不准就改用编辑器 local history(Step 2)。
Claude 为什么用整文件 Write 而不是 Edit?
对于大的或结构性的改动,agent 有时会整文件重写而不是逐 hunk 打 patch。这是预期行为,不是 bug——但这也正是为什么文件里没纳入任务的未提交改动会被抹掉。先 commit,或对那个路径 deny Edit,都能避免。
先 commit 真能挡住这种事吗?
能。有一个干净的 commit,最坏情况也只是 git restore 或 git checkout 回那个 commit——你的活儿永久进了 git 历史,而不只是停在 working tree。这是杠杆最高的一个习惯,也是为什么 checkpoint 是「补充而非替代」版本控制。
deny 规则没挡住那次编辑,怎么回事?
检查路径锚点对不对:/src/billing.ts 是相对于项目根目录的,而 //src/billing.ts 才是绝对文件系统路径。用 /permissions 确认规则已加载。deny 规则覆盖 Claude 的内置工具和它识别的 Bash 文件命令,但不覆盖自己打开文件的自定义脚本——那种情况要用沙箱做 OS 级强制。
相关阅读
- Claude Code 改错文件
- Claude Code 重构 scope 太大
- Agent 回滚不彻底
- Claude Code 新手入门
- Claude Code 工作流
- Claude Code 项目配置
标签: #排查 #Claude Code #排查 #覆盖