☰
Claude Code 会话分支实战:用 checkpoint 给 CLI 探索留一条安全岔路
2026/9/26 9:57:18 网站建设 项目流程

1. 长会话里最怕的不是报错,是思路被污染

Claude Code 会话分支(session branch)和 checkpoint 是 CLI 里两个容易被混用的能力:前者复制对话历史、让你从同一个上下文岔出去试另一条路,后者跟踪文件编辑、让你把工作区退回某个 prompt 之前的状态。它们适合谁?适合那些在长会话里已经让 Claude Code 读了十几个文件、跑过测试、形成了一套判断,却突然想换一种解法的开发者。我自己在排查一个 OData V4 批量重试的问题时,主会话已经积累了 CDS view、behavior definition、service binding 的完整分析,这时候团队里有人提出“不如把整个 service layer 重构掉”。如果直接在原会话里追问,前面那条最小改动的路线就被新讨论盖住了,等想回到原判断,Claude 的推理链路里已经混进了重构话题。会话分支解决的正是这个:它分的是思路,不是代码。而 checkpoint 解决的是另一件事:文件真的被改了,怎么退回去。把这两个混为一谈,是长会话里最容易踩的坑。下面我把 settings.json、config.toml 骨架、TaoToken 统一通道配置,以及 branch 创建、checkpoint 回滚、隔离验证的完整操作串起来,你可以直接跟着做。

2. 前置:用 TaoToken 统一 Key 与 API 通道

在动 branch 之前,先把模型通道固定下来。Claude Code CLI 支持通过环境变量或配置文件指定 API 端点,我习惯用 TaoToken 做统一入口,这样换模型、换 Key 都不用改 CLI 本身的逻辑。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api (这个不加 UTM)。先去控制台建一个 Key,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,Key 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。拿到 Key 之后,不要写死在 shell 历史里,用环境变量注入。

export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="sk-你的TaoToken密钥"

如果你用的是 Claude Code 的 settings.json(通常在~/.claude/settings.json或项目级.claude/settings.json),可以写成下面这个骨架。注意 env 里的键名要和 CLI 读取的一致,不同版本对ANTHROPIC_AUTH_TOKEN和ANTHROPIC_API_KEY的优先级略有差异,我实测下来两个都填最稳。

{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的TaoToken密钥", "ANTHROPIC_AUTH_TOKEN": "sk-你的TaoToken密钥" }, "permissions": { "allow": [], "deny": [] } }

如果你更习惯用 config.toml(部分封装工具或自建脚本会读这个),骨架如下。这里把 base_url 和 api_key 分开写,方便你后续接 Coding Plan 时只改一处。

[api] base_url = "https://taotoken.net/api" api_key = "sk-你的TaoToken密钥" timeout_seconds = 120 [session] auto_checkpoint = true checkpoint_retention_days = 7

注意:settings.json 里的 permissions.allow 不要提前塞一堆规则。会话分支出来的新 session 不会继承原会话里“allow for this session”的临时授权,这是安全设计,不是 bug。你提前写死的全局 allow 反而会绕过这个边界。

配置好之后,先用一次最小请求确认通道通。模型对话入口在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite ,你可以先在网页端发一条消息验证 Key 有效,再回到 CLI。CLI 侧验证命令:

claude -p "只回复 ok 两个字母" --output-format text

如果返回ok,说明 base_url 和 Key 都生效了。这一步别跳过,后面 branch 出问题的时候,你才能确定是会话逻辑的问题,不是通道的问题。

3. 可复制配置:branch 与 checkpoint 的落地骨架

Claude Code 的会话分支有两种触发方式:会话内命令/branch,和命令行参数--fork-session。checkpoint 则是自动的,每个用户 prompt 会创建一个 checkpoint,编辑前捕获代码状态。下面把两套配置和操作骨架都给你。

3.1 会话内 branch 的命名规范

在正在运行的 session 里直接敲:

/branch try-streaming-approach

名字不是装饰。官方文档说/branch后面可以带可选名字,省略的话会用会话第一条 prompt 命名。从 v2.1.198 开始,即使会话经过 compaction,也会越过摘要回看原始第一条 prompt。但自动命名只是兜底,第一条 prompt 很可能是“帮我看一下这个问题”,这种名字在 session picker 里根本没法检索。我的命名习惯是“目标+范围”,比如rap-draft-lock-debug、odata-v4-batch-retry、auth-refactor-with-cache。避免test1、new-way、maybe-fix这种。

3.2 命令行 fork 的写法

早上重新进项目,想拿昨天的会话当底稿另起一条路:

claude --continue --fork-session

如果要从某个具体旧会话分叉,先claude --resume打开 session picker,找到目标会话,再结合 fork 思路进入新副本。session picker 支持搜索、展开分组、按当前 Git branch 过滤、扩大到所有 worktree 或所有项目。

3.3 checkpoint 的配置与触发

checkpoint 不需要你手动敲命令创建,它跟着 prompt 走。但你要确认它开着。在 settings.json 里可以显式声明:

{ "checkpointing": { "enabled": true, "captureBeforeEdit": true, "retainAcrossSessions": true } }

retainAcrossSessions设为 true 时,checkpoint 会跨 session 保留,随 session 一起按清理周期处理。回滚的时候,Claude Code 会列出可用的 checkpoint,你选一个回到那个 prompt 之前的状态。这里要记住:checkpoint 是本地 undo,不是 version control 的替代品。长期历史还是交给 Git。

3.4 branch 与 checkpoint 的职责对照

能力复制/回退的对象典型场景是否影响文件系统
/branch对话历史、工具调用痕迹、上下文同一判断基础上试另一条思路否,但分支里的编辑会真实落盘
--fork-session同上,从 CLI 入口触发重新进场时另起副本否,同上
checkpoint文件编辑状态、会话状态撤回某批文件修改是,回退工作区文件
Git branch/worktree代码提交历史、工作目录长期隔离、团队协作是

这张表建议你贴在显示器边上。我见过太多人以为/branch之后原会话的文件没变,结果两个 session 改同一个工作目录,冲突到怀疑人生。

4. 验证:branch 隔离效果与 checkpoint 回滚实测

配置就绪后,用一个最小可复现的场景验证。我选一个只有两个文件的小项目,避免干扰。

4.1 建立主会话并制造上下文

mkdir -p /tmp/branch-demo && cd /tmp/branch-demo printf 'def add(a, b):\n return a + b\n' > calc.py printf 'from calc import add\n\nprint(add(1, 2))\n' > main.py claude

进入会话后,先让 Claude 读文件、形成判断:

读取 calc.py 和 main.py,说明当前实现,并给出一个把 add 改成支持可变参数的方案,先不要改文件。

等它输出方案后,这就是主会话的“决策现场”。此时执行:

/branch varargs-experiment

Claude Code 会打印两个 session ID,一个是新 branch,一个是原始 session。把原始 session ID 记下来。

4.2 在新分支里试另一条路

在varargs-experiment分支里,给一个明确方向声明:

在这个 branch 里,只验证用 *args 实现可变参数。不要改 main.py 的调用方式,不要引入新依赖。先给出最小改动,再改 calc.py。

让它改完后,查看文件:

cat calc.py

你会看到*args版本已经落盘。这时候回到原 session(用之前记下的 ID):

/resume <original-session-id>

在原 session 里再cat calc.py,你会发现文件还是*args版本——因为 branch 分的是会话历史,不是文件系统。这一步是很多人翻车的地方。要验证“原会话的上下文没被污染”,看的是对话,不是文件。你可以在原 session 里问:

当前 calc.py 的实现是什么?你之前给出的方案是什么?

如果它回答的还是最初那个“支持可变参数”的方案,而不是分支里*args的讨论,说明上下文隔离生效了。

4.3 用 checkpoint 回滚文件

现在文件被分支改过了,主会话的代码状态也变了。用 checkpoint 退回去。在会话里触发回滚(不同版本命令名略有差异,常见的是/rewind或 checkpoint 选择器):

/rewind

选择“编辑 calc.py 之前”的那个 checkpoint。回滚后再次cat calc.py,应该回到最初的return a + b。如果没回去,检查 settings.json 里captureBeforeEdit是否为 true,以及这个 checkpoint 是否在当前 session 的保留范围内。

4.4 验证权限不继承

在主会话里,如果之前批准过“allow for this session”的编辑权限,切到新 branch 后再让它改文件,应该会重新弹审批。这是预期行为。如果你没看到重新审批,检查是不是在 settings.json 里写了全局 allow 规则,那会绕过 session 级边界。

4.5 验证两个终端不要共享 session

开两个终端,都执行claude --continue(不带 fork),然后各发一条消息。你会看到 transcript 里两条消息交错写入,上下文顺序被打乱。正确做法是第二个终端用:

claude --continue --fork-session

这样两个终端各自有独立副本,互不干扰。

5. 本篇常见错排查

报错一:/branch之后找不到原会话。先确认你记下了/branch打印的两个 session ID。原 session 不会被删除,它仍在 session picker 里。用/resume <original-name>或 session picker 展开 root session 找。如果 session picker 里看不到,检查是不是按当前 Git branch 过滤了,试试扩大到所有 worktree 或所有项目。

报错二:branch 名字全是Branched conversation。这是旧版本在 compaction 后的行为。v2.1.198 起会回看原始第一条 prompt。如果你还在旧版本,升级,或者养成显式命名的习惯。显式命名永远比自动命名可靠。

报错三:以为 branch 会保护文件,结果两个 session 改同一目录冲突。这是概念混淆。session branch 隔离上下文,Git branch 或 worktree 隔离代码状态。两条路线都要写代码时,配合git worktree add开两个工作目录,再各自跑 Claude Code。

报错四:checkpoint 回滚后文件没变。检查三点:captureBeforeEdit是否为 true;回滚选的是不是编辑前的 checkpoint;这个 checkpoint 是否已被清理周期删除。checkpoint 跨 session 保留是有期限的,checkpoint_retention_days设太短会提前清掉。

报错五:新 branch 里权限提示消失,直接改了敏感文件。说明你在 settings.json 里写了过宽的全局 allow。session 级授权不继承是安全设计,全局 allow 会绕过它。把 allow 规则收窄到具体路径和操作。

报错六:API 请求 401 或超时。回到第 2 节,确认ANTHROPIC_BASE_URL是https://taotoken.net/api,Key 没有多余空格,claude -p "只回复 ok"能通。通道问题不要和会话问题混在一起排查。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,Key 在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。

6. 把 branch 当成工程判断的岔路控制器

如果你只是偶尔用 Claude Code 问几个问题,/branch可能用不上。但一旦进入架构判断、性能优化、安全修复、权限改造、数据库迁移这类场景,线性会话会让上下文越来越沉。我的习惯是:主会话完成问题理解,到达决策点时先/branch minimal-fix试最小改动,回原会话再/branch refactor-service-layer试彻底重构。两条路线跑完,在 session picker 里展开 root session,比较消息数量、最后活动时间和所在 Git branch。代码层面交给 Git worktree,上下文层面交给 session branch,两层都用上才稳。

如果你要长期跑编码任务或 Agent 流程,建议把通道固定到 Coding Plan,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,这样 Key 和额度管理不用每次手动切。Claude Code 的接入细节可以对照 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。最后留一句我踩过的坑:别在两个终端里无 fork 恢复同一个 session,transcript 交错写入之后,你连哪条消息属于哪条思路都分不清。分支就是给并行探索准备的隔离层,用起来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询