Files
claude-code/docs/agent/worktree-isolation.mdx
CyberScrubber 81073135e2 docs: 审校 Agent 文档术语与架构描述准确性(docs/agent) (#231)
基于源码验证和官方文档对照,修正三份 Agent 文档的内容准确性:

coordinator-and-swarm.mdx:
- 对齐官方术语:Leader→Team Lead, teammate→Teammate, 引入 Mailbox 消息系统
- 修正 Swarm 门控为 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1(原文"默认启用"不准确)
- 拓扑描述改为"星型+P2P混合"(官方有 Team Lead 角色,非纯网状)
- 新增架构组件表、Mailbox 消息系统、Hook 事件(TeammateIdle/TaskCreated/TaskCompleted)、限制说明
- 新增持久化存储路径(~/.claude/teams/, ~/.claude/tasks/)
- 移除虚构的"高水位标记"(claimTask 仅使用文件锁)
- 修正 Scratchpad 描述(Workers 获得,非 Coordinator 拥有)
- 修复引号不匹配

sub-agents.mdx:
- 修正为三路径路由(命名 Agent / Fork / GP 回退),原文仅两种
- 补充 isForkSubagentEnabled() 三前提(feature flag + 非Coordinator + 非非交互式)
- 新增模型解析优先级(4级链 + inherit 运行时解析)
- 新增内置 Agent 表(Explore/Plan/GP/statusline-setup/claude-code-guide)
- 新增 Hook 事件表(frontmatter + settings.json 双级别)
- 补充异步生命周期完整链路(finalizeAgentTool → getWorktreeResult)
- 补充 transcript 存储路径和结果格式细节

worktree-isolation.mdx:
- 区分 createWorktreeForSession vs createAgentWorktree 两种函数
- 修正清理机制为 cleanupWorktreeIfNeeded(非 ExitWorktreeTool 手动操作)
- 补充 usedSparsePaths 字段
2026-04-10 18:00:42 +08:00

186 lines
8.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Worktree 隔离 - Git Worktree 实现文件级隔离"
description: "揭秘 Claude Code 的 git worktree 隔离机制:子 Agent 如何获得独立工作空间worktree 创建/销毁生命周期、路径命名规则和安全防护。"
keywords: ["Worktree", "git worktree", "文件隔离", "多 Agent 隔离", "并行安全"]
---
{/* 本章目标:揭示 worktree 的创建/销毁生命周期、路径命名规则、hook 机制和退出时的安全防护 */}
## 为什么需要文件级隔离
多 Agent 并行工作时,共享同一工作目录会导致三类冲突:
1. **写入冲突**:两个 Agent 同时编辑 `config.ts`,后写的覆盖前写的
2. **状态干扰**Agent A 的测试依赖某个环境状态Agent B 的修改破坏了它
3. **不可区分**:半完成的修改混在一起,无法分辨哪些是哪个 Agent 的
Git worktree 是 git 原生的解决方案——在同一个仓库中创建多个独立工作目录,每个在自己的分支上。
## 目录结构与命名规则
Worktree 文件统一存放在仓库根目录下的 `.claude/worktrees/`
```
<repo-root>/
├── .claude/
│ └── worktrees/
│ ├── fix-auth-bug/ # worktree 工作目录
│ │ ├── .git # 指向主仓库的链接文件
│ │ └── src/... # 独立的文件系统视图
│ └── add-dark-mode/ # 另一个 worktree
│ └── ...
├── src/ # 主工作目录(不受影响)
└── .git/ # 主仓库
```
分支命名规则为 `worktree/<slug>`,其中 slug 由 `validateWorktreeSlug()` 校验:每个 `/` 分隔的段只允许字母、数字、`.`、`_`、`-`,总长 ≤64 字符。未指定时使用 plan slug 自动生成。
## 创建流程EnterWorktreeTool
`EnterWorktreeTool``src/tools/EnterWorktreeTool/EnterWorktreeTool.ts`)的执行链路:
```
EnterWorktreeTool.call({ name? })
1. 检查是否已在 worktree 中(防嵌套)
2. 解析到主仓库根目录findCanonicalGitRoot
如果当前已在 worktree 内chdir 到主仓库
3. 生成 slug用户提供或 plan slug
4. createWorktreeForSession(sessionId, slug)
├── 有 WorktreeCreate hook
│ └── 执行 hook返回 hook 指定的路径(支持非 git VCS
└── 无 hook → git 原生路径:
a. getOrCreateWorktree(repoRoot, slug)
├── 快速恢复:检查 worktree 目录是否已存在
│ └── 读取 .git 指针文件的 HEAD SHA无子进程
└── 新建:
i. mkdir .claude/worktrees/recursive
ii. fetch origin/<default-branch>(有缓存则跳过)
iii. git worktree add -b worktree/<slug> <path> <base>
iv. performPostCreationSetup()sparse checkout 等)
5. 更新进程状态:
- process.chdir(worktreePath)
- setCwd(worktreePath)
- setOriginalCwd(worktreePath)
- saveWorktreeState(session) → 持久化到项目配置
- clearSystemPromptSections() → 重新计算系统提示中的 cwd 信息
- clearMemoryFileCaches() → 重新加载 worktree 中的 CLAUDE.md
6. 返回 worktreePath 和 worktreeBranch
```
### Hook 优先的架构
`createWorktreeForSession()` 首先检查 `hasWorktreeCreateHook()`——如果用户在 settings.json 中配置了 `WorktreeCreate` hook系统完全不调用 git而是执行 hook 命令并将返回的路径作为 worktree 路径。这允许非 git 版本控制系统(如 Pijul、Mercurial通过 hook 接入。
### 快速恢复路径
`getOrCreateWorktree()` 有一个关键优化:如果目标路径已存在,直接读取 `.git` 指针文件获取 HEAD SHA纯文件 I/O无子进程跳过整个 `fetch` + `worktree add` 流程。在大仓库中 `fetch` 需要 6-8 秒,这个优化将恢复场景的延迟降到接近 0。
## 退出流程ExitWorktreeTool
`ExitWorktreeTool``src/tools/ExitWorktreeTool/ExitWorktreeTool.ts`)支持两种退出策略:
### keep保留 worktree
```
keepWorktree()
1. chdir 回 originalCwd
2. 清空 currentWorktreeSession
3. 更新项目配置activeWorktreeSession = undefined
4. worktree 目录和分支保留在磁盘上
```
用户可以通过 `cd <worktreePath>` 继续工作,或稍后手动合并。
### remove删除 worktree
有严格的**安全防护**
```
validateInput() — 第一道防线
1. 检查是否在 EnterWorktree 创建的会话中
(手动创建的 worktree 不会被删除)
2. countWorktreeChanges(worktreePath, originalHeadCommit)
├── git status --porcelain → 统计未提交文件数
├── git rev-list --count <originalHead>..HEAD → 统计新提交数
└── 返回 nullgit 失败时)→ fail-closed拒绝删除
3. 有未提交文件或新提交?
→ 拒绝,要求 discard_changes: true 确认
```
```
call() — 实际执行
1. 重新计数变更validateInput 和 call 之间可能有新修改)
2. 如果有 tmux session → killTmuxSession()
3. cleanupWorktree()
├── hook-based → 执行 WorktreeRemove hook
└── git-based → git worktree remove --force + git branch -D
4. restoreSessionToOriginalCwd()
- setCwd(originalCwd)
- setOriginalCwd(originalCwd)
- 如果 projectRoot 是 worktree 时才恢复(防误触)
- 更新 hooks config snapshot
- 清空系统提示和 memory 缓存
```
### fail-closed 设计
`countWorktreeChanges()` 在以下情况返回 `null`"未知,假设不安全"
- `git status` 或 `git rev-list` 退出非零(锁文件、损坏的索引)
- `originalHeadCommit` 未定义hook-based worktree 没有设置基线 commit
返回 `null` 时,`validateInput` 拒绝删除——宁可让用户手动处理,也不冒险丢失工作。
## 与 Agent 工具的联动
Agent 工具(`AgentTool`)的 `isolation` 参数决定子 Agent 是否在 worktree 中运行。注意 Agent 工具使用**专用的** `createAgentWorktree()``src/utils/worktree.ts`),而非用户会话用的 `createWorktreeForSession()`,两者有关键差异:
| 维度 | `createWorktreeForSession`(用户会话) | `createAgentWorktree`(子 Agent |
|------|---------------------------------------|----------------------------------|
| 调用者 | EnterWorktreeTool | AgentTool |
| Session 管理 | 设置 `currentWorktreeSession` | **不设置** `currentWorktreeSession` |
| 恢复已有 worktree | 直接复用 | 复用并 bump mtime防止被周期性清理误删 |
子 Agent 结束时的处理由 `cleanupWorktreeIfNeeded()` 自动完成——它不走 `ExitWorktreeTool`(因为 Agent worktree 没有会话状态,`ExitWorktreeTool` 的 `validateInput` 会拒绝):
- **有变更** → 保留 worktree返回 `worktreePath` 供主 Agent 后续合并
- **无变更** → 自动删除
- **Hook-based** → 始终保留
## Session 状态持久化
`WorktreeSession` 对象通过 `saveCurrentProjectConfig()` 持久化到磁盘,包含:
```typescript
{
originalCwd: string, // 进入 worktree 前的工作目录
worktreePath: string, // worktree 的绝对路径
worktreeName: string, // slug
worktreeBranch?: string, // 分支名(如 worktree/fix-auth
originalBranch?: string, // 进入前的分支
originalHeadCommit?: string, // 进入前的 HEAD commit用于变更统计
sessionId: string, // 创建此 worktree 的会话 ID
tmuxSessionName?: string, // 关联的 tmux session
hookBased?: boolean, // 是否由 hook 创建
creationDurationMs?: number, // 创建耗时(分析用)
usedSparsePaths?: boolean, // 是否使用了 sparse checkout
}
```
这使得 session 恢复(`--resume`)时能正确还原 worktree 上下文——即使进程重启,`getCurrentWorktreeSession()` 从项目配置中读取状态。
## Sparse Checkout 优化
对于大型 monorepoworktree 支持 `sparsePaths` 配置——只检出特定目录而非整个仓库。这在 210K 文件的仓库中将 worktree 创建时间从数十秒降到几秒。
配置位于 `getInitialSettings().worktree?.sparsePaths`,在 `performPostCreationSetup()` 中应用。