mcpField
W
worktree

worktree

github.com/shipshitdev开发3859 次安装今天更新
代码仓库

关于 worktree

worktree 是指导 Agent 安全创建和列出 Git worktree 的技能,适用于需要并行处理任务的开发者。它会按当前分支与默认分支选择基线,并将工作树放在仓库外。

主要功能

  1. 01按规则选择基线分支
  2. 02在仓库外创建 worktree
  3. 03检查分支和目标路径冲突
  4. 04创建前检查数量上限并提示确认
  5. 05列出已有 worktree
  6. 06默认使用本地引用且不联网

适用场景

  1. 01为独立任务开启并行工作目录
  2. 02从当前功能分支派生工作树
  3. 03检查仓库已有 worktree 与分支
  4. 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

基础分支选择(核心行为)

按以下顺序解析基础分支。仅使用本地分支指针,不自动获取。

  1. 显式覆盖。 如果用户指定了基础分支(/worktree fix-auth from develop 或 --base release/1.4),则原样使用。
  2. 位于功能分支上 → 从当前分支创建分支。 如果当前分支不是受保护/集成分支, 工作树就从该分支分出。这是默认行为:继续基于正在进行的工作,探索并行分支, 而不影响当前检出。
  3. 位于受保护分支上或处于分离 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”“使用我当前的分支”)时,应遵从该范围,但保留安全检查,确保工作树位于仓库之外,并防止覆盖现有分支或路径。

安全模型

  1. 绝不切换当前分支,也不修改当前工作树中受跟踪的文件。工作树只会新增。
  2. 绝不在仓库内创建工作树,因此工作树绝不会被跟踪;创建前会验证目标位置位于仓库之外。
  3. 默认使用本地分支头;只有明确指定 --fetch 时才访问网络。
  4. 绝不使用 --force,绝不重置现有分支,也绝不覆盖现有路径。发生冲突时,报告问题并停止。
  5. 删除和清理不在处理范围内——交由 git-cleanup(/git-cleanup)处理。

评论

支持 Markdown