Claude Code 12:Worktree 隔离——多 Agent 并行开发不踩踏

当我们的 Agent 团队进化为能够自主抢单的自组织集群后,一个致命的物理瓶颈赫然挡在面前:文件系统踩踏(File Stepping Collision)。

设想 Worker A 正在重构用户鉴权逻辑,Worker B 正在为 API 编写集成单测。如果两者都在同一个物理工程目录下工作:

  1. 未提交代码相互污染:Worker A 改了一半的代码还未通过单测,直接被 Worker B 读到,导致 Worker B 的单测报错并得出错误结论;
  2. 临时文件同名覆写:两个 Worker 都想创建 temp_patch.py 或生成 build/ 产物,相互覆盖导致文件损坏;
  3. Git 状态彻底混乱:git diff 混杂了多个智能体的散乱改动,任何一个任务失败,无法干净地单独回滚其修改。

纯粹的内存隔离或通信协议无法解决物理磁盘上的并发冲突。Claude Code 给出的终极工程解法是:利用 Git 原生的 Worktree 机制,为每个并行任务分配专属的物理工作树目录。

本文深入剖析其工作树隔离生命周期、缓存清理守卫与主干安全合并。


Git Worktree 物理隔离机制

Git Worktree 允许同一个本地代码仓库在多个独立的物理文件夹中同时检出(Checkout)不同分支:

与完整的 git clone 相比,创建 Worktree 只需要几十毫秒且几乎不占硬盘空间,因为它们共享底层的 .git 对象库。但对操作系统和运行在其中的 Agent 而言,每个 Worktree 是一个完完全全干净、独立的物理工作目录。


生产源码探秘:EnterWorktreeTool 的严密规程

在 Claude Code 源码 src/tools/EnterWorktreeTool/EnterWorktreeTool.ts 中,进入 Worktree 隔离有着严苛的安全状态切换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// EnterWorktreeTool.ts 核心流程(生产源码精简)
export class EnterWorktreeTool {
async call(input, context) {
// 1. 防嵌套拦截:若当前会话已经在某个 worktree 内,直接拒绝
if (getCurrentWorktreeSession()) {
throw new Error('当前会话已处于 Worktree 隔离环境中,禁止嵌套创建!')
}

// 2. 规整至主干根目录:确保从标准工程根路径创建子分支
const mainRepoRoot = findCanonicalGitRoot(getCwd())
if (mainRepoRoot && mainRepoRoot !== getCwd()) {
process.chdir(mainRepoRoot)
setCwd(mainRepoRoot)
}

// 3. 在 .claude/worktrees/<slug> 创建新分支与物理工作树
const slug = input.name ?? getPlanSlug()
const session = await createWorktreeForSession(getSessionId(), slug)

// 4. 切换进程的当前物理工作目录 (CWD)
process.chdir(session.worktreePath)
setCwd(session.worktreePath)

// 5. 核心防御:清空所有与路径绑定的内存缓存
clearSystemPromptSections() // 系统提示词根据新工作区重新生成
clearMemoryFileCaches() // 重新加载新工作树内的 CLAUDE.md 等配置
getPlansDirectory.cache.clear?.()

// 6. 持久化当前工作树会话状态,用于故障恢复
saveWorktreeState(session)

return {
worktreePath: session.worktreePath,
branch: session.worktreeBranch
}
}
}

关键设计细节

  • 工作树专有目录:Claude Code 统一将工作树存放在 .claude/worktrees/<slug>,并在根目录 .gitignore 中将其排除,绝不污染用户主源码树;
  • 环境缓存彻底清洗(Cache Invalidation):当进程切换 chdir 后,前序针对主工程缓存的 CLAUDE.md、上下文规范必须立即清空重读,防止 Agent 带着旧路径的思维定势去操作新工作树。

退出与主干合并流:ExitWorktreeTool

当 Worker 在专属 Worktree 中完成代码重构后,如何安全回归主干?

这种门禁设计确保了唯有在沙箱内跑通全部测试的提交,才具备合入主分支的资格。任何半成品或损坏的代码,被永久阻绝在独立的临时分支之外,清理时一条命令即可抹去,绝不污染主干。


极简 Python 实现:WorktreeSandboxManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import subprocess
import os
import shutil
from pathlib import Path

class WorktreeSandboxManager:
def __init__(self, repo_root: str):
self.repo_root = Path(repo_root).resolve()
self.worktrees_dir = self.repo_root / ".claude_worktrees"
self.worktrees_dir.mkdir(exist_ok=True)

def create_sandbox(self, task_name: str) -> str:
"""为特定任务创建独立的物理工作树沙箱"""
branch_name = f"task/{task_name}"
target_dir = self.worktrees_dir / task_name

if target_dir.exists():
raise FileExistsError(f"沙箱路径 {target_dir} 已存在")

# 调用 git worktree add 原生命令
cmd = f"git worktree add -b {branch_name} {target_dir} HEAD"
res = subprocess.run(cmd, shell=True, cwd=self.repo_root, capture_output=True, text=True)
if res.returncode != 0:
raise RuntimeError(f"创建工作树失败: {res.stderr}")

print(f"[沙箱就绪] 任务 [{task_name}] 已挂载至独立物理空间: {target_dir}")
return str(target_dir)

def cleanup_sandbox(self, task_name: str, force: bool = False):
"""回收临时工作树"""
target_dir = self.worktrees_dir / task_name
branch_name = f"task/{task_name}"

# 1. 移除工作树挂载
cmd = f"git worktree remove {'--force' if force else ''} {target_dir}"
subprocess.run(cmd, shell=True, cwd=self.repo_root, capture_output=True)

# 2. 如果已合入主干,可清理对应临时分支
subprocess.run(f"git branch -D {branch_name}", shell=True, cwd=self.repo_root, capture_output=True)
print(f"[沙箱回收] 任务 [{task_name}] 的工作树与临时分支已安全清除。")

专栏全景总结:从 30 行到工业级 Coding Agent

走到这里,《Claude Code 核心与实战》 系列的 13 篇文章全部完结。

让我们回顾这趟扎实的工程演进之旅:

1
2
3
4
5
6
7
8
9
10
11
12
[s01] Agent Loop        : 30 行 while True + stop_reason,建立自主循环执行核
[s02] Tool Use : 引入 Dispatch Map 字典路由,给模型装上带沙箱的文件读写能力
[s03] TodoWrite : 建立任务看板,强制单一进行中任务,阻断长链路思维迷航
[s04] Subagent : 派生独立上下文的子智能体,阻断大段探索日志污染主对话
[s05] Skill Loading : 打造两层渐进披露与 SKILL.md,实现 20,000+ Token 的常驻消减
[s06] Context Compact : 搭建 Microcompact 静默清理与带标识符保留的深度压缩
[s07] Task System : 从内存转向磁盘持久化 DAG,支持前置依赖与崩溃零损失续跑
[s08] Background Tasks : 非阻塞后台子进程与增量输出流,解放被慢命令卡死的主循环
[s09] Agent Teams : 建立确定性 ID 与磁盘 Mailbox 架构,实现零中间件依赖的消息总线
[s10] Team Protocols : 落地强类型结构化握手,杜绝暴力关机与通信死锁
[s11] Autonomous Agents : 落地 Coordinator 编排与原子锁动态抢单,迈入弹性自组织集群
[s12] Worktree Isolation: 借助 Git Worktree 物理目录沙箱,彻底根除并发文件写踩踏

终局思考:什么是好的 Agent 架构?

回顾全部 12 个机制,你会发现:优秀的技术系统从来不是靠引入深奥的框架概念包装出来的,而是始终直面最朴素的物理限制。

  • Token 窗口不够大?$\rightarrow$ 做 Microcompact 与 Skill 动态加载;
  • 长任务容易遗忘?$\rightarrow$ 做持久化 DAG 任务图与专注力约束;
  • 多任务容易冲突?$\rightarrow$ 做文件消息总线与 Git Worktree 物理隔离。

每一行代码都可追溯、每一个决策都有数据与边界支撑。希望这套从 30 行代码出发的架构演进体系,能帮助你在自主智能体与系统工程的深水区中,走得更加沉稳而通透!