Worktrunk `wt remove` 命令全解析:安全删除 Git worktree 与已合并分支
2026/9/16 12:45:50 网站建设 项目流程

Worktrunkwt remove命令全解析:安全删除 Git worktree 与已合并分支

【免费下载链接】worktrunkWorktrunk is a CLI for Git worktree management, designed for parallel AI agent workflows项目地址: https://gitcode.com/GitHub_Trending/wo/worktrunk

wt remove是 Worktrunk 中用于拆除 Git worktree 的命令:它删除 worktree,并在分支已合并时顺带删除分支,默认作用于当前 worktree。在并行 AI Agent 工作流中,每个任务对应一个 worktree,任务结束后用一条命令即可完成“清场”与分支回收。本文基于仓库文档 remove.md,结合命令入口 src/commands/remove.rs、底层删除实现 src/git/remove.rs 与进程收割模块 src/git/reap.rs,完整讲解其用法、六项分支集成判定、后台删除机制、--reap进程收割、JSON 输出与钩子行为,读完即可在真实仓库中安全、自动化地使用wt remove

命令概览:一次完成 worktree 与分支的拆除

wt remove把“删除 worktree”和“删除分支”合并为一步:删除 worktree 后,如果其分支的内容已经全部进入默认分支,分支也会被一并删除。不指定目标时默认删除当前所在 worktree。

删除当前 worktree:

$ wt remove ◎ Running pre-remove project:cleanup flyctl scale count 0 Scaling app to 0 machines ◎ Removing api worktree & branch in background (same commit as main, _) ○ Switched to worktree for main @ ~/repo

示例输出中值得注意的细节:

  • pre-remove钩子在删除前运行(这里是project:cleanup),此时 worktree 文件仍可访问;
  • same commit as main表明分支与默认分支同提交,是六项集成判定中的第一种;
  • in background表明默认采用后台删除,命令立即返回;
  • ○ Switched to worktree for main表示删除当前 worktree 后自动切回主 worktree。

删除指定 worktree / 分支(可一次传多个):

$ wt remove feature-branch $ wt remove old-feature another-branch

保留分支(不删除,无论合并状态如何):

$ wt remove --no-delete-branch feature-branch

强制删除未合并的分支:

$ wt remove -D experimental

从命令入口 src/commands/remove.rs 可以看到,多目标删除会先全部校验、再逐一执行,并按“其他 worktree → 纯分支 → 当前 worktree”的顺序执行,同时收集每个目标的错误以支持部分成功;输入中的分支名与路径别名会被去重(validation_deduplicates_branch_and_path_aliases测试覆盖了这一行为)。

目标选择:分支名与 worktree 路径

[BRANCHES]...参数接受分支名或 worktree 路径,解析逻辑由Repository::resolve_worktree承担。有几个边界情况值得注意:

  • detached HEAD worktree 没有分支名,必须传路径:wt remove /path/to/worktree
  • 当分支被多个 worktree 检出时(只有通过git worktree add --force才能形成),按分支名解析会回到 git 列出的第一个 worktree,可能删错对象;因此实现上对解析到 worktree 的目标一律使用规范化后的路径来定位(见 src/commands/remove.rs 中RemoveTarget::WorktreePath分支的注释);
  • 路径下没有注册 worktree 的目录(如被中断创建留下的残骸)会被明确拒绝并给出WorktreeNotFoundAtPath错误;
  • 目录已消失的 worktree 会在准备阶段降级为“仅删除分支”(BranchOnly)处理,测试validation_buckets_missing_worktree_by_returned_plan专门验证了这种降级归桶行为。

分支清理:六项“已集成”判定

默认情况下,只有当分支合并进默认分支后不会带来任何新增变更时,分支才会被删除。这同时兼容两类工作流:提交历史未改变的普通合并,以及 squash-merge / rebase 后提交历史不同但文件变更一致的工作流。

Worktrunk 按成本从低到高检查六种条件(判定逻辑集中在 src/git/mod.rs 的IntegrationReasoncheck_integration):

#条件含义wt list符号典型成本
1Same commit分支 HEAD 与默认分支是同一个提交_~1ms
2Ancestor分支在目标的历史中(fast-forward 或 rebase 场景)~1ms
3No added changes三点差异(target...branch)为空~50–100ms
4Trees match分支 tree SHA 等于目标 tree SHA~100–300ms
5Merge adds nothing模拟合并产生与目标相同的 tree,覆盖目标已推进且改动了不同文件的 squash 合并~500ms–2s
6Patch-id match分支的完整 diff 与目标上某个 squash 合并提交匹配;当模拟合并因双方改动了同一批文件而冲突时兜底~1–3s

实现层面,check_integration按优先级短路返回:任一信号确认即停止,避免为已确认集成的分支付出昂贵判定成本。对应的成本注释(~1ms 到 ~1–3s)也在 src/git/mod.rs 中逐条标注,这也解释了“默认分支遍历设有上限”的设计:当 squash 合并发生在合并点之后又积累了数百个提交时,会超出遍历上限,此时必须用-D才能删除。

几点判定边界(文档 remove.md 明确说明):

  • “Same commit”判定使用本地默认分支;其余判定中“目标”指默认分支,或当默认分支严格落后时其上游(如origin/main);
  • 满足上述条件且工作树为空的分支,会在wt list置灰显示,表示可安全删除;
  • 六项判定只回答“删除是否会丢失工作”。还有另一种失败场景:分支被第二个 worktree 检出(只能靠git worktree add --force形成),此时删除引用会导致该 worktree 无法解析HEADgit branch -d也会拒绝同样的删除。这类分支无论是否使用-D都会被保留,并在输出中指名幸存的检出位置(源码对应 src/git/remove.rs 中的fresh_branch_checkoutbranch_checkout_requires_retention,后者对“被锁定但目录缺失”的 worktree 采取 fail-closed 的保守保留策略)。

原子性保障:compare-and-swap 删除

安全删除并非“检查通过就直接删”。在 src/git/remove.rs 的delete_branch_if_safe中,集成检查通过后,删除使用git update-ref -d <ref> <expected-sha>这一比较并交换(CAS)原语:仅当引用当前值仍等于快照 SHA 时才删除。若在检查与删除之间分支被钩子或并发进程推进,命令失败且分支被保留(RetainedRaced),绝不会静默丢弃未合并的提交。单元测试cas_rejects_delete_when_branch_advancescas_deletes_when_branch_unchanged分别验证了竞态拒绝与正常删除两条路径。若分支的 SHA 不在快照中(极端的快照偏移场景),则回退到非 CAS 的branch -D

两个强制标志:分清“worktree 脏”与“分支未合并”

wt remove有两个作用域不同的强制标志:

标志作用域使用场景
--force-fWorktreeworktree 存在未提交变更
--force-delete-DBranch分支存在未合并提交
$ wt remove feature --force # 删除脏 worktree $ wt remove feature -D # 删除未合并分支 $ wt remove feature --force -D # 两者都要
  • --force跳过脏工作树门禁:允许删除包含已暂存、已修改和未跟踪文件的 worktree;不加该标志时,只要存在任何未提交变更,删除就会失败。集成测试覆盖了未跟踪文件、已修改跟踪文件、已暂存文件三种脏状态(test_remove_force_with_untracked_filestest_remove_force_with_modified_filestest_remove_force_with_staged_files)。
  • --force不能绕过git worktree lock,也不能绕过“目录所有权”校验:stage_worktree_removal中锁检查与所有权检查都在force_worktree分支之外无条件执行。集成测试test_remove_refuses_foreign_repository_at_worktree_path验证了即使--force也不能删除路径上被另一个无关仓库占用的目录——--force只是用户放弃自己的未提交变更,绝不是对“谁拥有该目录”的声明。
  • --no-delete-branch或配置[remove] delete-branch = false-D同时出现时,命令直接报错拒绝(见 src/commands/remove.rs 中的冲突校验)。
  • 另外,-D走的是直接git branch -D,此时 git 自带的“已检出分支保护”仍会拒绝删除规划后新检出的分支(测试force_delete_uses_git_checkout_protection)。

后台删除:瞬时改名进 trash,命令立即返回

默认情况下删除在后台运行——命令立即返回,不阻塞终端。完整流程如下(对应 src/git/remove.rs 的stage_worktree_removal与模块级文档):

  1. 锁检查与脏工作树门禁:锁检查读取locked文件(而非缓存的list_worktrees()),确保能感知批准提示与pre-remove钩子之间发生的锁变更;--force跳过脏检查;
  2. 停止 fsmonitor daemon(尽力而为):发送git fsmonitor--daemon stop的优雅 IPC 请求(2 秒超时),若 daemon 卡死,Unix 下再通过lsof按 socket 解析 PID 并 SIGTERM→SIGKILL 强制终止——否则 daemon 会在 worktree 消失后永久泄漏;
  3. 改名进 trash:worktree 目录被改名到.git/wt/trash/<name>-<timestamp>/,同文件系统改名是瞬时元数据操作,用户工作区立刻清空;git 元数据随即被剪除(prune_worktree_entry,只针对该 worktree 条目,不影响目录暂时缺失的兄弟 worktree);
  4. 分支删除:按集成判定同步完成;
  5. detachedrm -rf收尾:由分离进程完成目录的最终物理删除;跨文件系统(rename 返回 EXDEV)或权限问题导致改名失败时,回退到git worktree remove

日志写入.git/wt/logs/{branch}/internal/remove.log。需要等待完整完成后才继续的脚本可用--foreground在前台运行(阻塞至完成)。

值得一提的清理机制:每次wt remove之后.git/wt/trash/中超过 24 小时的条目都会由一个分离的rm -rf清扫——这是为之前某次后台删除被中断(SIGKILL、重启、磁盘写满)而遗留的孤儿目录准备的“最终清理”。该内部清扫在 src/commands/remove.rs 中由run_internal_sweep触发,安排在主要输出之后执行,因此绝不会延迟用户可见的进度与成功信息。

删除前还有个细节:门禁、daemon 停止、改名三步严格按序执行,且所有权检查在脏门禁之前——若目录被其他仓库占用,git status会把对方仓库的“脏”读成当前 worktree 的脏并诱导用户用--force补救,而该检查正是为拦截这种误导而存在。集成测试test_remove_rechecks_ownership_after_pre_remove_hook验证了规划之后、批准提示与钩子运行期间拓扑变化时,改名前的二次检查仍能拦截。

进程收割--reap[实验性]

删除 worktree 后,在其中启动的常驻进程——post-start开发服务器、文件监听器、语言服务器——仍会占用端口与文件句柄。--reap在删除前终止这些进程:

$ wt remove --reap feature ◎ Reaping 2 processes under feature worktree ┃ 51234 node ┃ 51240 esbuild ✓ Reaped 2 processes ◎ Removing feature worktree & branch in background (same commit as main, _)

进程发现机制基于工作目录:凡是当前目录位于(或位于其下)worktree 路径的进程都会被找到,随后先发SIGTERM,幸存者再发SIGKILL。实现位于 src/git/reap.rs:lsof -d cwd -F pcn一次调用即可列出所有可见进程的 cwd(仅限当前用户可见范围,不做 root),解析后按路径前缀过滤。

为不误杀用户无意终止的工作,--reap两道保守护栏

  • 交互进程一律放过:持有控制终端的进程——交互式 shell,或带未保存缓冲区的vim等终端编辑器——永远不会被收割。实现用ps -o pid=,tty=分类,tty 列为?/??/-才视为无终端;解析失败时按“有终端”处理(fail-safe,宁可不收割)。
  • 仅按工作目录发现:在 worktree 中启动后改过目录的进程、或重新挂靠到init的守护进程,不再报告 worktree 下的 cwd,因而不会被找到。要可靠收割这类进程,应当用wt step tether启动它们——它会在 worktree 被删除时杀掉整个进程组(见 step.md 中wt step tether一节)。

收割发生在 worktree 目录被改动之前(cwd 匹配要求目录仍在原路径),因此与前台/后台删除及--force无关;同时当前wt进程自身永远不是候选。--reap仅限 Unix;Windows 没有廉价的按进程 cwd 查询手段,命令直接拒绝该标志(见 src/commands/remove.rs 的#[cfg(not(unix))]分支)。集成测试 tests/integration_tests/remove.rs 的test_remove_reap_kills_processtest_remove_reap_spares_terminal_process分别验证了“无终端进程被收割”与“持 PTY 进程被保留”两条路径。

JSON 输出:可编程的删除结果

--format=json将每次删除打印为一个对象到 stdout:worktree 删除输出{kind, branch, path, branch_outcome, branch_checked_out_at},纯分支删除则以pruned替代path字段。

branch_outcome明确命名了分支的结局,调用方可以区分“删除被拒绝”与“本来就没打算删”:

含义
deleted分支已消失
deferred交由分离的后台进程处理,本次运行看不到其结果;--foreground永远不会报告该值
not_attempted未尝试删除:detached worktree、存在兄弟检出,或使用了--no-delete-branch
retained_unmerged拒绝删除:分支未集成进目标
retained_checked_out拒绝删除:最终拓扑读取发现有活跃 worktree 检出了该分支
retained_raced被比较并交换拒绝:分支在集成检查与删除之间发生了移动。应重新读取引用后重试
retained_failed删除命令本身执行失败

多目标删除时,JSON 数组按执行顺序(其他 worktree → 纯分支 → 当前 worktree)输出,与 src/commands/remove.rs 中的执行顺序一致;branch_outcome: deferred--foreground的互斥关系也源于该文件:后台模式才把rm -rf交给分离进程。

钩子:pre-removepost-remove

  • pre-remove钩子在 worktree 被删除之前运行,此时仍可访问 worktree 文件(例如示例中的project:cleanup,用于在拆除前停掉云上资源);
  • post-remove钩子在删除完成后运行。

钩子的选取以运行wt remove的 worktree 的.config/wt.toml为准:每个被删 worktree 的pre-remove/post-remove依据该 worktree 自身的配置批准,而删除后落点的post-switch则依据用户将进入的 worktree 配置(src/commands/remove.rs 的approve_remove闭包)。批准提示被拒绝或使用--no-hooks时,生成空钩子计划,所有执行器都不运行项目钩子。钩子的完整配置方式见 hook.md。

命令参考

wt remove - Remove worktree; delete branch if merged Defaults to the current worktree. Usage: wt remove [OPTIONS] [BRANCHES]... Arguments: [BRANCHES]... Branch name or worktree path [default: current] Options: --no-delete-branch Keep branch after removal -D, --force-delete Delete unmerged branches --foreground Run removal in foreground (block until complete) --reap Kill processes started in the worktree [experimental] Before removal, terminate processes whose working directory is under the worktree — dev servers, watchers, language servers. Processes holding a controlling terminal (interactive shells, terminal editors) are left alone. Unix only. -f, --force Force worktree removal Remove a dirty worktree, including staged, modified, and untracked files. Without this flag, removal fails if the worktree has any uncommitted changes. -h, --help Print help (see a summary with '-h') Automation: --no-hooks Skip hooks --format <FORMAT> Output format JSON prints structured result to stdout after removal completes. [default: text] [possible values: text, json] Global Options: -C <path> Working directory for this command --config <path> User config file path --config-set <toml> Override config with inline TOML, e.g. --config-set list.full=true (repeatable) -v, --verbose... Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to apply the same level everywhere — including shell completion, which no flag can reach -y, --yes Skip approval prompts

该帮助文本的权威来源是 src/cli/mod.rs 中Remove(RemoveArgs)after_long_help(网站文档由同步测试从该处生成,见 docs/CLAUDE.md)。需要补充的实现细节:

  • -C <path>允许在任意目录下以指定仓库上下文执行删除;--config-set支持内联 TOML 覆盖(可重复)。
  • 配置项[remove] delete-branch(默认true)可持久化控制是否删除分支,命令行--no-delete-branch优先于配置(见 src/commands/remove.rs 的flag_pair逻辑)。
  • 钩子批准提示默认在交互终端弹出,自动化场景可用-y跳过(需先在 approvals 配置 中完成白名单设置)。

延伸阅读

  • wt merge:合并后自动删除 worktree(wt remove的姊妹命令)
  • wt list:查看所有 worktree 与集成状态符号(_
  • wt step tether:用进程组方式可靠收割改过目录的守护进程
  • hooks 配置:pre-remove/post-remove钩子定义
  • src/git/remove.rs:trash 暂存、CAS 分支删除、fsmonitor 停止的完整实现
  • src/git/reap.rs:--reap的发现与信号升级实现
  • tests/integration_tests/remove.rs:锁、脏树、跨仓库占用、--reap等场景的集成测试

【免费下载链接】worktrunkWorktrunk is a CLI for Git worktree management, designed for parallel AI agent workflows项目地址: https://gitcode.com/GitHub_Trending/wo/worktrunk

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询