首页
/ RTK 中 `/tech:remove-worktree` 命令解析:Git Worktree 的安全移除与分支清理

RTK 中 `/tech:remove-worktree` 命令解析:Git Worktree 的安全移除与分支清理

2026-09-05 21:56:58作者:裘旻烁

本文以 RTK(Rust Token Killer)仓库自带的 Claude Code 自定义命令 remove-worktree 为主体,完整讲解该命令的用法、脚本实现、逐段工作原理与安全机制。读完后你可以:独立安全地移除指定分支的 worktree(目录 + git 引用 + 本地/远程分支),理解脚本中每道防护(保护分支、未合并确认、主仓库保护、强删兜底)对应的底层 git 语义,并掌握脚本失败时的手动恢复手段。

一、背景:RTK 的 worktree 工作流,remove 是收尾环节

RTK 仓库为并行开发提供了一套基于 git worktree 的 Claude Code 命令族(位于 .claude/commands/tech/),覆盖 worktree 的完整生命周期:

命令 文档 职责
/tech:worktree worktree.md 创建隔离 worktree,分支名 feature/new-filter 映射到目录 .worktrees/feature-new-filter,并在后台运行 cargo check
/tech:worktree-status worktree-status.md 查询后台 cargo check 结果(读 /tmp/worktree-cargo-check-*.log
/tech:remove-worktree remove-worktree.md 本文主题:移除单个指定 worktree 及其分支
/tech:clean-worktree clean-worktree.md 交互式批量清理已合并 worktree
/tech:clean-worktrees clean-worktrees.md 自动批量清理(仅已合并,无交互,支持 --dry-run

创建端约定:分支名必须含斜杠(如 fix/session-bug),斜杠在目录名中被替换为连字符,worktree 统一落在仓库根的 .worktrees/ 下;该目录已写入 .gitignore 第 50 行的 .worktrees/,确保 worktree 内容不污染 git 状态。正因为 worktree 是"用完即弃"的轻量工作区,移除端 /tech:remove-worktree 必须足够安全——它要同时清掉三样东西:物理目录、git 的 worktree 元数据、以及(可选的)本地和远程分支。

另外注意一个细节:/tech:worktree 脚本通过 git rev-parse --git-common-dir 定位主仓库根再创建 worktree,这意味着从 worktree 内部执行命令也能正确解析仓库根。remove 脚本中的"主仓库保护"检查正是同一问题的镜像:必须防止把当前主仓库目录当作普通 worktree 删掉。

二、命令用法与 Frontmatter 约定

文档定义在 remove-worktree.md,其 frontmatter 遵循 Claude Code 自定义命令规范:

---
model: haiku
description: Remove a specific worktree (directory + git reference + branch)
argument-hint: "<branch-name>"
---
  • model: haiku:指定执行该命令的模型档位;
  • argument-hint:向用户提示命令接受一个参数——分支名。

基本用法就是传分支名:

/tech:remove-worktree feature/new-filter
/tech:remove-worktree fix/session-bug

命令的实际逻辑由文档内嵌的 bash 脚本承载(Agent 从 $ARGUMENTS 取分支名执行)。下面先给出完整脚本,再逐段解析。

三、完整实现脚本

#!/bin/bash
set -euo pipefail

BRANCH_NAME="$ARGUMENTS"

if [ -z "$BRANCH_NAME" ]; then
  echo "❌ Usage: /tech:remove-worktree <branch-name>"
  echo ""
  echo "Example:"
  echo "  /tech:remove-worktree feature/new-filter"
  exit 1
fi

echo "🔍 Checking worktree: $BRANCH_NAME"
echo ""

# 检查 worktree 是否存在于 git
if ! git worktree list | grep -q "$BRANCH_NAME"; then
  echo "❌ Worktree not found: $BRANCH_NAME"
  echo ""
  echo "Available worktrees:"
  git worktree list
  exit 1
fi

# 从 git 中获取 worktree 路径
WORKTREE_FULL_PATH=$(git worktree list | grep "$BRANCH_NAME" | awk '{print $1}')

# 安全检查:绝不删除主仓库
if [ "$WORKTREE_FULL_PATH" = "$(pwd)" ]; then
  echo "❌ Cannot remove main repository worktree"
  exit 1
fi

# 安全检查:绝不删除 master 或 main
if [ "$BRANCH_NAME" = "master" ] || [ "$BRANCH_NAME" = "main" ]; then
  echo "❌ Cannot remove $BRANCH_NAME (protected branch)"
  exit 1
fi

echo "📂 Worktree path: $WORKTREE_FULL_PATH"
echo "🌿 Branch: $BRANCH_NAME"
echo ""

# 检查分支是否已合并
IS_MERGED=false
if git branch --merged master | grep -q "^[* ] ${BRANCH_NAME}$"; then
  IS_MERGED=true
  echo "✅ Branch is merged into master (safe to delete)"
else
  echo "⚠️  Branch is NOT merged into master"
fi
echo ""

# 未合并时要求确认
if [ "$IS_MERGED" = false ]; then
  echo "⚠️  This will DELETE unmerged work. Continue? [y/N]"
  read -r confirm
  if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
    echo "Aborted."
    exit 0
  fi
fi

# 移除 worktree
echo "🗑️  Removing worktree..."
if git worktree remove "$WORKTREE_FULL_PATH" 2>/dev/null; then
  echo "✅ Worktree removed: $WORKTREE_FULL_PATH"
else
  echo "⚠️  Git remove failed, forcing removal..."
  rm -rf "$WORKTREE_FULL_PATH"
  git worktree prune
  echo "✅ Worktree forcefully removed"
fi

# 删除本地分支
echo ""
echo "🌿 Deleting branch..."
if [ "$IS_MERGED" = true ]; then
  if git branch -d "$BRANCH_NAME" 2>/dev/null; then
    echo "✅ Branch deleted (local): $BRANCH_NAME"
  else
    echo "⚠️  Local branch already deleted or not found"
  fi
else
  if git branch -D "$BRANCH_NAME" 2>/dev/null; then
    echo "✅ Branch force-deleted (local): $BRANCH_NAME"
  else
    echo "⚠️  Local branch already deleted or not found"
  fi
fi

# 删除远程分支(如存在)
echo ""
echo "🌐 Checking remote branch..."
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
  echo "⚠️  Remote branch exists. Delete it? [y/N]"
  read -r confirm_remote
  if [ "$confirm_remote" = "y" ] || [ "$confirm_remote" = "Y" ]; then
    if git push origin --delete "$BRANCH_NAME" --no-verify 2>/dev/null; then
      echo "✅ Remote branch deleted: $BRANCH_NAME"
    else
      echo "❌ Failed to delete remote branch (may require permissions)"
    fi
  else
    echo "⏭️  Skipped remote branch deletion"
  fi
else
  echo "ℹ️  No remote branch found"
fi

echo ""
echo "✅ Cleanup complete!"
echo ""
echo "📊 Remaining worktrees:"
git worktree list

四、逐段原理:每一步对应的 git 机制

4.1 定位目标:git worktree list 的解析

git worktree list 每行格式大致为 <路径> <commit> [<分支名>](主仓库行不带方括号)。脚本先 grep -q "$BRANCH_NAME" 判断存在性,再用 awk '{print $1}' 取第一列得到 worktree 的物理路径。找不到时打印全量列表并以退出码 1 终止——这是典型的 fail-fast 设计,避免后续步骤在一个不存在的目标上空转。

4.2 三道安全检查

  1. 主仓库保护git worktree list 的第一行就是主仓库本身。若解析出的路径恰好等于 $(pwd),脚本拒绝执行。防止"误把主 checkout 当普通 worktree 删掉"。
  2. 保护分支master / main 直接拒删,属于硬编码的白名单黑名单。
  3. 未合并确认git branch --merged master 列出所有已合并进 master 的本地分支,配合 grep -q "^[* ] ${BRANCH_NAME}$" 精确匹配整行(* 表示当前分支、空格表示普通分支),避免 feature/fix 误匹配 feature/fix-v2 这类前缀陷阱。未合并分支必须先获得 y/Y 交互确认,否则脚本以退出码 0 优雅退出("Aborted.")。

适用前提提示:脚本以 master 作为合并基准分支硬编码。从仓库当前状态看(主分支为 develop,如 git worktree list 所示),在 RTK 这类以其他分支为默认分支的仓库中,git branch --merged master 的判定可能失真(倾向判定为"未合并"),此时脚本会退化为"每次都要求确认"的保守行为——更安全但稍显啰嗦。复用此脚本到其他仓库时,应把 master 替换为该仓库的实际默认分支。

4.3 移除 worktree:优雅删除 + 强删兜底

git worktree remove "$WORKTREE_FULL_PATH"

git worktree remove 一步完成两件事:删除工作目录、清理主仓库 .git/worktrees/<name>/ 下的元数据(git 为每个附加 worktree 存放的 HEAD、refs 等元信息)。它对"有未提交修改"的 worktree 默认拒绝删除,需要 --force 才能强删——这正是兜底分支存在的原因:

rm -rf "$WORKTREE_FULL_PATH"   # 直接删目录
git worktree prune             # 回收指向已消失目录的元数据引用

git worktree prune 会扫描 git worktree list 中登记但目录已不存在(或被手工 rm 掉)的条目并清除。这条兜底路径也解释了为什么 prune 在批量清理命令 clean-worktrees.md 里被放在第 1 步:它先回收"目录被外部手段(如 rm -rf)删掉后残留的孤儿引用",再处理合并过的 worktree。

需要留意一个边界情形:若 worktree 存在未提交的本地修改git worktree remove 会失败并触发强删兜底,但这些未提交内容会随之丢失——这正是前面"未合并需确认"提示 This will DELETE unmerged work 的语义所在。若你确定工作已推送或合并,可放心强删。

4.4 分支删除:-d-D 的分岔

  • 已合并:git branch -d(小写)——git 自身会校验该分支确实已合入上游,未合入则拒绝,形成第二道保险;
  • 未合并(用户已确认):git branch -D(大写)——强制删除。

两条路径在分支不存在时都会静默失败(2>/dev/null),脚本打印 "already deleted or not found" 而不中断,使整个流程幂等——对同一分支重复执行命令不会产生错误。

4.5 远程分支:探测 + 可选删除

git ls-remote --heads origin "$BRANCH_NAME"
git push origin --delete "$BRANCH_NAME" --no-verify

git ls-remote --heads 只查询远端引用而不拉取对象,开销小,适合"先探测后询问"的交互模式。远程删除同样要求用户二次确认(默认 N),且使用 --no-verify 跳过 push 钩子——分支删除操作通常不需要也不应触发本地的提交/推送校验脚本。失败时(如无权限)脚本只报错不中断,本地清理结果不受影响。

最后打印 git worktree list 展示剩余 worktree,给用户一个可核对的收尾视图。

五、安全特性小结

原文档 remove-worktree.md 列出的安全特性可汇总为:

  • 永不删除 master/main(保护分支黑名单);
  • 未合并分支必须交互确认,已合并分支走 git branch -d 安全删除;
  • 主仓库保护:路径等于当前工作目录时拒绝执行;
  • 一次清理三处状态:目录、git worktree 引用、分支(本地默认 + 远程可选);
  • git 删除失败时兜底rm -rf + git worktree prune 保证不会留下"目录没了、引用还在"的孤儿状态。

六、手动覆盖与故障恢复

当脚本不可用或需要精确控制时,等价的手动序列为(文档 "Manual Override" 一节):

git worktree remove --force <path>          # 强制移除 worktree(--force 忽略未提交修改)
git branch -D <branch>                     # 强制删除本地分支
git push origin --delete <branch> --no-verify  # 删除远程分支

对应到 /tech:worktree 约定的命名规则(分支 feature/new-filter → 目录 .worktrees/feature-new-filter),一次完整的手动清理是:

git worktree remove --force .worktrees/feature-new-filter
git worktree prune
git branch -D feature/new-filter
git push origin --delete feature/new-filter --no-verify

创建端文档 worktree.md 的 Cleanup 一节也给出了非强制版本:

git worktree remove .worktrees/feature-new-filter
git worktree prune

七、与批量清理命令的分工

场景 推荐命令 特点
结束单个任务、移除指定分支的 worktree /tech:remove-worktree <branch> 精确到单个分支;未合并需确认;可选删远程分支
合并 PR 后批量回收 /tech:clean-worktrees [--dry-run] 全自动,只碰已合并分支,远程分支仅报告不删
交互式盘点 + 清理 /tech:clean-worktree 先展示状态,人工确认后删除

可以推断出这套命令族的设计意图:remove-worktree 负责"点"(单个任务收尾,含远程分支决策),clean-worktrees 负责"面"(定期维护,无交互),两者共享同一套安全基线——保护 master/main、保护主仓库、只强删已确认目标、rm -rf + prune 兜底。

八、复用建议

这套脚本不依赖 RTK 本身,任何使用 git worktree 的仓库都可以直接借用,只需调整三处:

  1. git branch --merged master 中的 master 改为仓库实际默认分支(可先用 git symbolic-ref refs/remotes/origin/HEAD 查询);
  2. worktree 目录约定(脚本按"分支名出现在 git worktree list 中"来匹配,与 .worktrees/ 约定无强耦合);
  3. 若仓库无 origin 远程(纯本地仓库),可省略 4.5 节的远程探测段,git ls-remote 会失败但 2>/dev/null 已使其无害。

配合创建端 worktree.md(含 --fast/--no-check 参数与后台 cargo check)、状态端 worktree-status.md,本文的移除端构成了一个完整的"创建 → 验证 → 移除"闭环,可作为 AI 辅助并行开发工作流的参考实现。

登录后查看全文
热门项目推荐
相关项目推荐