mcpField
W
worktree

worktree

github.com/davidlee开发2141 次安装4 天前更新
代码仓库

关于 worktree

用于管理隔离 Git worktree 创建与交接的技能,通过 doctrine 命令创建、配置并验证工作区。支持 solo、worker 和独立的 coordination worktree 流程,各自具有不同的隔离要求。

主要功能

  1. 01检测并采用已有的 linked worktree
  2. 02使用 doctrine worktree fork 创建并配置分支工作区
  3. 03支持 solo、worker 和 coordination 流程
  4. 04派生工作区前检查源目录是否干净
  5. 05交接前验证项目基线通过
  6. 06配置时仅复制允许列表中的忽略文件

适用场景

  1. 01为 solo 工作建立隔离工作区
  2. 02从明确的基准提交准备 worker 分支
  3. 03创建或恢复 dispatch coordination worktree
  4. 04交给 Agent 前验证工作区基线

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/davidlee/doctrine --skill worktree

安装后在 Claude Code 中输入/worktree确认已加载。

附带文件NOTICE.md

详细介绍

Worktree

管理隔离式 git worktree 的生命周期:检测现有隔离状态,通过 doctrine worktree fork 创建分支(该命令会添加 worktree、为其配置环境,并在可行时将其绑定到对应阶段;所有操作均支持补偿回滚;fork 会在自身树内构建到 target/),运行生成前检查,并确认基线正常后再移交。

开始时宣布:“正在使用 worktree 技能设置隔离工作区。”

这是一个子技能。 /execute(mode=solo)会在选择启用隔离时调用它;dispatch 生成脚本会为每个 worker 调用 fork,不受执行环境影响;/dispatch 路由器会针对每次运行的 dispatch/<slice> 树调用协调路径(见下文的 worktree coordinate)。它不是 /route 的目标——请通过调用方进入。

模式约定

此技能采用参数化设计,使 funnel 能复用它,而不继承 solo 语义(分离边界)。

输入:

  • mode = solo | worker
  • allow_work_in_place: bool — 仅 solo 时为 true
  • 请求的分支 / 路径
  • base — 创建 fork 所基于的引用。solo 默认使用 HEAD; worker 必须显式指定 base(编排器的协调 HEAD B), 参见 lib:reference/dispatch-mechanics.md § Fork 的 base 必须显式指定,绝不使用会话 HEAD。

行为:

  • solo — 在沙箱拒绝时,可以降级到原地工作路径(不创建 fork;这是认可的主干路径)。
  • worker — 绝不能降级。 worker 没有真实 fork 即视为硬性失败;funnel 必须保持隔离。fork --worker --base <B> 会显式固定 base——绝不使用隐式会话 HEAD,因为 worker 的会话 HEAD 不是 B,会破坏 S.parent == B。创建完成后,worker 部分会运行受限的编辑→验证→将单个 S 提交到 fork循环;参见Worker 模式。

输出: { fork_path, branch, head_sha, provision_report, baseline_result }。 worker 在提交其唯一的差异 S 后会增加 { fork_branch, head_sha_after }——编排器会从中导入 B..head_sha_after。

协调模式 — doctrine worktree coordinate(第三条路径)

上文的 solo / worker 都通过 fork 处理。dispatch 协调 worktree 使用单独的创建命令,不是 fork 的一种模式——/dispatch 路由器会在批次 1 之前,每次运行调用一次:

doctrine worktree coordinate --slice <N> --dir <path>

它会在已解析的主干上,于独立 worktree 中创建(或恢复)dispatch/<slice>——这是 funnel 唯一的写入目标。它与 fork 的关键区别如下:

  • 编排器自己的树。 协调树就是编排器(worker 模式关闭——worker 身份属于进程,由 worker 隔离环境中的 DOCTRINE_WORKER 标识,而不是由目录决定)。 它属于编排器类别,与该类别的其他命令一样,worker 模式下会拒绝执行。
  • 创建或恢复,不创建第二个分支。 如果已有活跃 worktree 使用 dispatch/<slice>,则拒绝(coordination-live——同一 slice 的并发 dispatch 不合法)。如果分支存在但没有活跃 worktree,则会恢复(重新关联)——这样,/handover 后启动的新编排器就能使用同一分支(恢复稳定;若使用每次运行的标识符,会破坏恢复)。
  • 从已提交的 plan.toml 重新生成运行时阶段表(配置轴),并通过唯一的复制器完成——不执行协调层级的复制。

这不是 worker 差异路径:没有 S,也没有导入。生命周期关系是 worktree-life < branch-life——结束时会移除目录,但会保留 dispatch/<slice> 以及投影后的 review / phase 引用,作为交付物(参见 /dispatch)。下文的 solo / worker 创建方式保持不变。

检测(适配,不要重新创建)

如果当前工作目录(CWD)已位于关联工作树中,则适配该工作树;不要再创建一个。

git rev-parse --git-dir          # differs from common-dir ⇒ linked worktree
git rev-parse --git-common-dir

--git-dir ≠ --git-common-dir ⇒ 已位于关联工作树中 → 跳过创建。

子模块防护。 子模块也会触发此不等式。请区分:

  • .git gitdir 的解析路径位于 …/worktrees/ 下 ⇒ 工作树;位于 …/modules/ 下 ⇒ 子模块;或者
  • git rev-parse --show-superproject-working-tree 返回非空值 ⇒ 子模块。

子模块不是我们需要的隔离方式——将其视为“尚未分叉”。

创建 — doctrine worktree fork(受控路径)

选择分叉目录,运行提交前置检查,然后调用该动词:

doctrine worktree fork --base <B> --branch <name> --dir <path> [--worker]

fork 是唯一的创建 + 配置 +(标记)+ 输出动词,通过补偿回滚实现原子性(git worktree add 后的任何失败都会触发尽力执行的 worktree remove --force / branch -D / 目录清理;如果回滚本身失败,会指出残留项并以非零状态退出——绝不会悄悄留下半回滚状态)。它会:

  1. git worktree add -b <branch> <dir> <base> — 如果 <dir>/<branch> 已存在,或 <base> 不是提交,则拒绝执行。请显式传入 <base>:独立运行时用 HEAD, 工作进程用 B——显式指定基准正是此路径受控的原因。
  2. 配置分叉(唯一的复制器——在复制边界处排除协调/运行时层;参见配置)。
  3. 使用 --worker 时,必须同时提供 --slice 和 --phase,并使用 <coord>/.worktrees/<name> 目录,才能将分叉绑定到其 (slice, phase)。 缺少其中任何一项,分叉都只是未绑定;下游动词会如实报告,而不会猜测处理(独立运行时省略 --worker)。
  4. 将面向用户的状态信息输出到 stderr;stdout 保持为空(便于机器处理)。分叉会在自身工作树中的 <dir>/target 目录下构建——不会输出环境契约:平台已不再负责构建环境。

fork 属于Orchestrator 类操作——工作进程模式下拒绝执行(工作进程不会分叉;由协调器/独立运行模式在工作进程模式关闭时调用)。

创建后端梯队——按顺序降级:

  1. 已有隔离环境(见上文检测)→ 跳过创建,采用该分叉。
  2. 如果存在且可调用,则使用框架原生创建功能,但仅限创建(不自动复制)——仅作为可选方案。(Claude Code 的 WorktreeCreate hook 就是一个实例,但其载荷缺少基准/路径/类型,因此 doctrine 无法驱动它,dispatch 也不会这样做:而是显式创建分叉。)围绕它进行设计; 绝不要依赖它。
  3. doctrine worktree fork — 经过测试并获认可的默认方案。优先使用。
  4. 沙箱拒绝时,原地工作(仅限 solo,allow_work_in_place=true)— 不创建分叉,直接使用主干路径。worker 则中止。
工作树目录选择

在调用 fork 前,按优先级选择分叉目录 (superpowers:using-git-worktrees):

  1. 重用现有目录 — 如果 .worktrees 和 worktrees 都存在,优先使用 .worktrees。
  2. 检查 CLAUDE.md 中是否注明偏好——如有,直接使用,无需询问。
  3. 如果目录不存在且未注明偏好,询问用户。

分叉路径为 <dir>/<branch>。**安全要求:**创建前,目录 MUST 被 git 忽略 (git check-ignore -q .worktrees),否则分叉内容会被跟踪——如果未被忽略,请将该行添加到 .gitignore 并先提交。(位于项目目录之外的全局目录无需此项检查。)

配置

fork 会自动配置;绝不要再运行第二个复制器。 doctrine worktree provision <fork> 是独立的唯一复制器(从源根目录运行,绝不要在分叉内运行),用于少见的跳过了 fork 的采用已有工作树路径。它会读取 .worktreeinclude,遇到命名层级的模式时默认拒绝,并且只复制允许列表中的、被 git 忽略的文件——即使模式宽泛地使用 **,也会在复制边界处排除协调/运行时层。

诚实地说明不变量(不要夸大)

副本隔离的保证之所以成立,是因为 provision 是唯一的复制者。

  • doctrine worktree check-allowlist 是一项静态异味检查——检查通过表示没有任何模式名称涉及该层级;这不代表内容完整。真正的保证来自 select_copies(位于 provision 中)。
  • 如果某个 harness 在创建时强制复制,且无法仅运行创建流程,doctrine 就无法阻止该复制——保证会退化为仅依赖 check-allowlist,项目必须确保 .worktreeinclude 精确无误。因此,默认使用完全可控的 fork 动词,且绝不依赖 harness 原生层级。

防护措施

在生成工作树前后运行这些检查。检查失败时立即中止——报告问题,不要自行变通。

  • 提交后再生成。 执行 fork 前,源树必须干净,以确保分叉只获取已提交的 HEAD:git status --porcelain -z。如果存在任何已跟踪的脏文件或任何未被忽略的未跟踪文件(它们会悄无声息地缺失于分叉中),则中止。被忽略的文件没问题——provision 会处理允许列表中的子集。
  • 分支起点检查。 fork 按设计会将工作树置于 <base>;并发扩展的边界是批量提交:当协调 HEAD 仍等于编排器生成工作树前的 B 时,doctrine worktree branch-point-check --base <B> 退出码为 0;否则为 1(→ 重新分派)。这是编排器在导入时执行的防护检查,worker 模式为 OFF——不是 worker 执行的检查。这是 HEAD 静止状态比较,不是 merge-base。
  • 基线验证。 创建后,在分叉内运行项目的重新生成与验证命令,并要求检查通过后才能交接(本仓库使用:doctrine check gate)。该命令由项目提供——绝不能硬编码为 cargo …。无法构建的分叉必须在 provision 阶段修复,绝不能交接。

Worker 模式(漏斗的另一半)

mode=worker 是分派 worker 在其 fork --worker 工作树中运行的路径。创建、provision 和上述防护措施先照常执行;基线检查通过后,worker 会沿此路径运行,以取代单独交接。Worker 是受约束的写入者:只返回一个可导入的增量,绝不触碰协调/运行时层(fork 已将其隔离)。

lib:reference/dispatch-mechanics.md § Worker 身份是进程的一项属性,由生成过程设定。

lib:reference/dispatch-mechanics.md §§ “Worker 返回的是工作树,而不是提交” 和 “Workers can silently discard their own work”。

绝不能退化为原地工作。 没有真实 fork 的 worker 必须立即中止,绝不能悄悄在树内编辑(与 solo 第 4 级相反)。如果创建失败,报告问题并停止。

返回一份结构化报告(保存在编排器上下文中,绝不是 doctrine 产物):变更内容、验证结果、值得记入记忆的要点,以及 fork 分支。知识记录应跟随编排器确认的提交,而不是 fork(见下文“记录到 trunk”)。

Squash 后成为孤儿的注意事项(记录到 trunk)

在工作树分支中记录的记忆会在 squash-merge 后成为孤儿(内容仍然存在,但 Git 锚点指向一个从未进入目标分支的提交;因此会触发过时判定)。如果持久记忆必须在 fork 结束后继续保留,应将其记录到 trunk,而不是工作树分支中。

.worktreeinclude 模板(由项目所有,不随安装提供)

Doctrine 没有密钥或不可或缺的本地文件 → 它自己的默认设置是不复制任何内容,安装程序也不会附带 .worktreeinclude(安装根目录文件会覆盖使用该项目的文件)。Provision 能容忍文件缺失(不复制任何内容,退出码为 0)。

需要将被 Git 忽略的本地文件带入分叉的项目,可以在仓库根目录添加 .worktreeinclude:空行、# 注释、仓库相对路径字面值、简单的 glob 模式(*、**、?)。不支持 ! 否定规则,也不支持锚定(解析器会拒绝)。无论如何都会排除协调/运行时层。进行静态验证(气味测试,并非完整性检查):doctrine worktree check-allowlist。

快速参考

情况操作
--git-dir ≠ --git-common-dir,且不是子模块已经分叉 → 直接采用,跳过创建
子模块(modules/ gitdir / 超级项目)不具备隔离性 → 按未分叉处理
调度协调树(每次运行)doctrine worktree coordinate --slice <N> --dir <path> — 基于已解析的主干创建或恢复,不是 fork 模式
默认分叉(单人或调度工作进程)doctrine worktree fork --base <B> --branch <name> --dir <path> [--worker]
工作进程分叉基点显式指定 --base <B>,绝不使用隐式会话 HEAD(它不是 B)
工作树目录未被忽略添加到 .gitignore 并提交,然后再执行 fork
沙盒禁止分叉,mode=solo在原地工作(不分叉)
沙盒禁止分叉,mode=worker中止(必须隔离)
采用了跳过 fork 的分叉从源根目录运行 doctrine worktree provision <fork>(唯一的文件复制器)
工作树有未提交更改 / 未跟踪且未被忽略的文件中止,在启动前提交
批量提交边界doctrine worktree branch-point-check --base <B> → 1 = 重新调度
分叉中的基线为红在配置阶段修复;绝不交接
工作进程启动export DOCTRINE_WORKER=1(自行启用,失败时放行)
工作进程验证通过提交一个非合并提交 S;返回 {fork_branch, head_sha_after}
工作进程有多个提交 / 合并提交 / 已变基的分叉违反约定 — 协调器会在导入前拒绝
工作进程验证未通过报告问题;不要提交未通过的差异

风险提示

绝不:

  • 通过 fork / provision 以外的任何方式将被 Git 忽略的文件复制到分叉中(它们是唯一的复制器——排除保证依赖于它们)。
  • 在 fork 后运行第二个复制器,或在分叉内部运行 provision。
  • 暗示 check-allowlist 通过就表示允许列表完整(它只是气味测试)。
  • 让 worker 降级为在原地工作。
  • 在 worker 模式下:写入 .doctrine/ 编写树、跳过 export DOCTRINE_WORKER=1,或提交多个非合并提交 S。
  • 从有未提交更改的工作树创建分叉,或交接基线未通过的分叉。
  • 从隐式会话 HEAD 而非 --base <B> 创建 worker 分叉——对工作进程而言,会话 HEAD 不是 B,这是一个会破坏 S.parent == B 的分歧基点。
  • 编写或编辑此技能的主版本;重新安装时,被 Git 忽略的 .doctrine/skills/ 安装副本会被覆盖。

务必:

  • 创建前先检测;适配已有的隔离环境。
  • 优先使用 fork 动词;将宿主原生层级视为机会性方案。
  • 运行全部三个防护检查;交接前确认基线通过。
  • 在主干上记录持久记忆,而不是在分叉分支中记录。

结果

报告以下输出:fork_path、branch、head_sha、provision 报告(已复制 / 已排除的文件)以及基线结果。分叉分支是交还给调用方的交付物。

评论

支持 Markdown