worktree · diff

git:20260922.c2d70a5 to git:20261028.42f4dae

45 added, 52 removed. Audit A to A.

---
name: worktree
- description: 管理 git worktree 的专用技能。/worktree add <分支名> <目录> 从基础分支拉出新 worktree;/worktree remove <分支名> <目录> 删除 worktree(保留分支)。凡用户要求创建、删除、清理、恢复 worktree(工作树),或提到 "worktree add/remove",一律使用本技能的 git worktree 流程,禁止改用内置 EnterWorktree 工具(其 .claude/worktrees/ 约定与本技能的目录命名约定冲突)。
- argument-hint: "<add|remove> <分支名 (feature/0917)> <worktree目录 (../worktree/)>"
- disable-model-invocation: true
+ description: 创建和删除 Git worktree,目录固定在目标项目根的 .worktree/ 下并保留分支层级。add 从本地或远端基线创建新分支;remove 移除工作树并保留分支。用户要求创建、删除 worktree 时使用,不负责恢复已有分支或推送。
+ user-invocable: true
+ argument-hint: "add <from分支> <新分支> | remove <分支>"
---
# Worktree 管理
- 用 `git worktree` 命令管理 worktree:从基础分支拉出新的开发工作区,用完回收。所有操作只涉及 worktree 的挂载与拆除,不动分支、不 push、不 rebase。
-
- > **基础分支**:默认 `dev`。若项目根存在 `.claude/agentforge-conventions.md`,以其 `base-branch` 字段为准;两者都没有时默认 `dev`,并在报告里说明用的是哪个。
-
- ## 调用格式
-
- ```
- /worktree <add|remove> <分支名> <worktree目录>
+ ```text
+ /agentforge:worktree add dev feature/login
+ /agentforge:worktree remove feature/login
```
- | 参数 | 说明 | 示例 |
- |------|------|------|
- | 动作 | `add` 创建,`remove` 删除 | `add` |
- | 分支名 | 目标分支,可含斜杠 | `feature/0917` |
- | 目录 | worktree 存放目录,可为相对路径。开头若带 `@` 是文件引用语法,去掉即可 | `../worktree/` |
-
- ## 铁律
-
- 1. **当前分支必须是基础分支**,add 和 remove 都一样。先执行 `git branch --show-current`,不是基础分支就停下告知用户先切换分支,不要执行任何后续操作。worktree 一律从基础分支拉出,避免 feature 分支上再套 feature 分支。
- 2. remove **只删 worktree 目录,分支一律保留**。用户的工作成果都在分支上,删 worktree 不能波及分支。
- 3. 一切可能丢数据的参数(`--force` 等)必须先向用户确认,用户明确同意才能用。
-
- ## 路径与命名
-
- worktree 落在 `<目录>/<仓库名>-<分支名>`,其中分支名里的 `/` 替换为 `-`:
+ add 接受 from、新分支两个参数;remove 接受一个分支参数,动作不可省略。不接受目录参数,目标固定为 `<项目主工作树根>/.worktree/<分支>/`,如 `.worktree/feature/login/`。
- ```bash
- repo=$(basename "$(git rev-parse --show-toplevel)")
- slug=$(echo "feature/0917" | tr '/' '-')
- # 例:仓库 myrepo + 分支 feature/0917 + 目录 ../worktree/
- # → ../worktree/myrepo-feature-0917
- ```
+ ## 公共规则
- ## add 流程
+ - 从目标项目的 `git worktree list --porcelain` 确定主工作树根;在 linked worktree 中调用也使用该根,不使用技能安装目录。裸仓库不支持。
+ - 校验动作、参数数量及分支名:用 `git check-ref-format "refs/heads/$branch"`,拒绝以 `-` 开头的名称,不展开 checkout 简写。命令参数安全传递,shell 变量始终引用。
+ - 目标必须在根 `.worktree/` 内,目标和父路径不能经过符号链接、嵌入另一工作树或独立仓库。
+ - 使用原生 Git,不要求当前分支为 dev。不切换分支、不自动暂存、提交、stash、推送、删除分支或安装依赖。
+ - 检查失败就停止并报告实际状态;不把查询失败当作不存在,不自动覆盖、解锁、prune 或清理残留。强制删除必须先说明具体数据风险并获得用户对本次丢弃的明确同意。
- 1. 校验当前分支是基础分支(见铁律 1)
- 2. 按上面的命名规则算出目标路径
- 3. 目标路径已存在 → 停下报告给用户(可能之前建过),不要覆盖
- 4. 分支不存在 → `git worktree add <路径> -b <分支名>`:以当前基础分支为起点创建新分支并挂载
- 5. 分支已存在(没挂在任何 worktree 上,比如之前 remove 过)→ `git worktree add <路径> <分支名>`:把旧分支挂回 worktree
- - 若 git 报 `already checked out`:该分支已挂在别的 worktree 上,把那个路径告诉用户即可,不要强行操作
- - 判断分支是否存在:`git show-ref --verify --quiet refs/heads/<分支名>`
- 6. `git worktree list` 确认注册成功
- 7. 报告:worktree 路径、分支名、基于基础分支的 commit 短 hash。提醒用户可另开终端 cd 进去工作,或在该目录下重启 claude
+ ## add
- ## remove 流程
+ 1. **选择来源**:询问 from 使用本地还是远端;对话已明确时不重复询问。默认远端为 origin,没有 origin 时使用唯一远端,存在多个则补问。无远端或无法查询就停止,即使 from 选择本地也要检查远端重名。
+ 2. **锁定基线**:本地解析 `refs/heads/<from>^{commit}`;远端先获取指定分支,再解析远端跟踪引用,记录完整 OID。例如:
+ ```bash
+ git fetch --no-tags "$remote" "+refs/heads/$from:refs/remotes/$remote/$from"
+ ```
+ 来源不存在或获取失败就停止,不改写本地 from,不回退到其他来源或旧缓存。
+ 3. **检查冲突**:本地已有 `refs/heads/<new>`、目标路径已存在或已登记,都停止;已挂载时报告路径。用下列命令查询所选远端的精确引用,不依赖缓存:
+ ```bash
+ git ls-remote --exit-code --heads "$remote" "refs/heads/$new"
+ ```
+ 核对返回 ref;退出码 0 为存在,停止;2 为无匹配;其他为查询失败,停止。此检查不预留远端分支名。
+ 4. **准备目录**:确认 `.worktree/` 下没有已跟踪文件,并被忽略;必要时在根 `.gitignore` 追加 `/.worktree/`,保留原内容。通过路径检查后创建缺失父目录。
+ 5. **创建并验证**:
+ ```bash
+ git -C "$root" worktree add -b "$new" -- "$target" "$base_oid"
+ ```
+ 重新核对注册路径、实际分支、HEAD 等于基线 OID,以及目标被主工作树忽略。报告来源类型/远端、基线短 hash、新分支、完整路径及未提交的 `.gitignore` 改动。
- 1. 校验当前分支是 `dev`(见铁律 1)
- 2. 按命名规则算出目标路径
- 3. `git worktree list` 里找不到该路径 → 报告"不存在或已删除",停
- 4. 检查未提交改动:`git -C <路径> status --short`。非空 → 把改动列给用户看并停下询问,默认不删
- 5. `git worktree remove <路径>`。若 git 因存在改动或锁文件拒绝,如实转述错误原因,问用户是否 `--force`,用户明确同意才加
- 6. 报告:worktree 已删除、分支 `<分支名>` 仍保留,之后想恢复随时再 add
+ ## remove
- ## 禁止
+ remove 不查询远端,不询问 from,不修改忽略规则。
- - 不使用内置 EnterWorktree / ExitWorktree 工具——它们走 `.claude/worktrees/` 的另一套约定,与本技能的 `<目录>/<仓库>-<分支>` 命名不是一回事
- - 不删除分支、不 push、不 pull、不 rebase
+ 1. **核对身份**:按分支计算目标路径,精确匹配 worktree 登记;未登记则报告不存在,不能直接删同名目录。目录缺失、锁定、失效、detached HEAD,或登记分支与实际分支不一致时停止。记录分支提交 OID,并确认目标 HEAD 一致。
+ 2. **检查数据**:
+ ```bash
+ git -C "$target" status --porcelain=v1 -z --untracked-files=all --ignored=matching --ignore-submodules=none
+ ```
+ 按 NUL 分隔解析;任何暂存、未暂存、未跟踪、忽略内容或子模块变化都列出路径和状态并停止,等待用户处理,不输出敏感文件内容。**忽略文件也可能随正常删除丢失,保留分支不能保护这些数据。** 未推送的提交仍由本地分支保留,不要求先合并或推送。
+ 3. **删除**:确认身份和状态未变化,在主工作树根执行:
+ ```bash
+ git -C "$root" worktree remove -- "$target"
+ ```
+ Git 拒绝时报告原因,不自动加 `--force` 或改用 `rm -rf`。
+ 4. **验证**:目录和注册项均已消失,原分支仍存在且 OID 未变。报告移除路径、保留分支及短 hash;不清理空父目录或其他工作树。add 仍拒绝已有分支,不将其描述为恢复命令。