Claude Code 的检查点回滚不回来:checkpointing 存了什么、没存什么

2026-08-18

有个场景大概不少人碰到过:让模型改了一轮代码,结果越改越乱,于是回滚到某个提示词之前,回滚也提示成功了,可打开文件一看,有些改动确实退回去了,有些还在那儿摆着。第一反应通常是「回滚失败了」,但按官方文档的说法,更常见的情况是——那些改动从一开始就没被检查点记录

checkpointing 这个机制的边界,官方文档 code.claude.com/docs/en/checkpointingcode.claude.com/docs/en/agent-sdk/file-checkpointing 两页写得相当直白,而且专门列了「Limitations」小节。这篇就沿着这两页把覆盖范围和不可恢复的部分讲清楚,顺带给出几个能自己动手确认的判定动作。

一、先划清覆盖范围:记了什么

交互式那一页写明,checkpointing 会在每一条用户提示词之前自动捕获代码状态,也就是说每发一次提示词就产生一个新检查点。文档同时写明,一个会话内只保留最近若干个检查点的文件快照,更老的检查点被丢弃时会删掉没有其它检查点再引用的快照文件——只有每个文件的第一份快照会留下,因为 VS Code 扩展要拿它当会话 diff 的基线。检查点跟着对话一起存,所以恢复(resume)会话之后仍然可以跑 /rewind;它们也会随会话在保留期到期后被清掉,保留期由 cleanupPeriodDays 设置项控制(具体天数以官方文档为准,本文不写数值)。

Agent SDK 那一页把「记什么」说得更精确:跟踪的是通过 Write、Edit、NotebookEdit 三个工具产生的文件改动。检查点系统跟踪三类东西——会话中创建的文件、会话中修改的文件、以及被修改文件的原始内容。回滚时,Claude Code 会删除它创建的文件,并把它修改过的文件还原到那个时间点的内容。

这句话反过来读就是本文的重点:不经过这三个文件编辑工具的改动,一律不在覆盖范围内。

二、现象与判定:怎么确认是「没记进去」

判定动作 1:看 /rewind 有没有给出代码恢复的选项

交互式文档列出了 /rewind 打开后可选的六个动作:Restore code and conversation、Restore conversation、Restore code、Summarize from here、Summarize up to here、Never mind。

关键在紧接着那句限定:只有当所选检查点确实有被跟踪的文件改动可回退时,两个涉及代码恢复的选项才会出现;如果那个点之后没有捕获到文件编辑,可选的就只剩 Restore conversation、两个 summarize 和 Never mind。

所以这是个很好用的判定动作:如果你明明记得那一轮改了文件,而这两个代码恢复选项压根没出现,那结论不是「回滚坏了」,而是那一轮的改动没有被记录成检查点

顺带一个容易踩的入口问题:文档写明 /rewind 也可以在输入为空时连按两次 Esc 打开;若输入里有文字,连按 Esc 是清空文字而不是打开这个列表(清掉的文字会进输入历史,按 Up 能召回)。

判定动作 2:看回滚后的跳过提示

如果代码恢复选项出现了、也执行了,但部分文件没变,文档给了明确信号:Claude Code 会跳过任何是符号链接或硬链接的被跟踪路径,并给出 Restored the code, but skipped N files 这样的告警,被跳过的文件保持当前内容不变。

文档还给了定位手段:在执行恢复之前/debug 打开调试日志,日志文件 ~/.claude/debug/<session-id>.txt 里会写出每一个被跳过的路径。全部跳过原因与恢复步骤见官方错误参考页 code.claude.com/docs/en/errorsrestored-the-code-but-skipped-files 一节)。

路径展开在 Windows 上要换算:文档给的是 POSIX 写法 ~/.claude/debug/,Windows 上对应家目录下的 .claude\debug\,PowerShell 里可以用 $env:USERPROFILE\.claude\debug\——这一层是路径写法的换算,官方文档只给了 POSIX 一种写法。

文档举了两个很日常的例子:dotfile 管理器软链进项目的配置文件、以及 pnpm 硬链接到位的文件,都落在这一类里。前者在 Linux/macOS 上更常见,后者在装了 pnpm 的 Windows 项目里同样会遇到。

用 Claude Agent SDK 的话不用靠肉眼数。SDK 文档写明,RewindFilesResultskippedLinks 字段会统计每一个被跳过的路径。SDK 这一页列出的跳过条件比交互式那页更全:符号链接、硬链接或其它非常规文件;父目录已不再解析到检查点时刻的位置的被跟踪文件;以及备份读不安全的文件。

这里有一条必须照实标出来的版本限定:「跳过」这个行为要求 Claude Code v2.1.216 或更高版本;在此之前的版本,回滚会直接穿透链接进行写入和删除。 也就是说旧版本不是「跳过后提示你」,而是照改不误——如果你的现象是链接目标被动了,先核对版本。

三、文档明写的不可恢复情形

交互式文档的 Limitations 分了五节,SDK 文档的限制表也有五行,合起来是下面这些。这一节是本文的落点,建议对着官方原文再看一遍。

情形文档写明的行为文档给的处置
bash 命令造成的文件改动不跟踪,无法通过 rewind 撤销只有经文件编辑工具的直接编辑才被跟踪
subagent 的编辑通常不被记入你会话的检查点用 git 回退
会话外的改动你在 Claude Code 之外的手工修改、以及其它并发会话的编辑,通常不被捕获;文档给了一个例外——除非它们恰好改到当前会话也改过的那批文件文档未给出恢复手段
符号链接 / 硬链接路径恢复时跳过,文件保持当前内容让 Claude 反向编辑,或自己改回来
目录的创建、移动、删除回滚不撤销(SDK 页写明为 File content only)文档未给出恢复手段
远程 / 网络文件不跟踪(SDK 页 Local files 一行)文档未给出恢复手段
跨会话检查点绑定在创建它的那个会话上只能在同一会话内回滚

几处值得展开的:

bash 改动不跟踪。 文档直接给了例子,rm file.txtmv old.txt new.txtcp source.txt dest.txt 这类操作产生的文件变化都撤不回来。杀伤力在于:模型完全可能用 shell 完成一次「重命名」或「批量替换」,从你的角度看那就是一次文件修改,但它不在 Write/Edit/NotebookEdit 的口径里。

subagent 的编辑分两种走向,文档区分得很细:

  • 前台运行的 forked skill:即带 context: fork 的 skill,在前台运行时是在你自己的这一轮里改工作树的,所以 rewind 能照常还原它的编辑。让 fork 跑在前台要设 background: false;文档同时提到有少数情况无论这个设置如何都会在前台运行,具体列在 skills 那一页。
  • 其它任何 subagent:rewind 不还原其编辑,文档明说用 git 回退。这里面包括默认就是后台运行的 forked skill,以及后台跑的 /code-review --fix

「默认是后台」这一点最容易翻车——你以为 skill 的编辑也在检查点里,实际按默认配置它不在。

目录操作不撤销。 SDK 限制表里 File content only 那一行写得很短,但含义是:回滚只管文件内容,创建、移动、删除目录这些不会被还原。如果一轮改动里包含了目录结构调整,回滚之后目录结构是留在原地的。

四、处置之后怎么验证

  • 看跳过计数归零:再次执行恢复时,如果不再出现 Restored the code, but skipped N files,说明这一轮没有路径被跳过。
  • 看 debug 日志:恢复前开 /debug,恢复后翻 ~/.claude/debug/<session-id>.txt(Windows 下在家目录的 .claude\debug\ 里),逐条核对被跳过的路径是不是都用别的方式处理掉了。
  • SDK 侧读返回值:检查 RewindFilesResultskippedLinks
  • 不在覆盖范围内的部分只能靠版本控制核对。 交互式文档最后一节标题就是 Not a replacement for version control:检查点是为会话级的快速恢复设计的,永久历史与协作仍然要靠 Git 的提交、分支来做。bash 改动与 subagent 编辑这两类文档明确指向 git 回退,验证方式自然是看工作区 diff 是否干净——这是版本控制的常规做法,不是 Claude Code 文档的内容。

五、什么情况说明不是这个原因

最后这一节别跳过,下面几种现象都不该归因到「覆盖范围」上。

其一,你用的是 summarize 而不是 restore。 文档写明摘要不改磁盘上的文件,原始消息也仍然留在会话记录里,所以选了 Summarize 之后文件没变属于文档描述的行为。文档把它类比成一次定向的 /compact;若你要的是「保留原会话另起一支试别的路子」,文档指向的是 /branchclaude --continue --fork-session

其二,SDK 侧的回滚本来就不回滚对话。 这是两页文档口径不同、最容易看岔的一处:交互式 /rewind 可以同时恢复代码与对话,而 SDK 文档明确写了,rewindFiles()(TypeScript)/ rewind_files()(Python)只把磁盘上的文件恢复到之前的状态,不回滚对话本身,调用之后对话历史和上下文保持不变。所以在 SDK 里发现「文件回去了、对话没回去」,那是文档写明的设计,不是故障。

其三,报的是明确的错误信息。 SDK 文档的排障小节列了几条对应关系:

  • 拿不到 message.uuid:原因是没设 replay-user-messages。Python 加 extra_args={"replay-user-messages": None},TypeScript 加 extraArgs: { 'replay-user-messages': null }
  • No file checkpoint found for this message:常见原因是原会话没启用 checkpointing,或者会话没有正常结束就去 resume 回滚。
  • File rewinding is not enabled:在非交互式回滚时没开 checkpointing,包括裸跑 claude -p --rewind-files,以及 resume 出来的那个 SDK 会话自己没启用。文档写明,SDK 只在会话启用了 enable_file_checkpointing(Python)/ enableFileCheckpointing(TypeScript)时才会在内部设置 CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING 环境变量,裸 CLI 从不设置它。
  • ProcessTransport is not ready for writing:在响应流迭代完之后才调 rewind,此时到 CLI 进程的连接已经关了。文档给的做法是用空提示词 resume 会话,再在新的 query 上调用回滚。

裸 CLI 的写法官方文档给的是这一条:

CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING=true claude -p --resume <session-id> --rewind-files <checkpoint-uuid>

文档还特意说明,--rewind-files 这个标志不出现在 claude --help 的输出里,但 CLI 接受上面这种写法。

这条命令的前半段是 POSIX shell 的行内环境变量写法,Windows 上要拆开:PowerShell 里先 $env:CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING="true" 再执行 claude 那一段,cmd 里用 set 设置后再执行——这是 shell 层面的差异,官方文档只给了 POSIX 一种写法。以上为按官方文档中的参数语义组合的示例,未经实测,以官方文档与 --help 的实际输出为准。

其四,会话对不上。 SDK 限制表里写明检查点绑定在创建它的会话上。拿着一个 UUID 去另一个会话里回滚,问题出在会话而不是覆盖范围。

其五,跑了 /clear 之后找不到之前的记录。 交互式文档写明,如果你在同一个 Claude Code 进程里跑过 /clear,rewind 列表会多出一条 /resume <session-id> (previous session) 条目,选它可以回到 /clear 之前那段对话;这条目在你退出 Claude Code 或恢复了别的会话之前一直可用,并且要求 Claude Code v2.1.191 或更高版本。更早的版本文档让你改跑 /resume 从列表里挑之前那个会话。

把这五条排除掉之后,剩下的「回滚了但没回来」,基本都能落回第三节那张表里的某一行——不是回滚失败,是那次改动从来就不在检查点的口径内。


本文依据 Claude Code 官方文档(code.claude.com/docs)于 2026-08-17 的公开内容整理。 该产品闭源,本文只复述官方文档写明的机制,不推断其内部实现我们没有对文中涉及的功能做过实测,因此不涉及界面外观、操作手感与运行速度的任何描述。 该产品迭代频繁,文中涉及的命令、配置项与默认值随版本变动,请以官方文档最新内容为准。 本文不涉及价格、额度与限流的具体数值,相关信息请以官方定价与用量说明页为准。

安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。