ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

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

2026/9/16 12:45:55 拓冰建站 浏览量
Worktrunk `wt remove` 命令全解析:安全删除 Git worktree 与已合并分支 Worktrunkwt remove命令全解析安全删除 Git worktree 与已合并分支【免费下载链接】worktrunkWorktrunk is a CLI for Git worktree management, designed for parallel AI agent workflows项目地址: https://gitcode.com/GitHub_Trending/wo/worktrunkwt 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 的IntegrationReason与check_integration#条件含义wt list符号典型成本1Same commit分支 HEAD 与默认分支是同一个提交_~1ms2Ancestor分支在目标的历史中fast-forward 或 rebase 场景⊂~1ms3No added changes三点差异target...branch为空⊂~50–100ms4Trees match分支 tree SHA 等于目标 tree SHA⊂~100–300ms5Merge adds nothing模拟合并产生与目标相同的 tree覆盖目标已推进且改动了不同文件的 squash 合并⊂~500ms–2s6Patch-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_checkout与branch_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_advances与cas_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_files、test_remove_force_with_modified_files、test_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与模块级文档锁检查与脏工作树门禁锁检查读取locked文件而非缓存的list_worktrees()确保能感知批准提示与pre-remove钩子之间发生的锁变更--force跳过脏检查停止 fsmonitor daemon尽力而为发送git fsmonitor--daemon stop的优雅 IPC 请求2 秒超时若 daemon 卡死Unix 下再通过lsof按 socket 解析 PID 并 SIGTERM→SIGKILL 强制终止——否则 daemon 会在 worktree 消失后永久泄漏改名进 trashworktree 目录被改名到.git/wt/trash/name-timestamp/同文件系统改名是瞬时元数据操作用户工作区立刻清空git 元数据随即被剪除prune_worktree_entry只针对该 worktree 条目不影响目录暂时缺失的兄弟 worktree分支删除按集成判定同步完成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.rslsof -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仅限 UnixWindows 没有廉价的按进程 cwd 查询手段命令直接拒绝该标志见 src/commands/remove.rs 的#[cfg(not(unix))]分支。集成测试 tests/integration_tests/remove.rs 的test_remove_reap_kills_process与test_remove_reap_spares_terminal_process分别验证了“无终端进程被收割”与“持 PTY 进程被保留”两条路径。JSON 输出可编程的删除结果--formatjson将每次删除打印为一个对象到 stdoutworktree 删除输出{kind, branch, path, branch_outcome, branch_checked_out_at}纯分支删除则以pruned替代path字段。branch_outcome明确命名了分支的结局调用方可以区分“删除被拒绝”与“本来就没打算删”值含义deleted分支已消失deferred交由分离的后台进程处理本次运行看不到其结果--foreground永远不会报告该值not_attempted未尝试删除detached worktree、存在兄弟检出或使用了--no-delete-branchretained_unmerged拒绝删除分支未集成进目标retained_checked_out拒绝删除最终拓扑读取发现有活跃 worktree 检出了该分支retained_raced被比较并交换拒绝分支在集成检查与删除之间发生了移动。应重新读取引用后重试retained_failed删除命令本身执行失败多目标删除时JSON 数组按执行顺序其他 worktree → 纯分支 → 当前 worktree输出与 src/commands/remove.rs 中的执行顺序一致branch_outcome: deferred与--foreground的互斥关系也源于该文件后台模式才把rm -rf交给分离进程。钩子pre-remove与post-removepre-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.fulltrue (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_VERBOSE0|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合并后自动删除 worktreewt remove的姊妹命令wt list查看所有 worktree 与集成状态符号_、⊂wt step tether用进程组方式可靠收割改过目录的守护进程hooks 配置pre-remove/post-remove钩子定义src/git/remove.rstrash 暂存、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),仅供参考