关于 worktree
worktree 是指导 Agent 安全创建和列出 Git worktree 的技能,适用于需要并行处理任务的开发者。它会按当前分支与默认分支选择基线,并将工作树放在仓库外。
主要功能
- 01按规则选择基线分支
- 02在仓库外创建 worktree
- 03检查分支和目标路径冲突
- 04创建前检查数量上限并提示确认
- 05列出已有 worktree
- 06默认使用本地引用且不联网
适用场景
- 01为独立任务开启并行工作目录
- 02从当前功能分支派生工作树
- 03检查仓库已有 worktree 与分支
- 04需要时显式从远程刷新基线
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ shipshitdev/ skills --skill worktree安装后在 Claude Code 中输入/worktree确认已加载。
详细介绍
Worktree
基于正确的基准分支创建 Git worktree,并将其放在仓库之外的目录中,同时进行安全检查,确保主检出目录及其分支保持正确。Worktree 绝不会位于仓库内。此技能只会创建和列出 worktree。移除 worktree 以及清理已合并的 worktree 属于 git-cleanup 的职责(/git-cleanup)——请勿在此处删除。
Worktree 主目录
每个 Agent 都会为其所有账户使用一个共享主目录;手动创建的 worktree 使用用户级主目录。目录结构为 <home>/<owner-repo>/<name>。
| 创建者 | 主目录 |
|---|---|
| Claude Code | $HOME/.claude/worktrees |
| Codex | $HOME/.codex/worktrees |
| Grok | $HOME/.grok/worktrees |
| T3 Code | $HOME/.t3/worktrees |
| 手动 / 未知 | $HOME/worktrees |
- 设置
GIT_WT_HOME时,它会覆盖主目录。 <owner-repo>来自origin远程仓库(owner/repo会变成owner-repo),并转换为小写——不使用检出目录名称,因为不同仓库可能有相同的检出目录名称。仅当没有origin时,才回退为使用检出目录名称。- 如果某个工具已经将会话置于其自有主目录下的 worktree 中
(例如托管的
.../worktrees/检出目录),则保留该位置;绝不迁移会话,也不因其所在位置而阻止操作。 - 可以使用遵循此约定的
git wt <name>别名代替下方的手动命令。它必须解析到相同路径。
授权范围
仅在用户请求和现有授权范围内操作;加载此技能不会授予任何新权限。仅报告的请求必须保持为仅报告,遵守调用方设定的目标、主机、提供方和成本限制;如需扩大范围,先征求同意,并将这些限制转告委派对象。
合约
输入:
- 仓库根目录(必须位于 Git 工作树内)
- 可选的 worktree/分支名称(要创建的新分支)
- 可选的显式基准分支覆盖项(
from <base>/--base <base>) - 可选的
--fetch标志,用于在创建分支前从 origin 刷新基准分支
输出:
- 在
<home>/<owner-repo>/<name>下创建一个新的 worktree 目录,该目录位于仓库之外,并检出到新分支 - 已解析的基准分支及选择原因
- 如果基准分支落后于其远程分支,则发出警告(本地模式不会自动获取)
- 该仓库关联 worktree 的数量,以及与软上限和硬上限的对比
- 可供
cd进入的路径,已准备好用于并行会话
创建/修改:
- 如果
<home>/<owner-repo>目录不存在,则创建该目录 - 创建一个新分支和一个新的 worktree 目录
- 绝不触碰当前工作树中受 Git 跟踪的文件,也绝不切换当前分支
外部副作用:
- 默认无(仅使用本地分支提示,无网络访问)
- 仅在使用
--fetch时:执行一次git fetch origin <base>以刷新基准分支引用
需要确认:
- 如果仓库关联的 worktree 数量已达到软上限(5),则在创建 worktree 前确认
- 如果要复用现有分支而不是创建新分支,则先确认
- 如果 worktree 路径已存在,要覆盖或复用该路径前先确认
委派给:
- 读取同级目录中的
../<name>/SKILL.md并遵循其中说明来运行委派任务; 此处的技能仅由用户调用,因此 Skill 工具无法加载它们。 - 建议使用
git-cleanup来验证提升操作,并清理已合并的 worktree 和分支 - 如果即将在 worktree 中使用的分支可能包含机密,则使用
git-safety
基础分支选择(核心行为)
按以下顺序解析基础分支。仅使用本地分支指针,不自动获取。
- 显式覆盖。 如果用户指定了基础分支(
/worktree fix-auth from develop或--base release/1.4),则原样使用。 - 位于功能分支上 → 从当前分支创建分支。 如果当前分支不是受保护/集成分支, 工作树就从该分支分出。这是默认行为:继续基于正在进行的工作,探索并行分支, 而不影响当前检出。
- 位于受保护分支上或处于分离 HEAD 状态 → 从默认/主干分支创建分支。
如果当前分支是
master或main,或者 HEAD 处于分离状态,则从仓库的默认分支 (自动检测)分出。基于主干的工作流中,绝不使用develop或staging作为长期集成基础。
current branch -> base the worktree forks from
---------------------- ----------------------------
feat/foo (feature) -> feat/foo (continue current work)
bugfix/x (feature) -> bugfix/x (continue current work)
master / main -> default branch (trunk)
detached HEAD -> default branch (trunk)
explicit "from <base>" -> <base> (always wins)
受保护/主干分支集合(第 2 步中绝不作为“功能”基础分支):
master main
阶段 1:验证仓库并解析输入
git rev-parse --is-inside-work-tree # must be true; else STOP
TOPLEVEL="$(git rev-parse --show-toplevel)"
CURRENT="$(git symbolic-ref --quiet --short HEAD || echo DETACHED)"
git worktree list # show what already exists
解析新分支名称:
- 如果用户提供了名称,则将其用作新分支名称和目录名称。
- 如果用户未提供名称,则询问名称。不要擅自编造临时名称。
- 根据分支名称清理目录名称(例如,分支
feat/foo会对应目录<home>/<owner-repo>/feat-foo,而分支仍为feat/foo)。
阶段 2:解析基础分支
PROTECTED='master|main'
# Default/trunk branch fallback — resolved to a ref that actually exists locally.
# Auto-detect the repo's default branch; never hardcode develop or staging.
DEF="$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##')"
DEF="${DEF:-$(git symbolic-ref --quiet --short HEAD 2>/dev/null)}"
DEF="${DEF:-master}"
git show-ref --verify --quiet "refs/heads/$DEF" && FALLBACK="$DEF" || FALLBACK="origin/$DEF"
# Apply the precedence. BASE is the start-point ref passed to `git worktree add`.
if [ -n "$EXPLICIT_BASE" ]; then
BASE="$EXPLICIT_BASE"
elif printf '%s\n' "$CURRENT" | grep -qxE "$PROTECTED" || [ "$CURRENT" = DETACHED ]; then
BASE="$FALLBACK" # on trunk or detached HEAD -> default/trunk branch
else
BASE="$CURRENT" # on a feature branch -> continue current work
fi
echo "Base: $BASE (current: $CURRENT)"
BASE 始终是一个具体的起点(例如本地分支 master、远程跟踪引用 origin/main,或用户显式指定的基础分支)。创建任何内容之前,先报告解析出的基础分支及原因。
阶段 3:检查新鲜度(发出警告,不要获取)
默认使用本地模式:从 $BASE 的本地分支指针创建分支。如果基础分支有上游,且落后于上游,则发出警告,不要悄悄获取。
if git rev-parse --verify --quiet "$BASE@{upstream}" >/dev/null; then
BEHIND="$(git rev-list --count "$BASE..$BASE@{upstream}" 2>/dev/null || echo 0)"
[ "$BEHIND" -gt 0 ] && echo "WARNING: local $BASE is $BEHIND commit(s) behind its remote. Using local tip. Pass --fetch to update first."
fi
仅当用户传入 --fetch 时:
git fetch origin "$BASE"
git update-ref "refs/heads/$BASE" "origin/$BASE" # only when safe / fast-forward
默认不要获取。如果基础分支有不在远程上的本地提交,不要重写该分支——发出警告并使用本地分支指针。
阶段 4:解析工作树主目录并检查上限
工作树目录位于仓库之外,因此无需忽略或提交任何内容。解析主目录和目标路径,然后统计链接的工作树数量。
# Home: GIT_WT_HOME wins; else the running agent's shared home; else ~/worktrees.
# AGENT is the running agent: claude, codex, grok or t3 (empty for a manual run).
case "${AGENT:-}" in
claude) HOME_DIR="$HOME/.claude/worktrees" ;;
codex) HOME_DIR="$HOME/.codex/worktrees" ;;
grok) HOME_DIR="$HOME/.grok/worktrees" ;;
t3) HOME_DIR="$HOME/.t3/worktrees" ;;
*) HOME_DIR="$HOME/worktrees" ;;
esac
HOME_DIR="${GIT_WT_HOME:-$HOME_DIR}"
# owner-repo from the origin remote, lowercased.
URL="$(git -C "$TOPLEVEL" remote get-url origin 2>/dev/null | sed -E 's#/+$##; s#\.git$##')"
SLUG="$(printf '%s' "$URL" | sed -nE 's#.*[/:]([^/:]+)/([^/:]+)$#\1-\2#p')"
SLUG="$(printf '%s' "${SLUG:-$(basename "$TOPLEVEL")}" | tr '[:upper:]' '[:lower:]')"
# Guard: the destination must be outside the repository.
case "$HOME_DIR/" in "$TOPLEVEL"/*) echo "Worktree home is inside the repository: $HOME_DIR"; exit 1 ;; esac
# Caps: soft 5 (warn and confirm), hard 25 (stop). The primary checkout is not linked.
LINKED="$(( $(git worktree list --porcelain | grep -c '^worktree ') - 1 ))"
echo "Linked worktrees: $LINKED"
[ "$LINKED" -ge 25 ] && { echo "Hard cap (25) reached. Run /git-cleanup before creating another."; exit 1; }
[ "$LINKED" -ge 5 ] && echo "WARNING: soft cap (5) reached. Confirm, or remove merged worktrees with /git-cleanup first."
规则:
- 每项任务使用一个会话。PR 合并后移除工作树(
/git-cleanup),并保留任何未提交的工作。 - 旧版仓库内的
.worktrees/目录可能仍然存在;如果仓库已将其加入 Git 忽略,则继续忽略。不要创建新的此类目录,也不要在新项目中将.worktrees/添加到.gitignore。
阶段 5:创建工作树
NAME="<sanitized-name>"
BRANCH="<new-branch-name>"
DEST="$HOME_DIR/$SLUG/$NAME"
# Guard: destination must not already exist
[ -e "$DEST" ] && { echo "Path exists: $DEST — choose another name or remove it first."; exit 1; }
mkdir -p "$(dirname "$DEST")"
if git show-ref --verify --quiet "refs/heads/$BRANCH"; then
# Branch already exists: confirm, then attach it (no -b, no new branch)
git worktree add "$DEST" "$BRANCH"
else
# New branch off the resolved local base tip
git worktree add -b "$BRANCH" "$DEST" "$BASE"
fi
git worktree list
规则:
- 仅在创建新分支时使用
-b。如果分支已存在,则关联该分支并说明情况——绝不要悄悄重置已有分支。 - 一个分支一次只能在一个工作树中检出。如果
$BRANCH已在其他位置检出,则报告其位置并停止。 - 绝不要传入
--force。如果 Git 拒绝操作,说明原因并让用户决定。
阶段 6:报告并交接
报告:
- 工作树路径:
<home>/<owner-repo>/<name>(位于仓库之外) - 新分支及其分出的基础分支(并说明选择该基础分支的原因)
- 阶段 3 中的任何新鲜度警告
- 如何开始在该目录中工作:使用
cd进入该路径,并在该目录中打开并行会话。每个工作树都是独立的检出,共享同一个.git。 - 如果项目需要依赖项,请先在工作树中安装,再运行任何内容(工作树之间不共享
node_modules)。
模式
worktree <name>— 在新分支<name>上创建名为<name>的工作树,基准分支根据上文的智能规则确定。(默认。)worktree <name> from <base>/worktree <name> --base <base>— 强制指定基准分支。worktree <name> --fetch— 创建分支前,从 origin 刷新基准分支。worktree list— 列出现有工作树及其分支;不创建任何内容。
当用户指定了不同的范围(例如“基于 master”“使用我当前的分支”)时,应遵从该范围,但保留安全检查,确保工作树位于仓库之外,并防止覆盖现有分支或路径。
安全模型
- 绝不切换当前分支,也不修改当前工作树中受跟踪的文件。工作树只会新增。
- 绝不在仓库内创建工作树,因此工作树绝不会被跟踪;创建前会验证目标位置位于仓库之外。
- 默认使用本地分支头;只有明确指定
--fetch时才访问网络。 - 绝不使用
--force,绝不重置现有分支,也绝不覆盖现有路径。发生冲突时,报告问题并停止。 - 删除和清理不在处理范围内——交由
git-cleanup(/git-cleanup)处理。