Claude Code 会话分支实战:用 checkpoint 给 CLI 探索留一条安全岔路
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_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 这个不加 UTM。先去控制台建一个 Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。拿到 Key 之后不要写死在 shell 历史里用环境变量注入。export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-你的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_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 你可以先在网页端发一条消息验证 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-experimentClaude 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/apiKey 没有多余空格claude -p 只回复 ok能通。通道问题不要和会话问题混在一起排查。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite Key 在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。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_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 这样 Key 和额度管理不用每次手动切。Claude Code 的接入细节可以对照 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。最后留一句我踩过的坑别在两个终端里无 fork 恢复同一个 sessiontranscript 交错写入之后你连哪条消息属于哪条思路都分不清。分支就是给并行探索准备的隔离层用起来。