Git Worktree 安全清理实战方法论:来自 novu 仓库 AI 编码助手的审计式清理流程

发布时间:2026/9/10 15:19:22
Git Worktree 安全清理实战方法论:来自 novu 仓库 AI 编码助手的审计式清理流程 Git Worktree 安全清理实战方法论来自 novu 仓库 AI 编码助手的审计式清理流程【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu本文面向在大型 monorepo如 novu中使用git worktree并行开发、并且需要定期回收旧检出空间与陈旧分支的开发者与 AI 编码助手。内容以 novu 仓库中的 Agent Skill 文档 nv-worktree-cleanup 为骨架结合同仓库的 nv-worktree-commands、nv-worktree-create 与 nv-park-and-review 等配套技能展开。读完你既能照抄一套先审计、后批量确认、再执行、最后 prune的清理命令序列也能理解 squash merge、脏工作区、未推送分支等边角情况为什么必须单独处理。为什么 worktree 清理需要一套审计流程novu 是一个被拆分为apps/、packages/、libs/、enterprise/等多模块的大型 pnpm monorepo见 AGENTS.md仅 API 侧源码就达数千个 TypeScript 文件。在这种体量下并行开发时开发者或 AI Agent 通常不会反复git checkout切换分支而是为每个任务创建一个兄弟 worktree。按仓库约定见 nv-worktree-commandsworktree 位于主检出目录的同级目录命名规则为REPO_PARENT/sanitize(BRANCH)REPO_ROOT $(git rev-parse --show-toplevel) REPO_PARENT $(dirname $REPO_ROOT) DIR_NAME sanitize(BRANCH) # 小写分支中的 / 替换为 - WORKTREE $REPO_PARENT/$DIR_NAME例如在/Users/me/work/novu检出下创建分支nv-1234/foo-bar会得到路径/Users/me/work/nv-1234-foo-bar。问题是任务一旦合并或废弃这些检出、本地分支和磁盘空间并不会自动消失。一个真正的任务如 nv-park-and-review 的评审收尾、nv-worktree-create 的连续任务交接结束时都需要清理工作区。而清理最大的风险是误删删除未合并的提交、误删主检出、或删掉 Agent 当前正所在的工作区。因此该 Skill 把核心目标定义为一个明确的终态stale、merged、missing 的 worktree 被移除它们的本地分支被删除git worktree prune已执行用户对每一次删除都已签署确认。整篇文章都在解释如何安全地抵达这个终态。四条硬性规则先定安全边界在进入任何命令之前文档明确列出了一组不可协商的硬性规则它们是整个清理流程的红线没有用户明确确认绝不--force删除 worktree。脏工作区dirty保持原样直到用户发话。绝不删除主 worktree即git rev-parse --show-toplevel指向的主检出也绝不删除 Agent 当前所在的 worktree。判断方法是把pwd或当前目录的git rev-parse --show-toplevel与git worktree list输出的每个路径比对若命中则拒绝删除并提示先通过move_agent_to_root离开。删除必须一次性批量确认通过AskQuestion并设置allow_multiple: true不能逐个征求同意。默认使用git branch -d安全删除只有用户明确标记可以强删的分支才允许-D。规则 2 在 nv-park-and-review 的收尾动作里也有呼应——该技能明确要求从主检出执行git merge --ff-only与 worktree 拆除即不要在自己所在的 worktree 里拆除自己。规则 4 则对应 Git 底层语义-d会拒绝删除未合并进上游的分支这正是本流程用来兜底防止误删的机制。六步清理工作流第 1 步枚举所有 worktree使用面向机器解析的--porcelain变体而不是人类友好的列表输出git worktree list --porcelain把输出解析成path、branch、HEAD三要素的条目并跳过主 worktree其余步骤只处理非主检出。之所以坚持用--porcelain是因为该格式稳定、不依赖终端列宽适合后续脚本与 Agent 代码逐条结构化处理。第 2 步确定基准分支base branch按顺序探测远程默认分支# 优先跟随远程 HEAD 符号引用 git symbolic-ref refs/remotes/origin/HEAD # 例如 refs/remotes/origin/main # 兜底依次尝试 main → next → master关键点有二后续 merge 检查一律以origin/base为参照这样本地分支是否陈旧就无关紧要了在判断之前先执行git fetch --prune origin确保远端已删除分支的引用也被清理避免基于过期元数据做错误判断。对于 novu 这类以 GitHub 为主托管、PR 合流频繁的仓库以origin/main作为合并基准是默认假设next/master只是无法探测 HEAD 时的传统兜底分支名。第 3 步对每个 worktree 分类这是整套方法论的核心。文档为每个非主 worktree 计算且仅计算以下状态之一状态判定方法默认动作missing磁盘缺失路径在磁盘上不存在只做 prunemerged-direct普通合并git merge-base --is-ancestor branch origin/base返回成功remove 删除分支merged-squashsquash 合并gh pr list --state merged --head branch --json number返回 PR需 GitHub CLI若缺少gh则跳过此类并标记为active同时提示安装gh或人工核验合并情况remove 删除分支确认后使用-D因为-d会拒绝dirty有未提交改动git -C path status --porcelain非空标记不删除unpushed有未推送提交分支存在不在origin/branch上的提交且无已合并 PR标记不删除active活跃不属于以上任何情况保持不动同时还要单独标记出当前 Agent 所在的 worktree比较pwd/git rev-parse --show-toplevel与每个 worktree 路径命中者从可删除集合中剔除。这个表格区分了 Git 中一个极易踩坑的语义差异本地分支落后于 origin/main并不能证明该分支已合并。squash merge 在拓扑上会让被合并分支仍然领先且未合并——git merge-base --is-ancestor这类拓扑判断对 squash 场景会给出错误答案。因此对 squash 合并的检出必须借助 GitHub CLI 的合并状态查询gh pr list --state merged --head branch来确认。第 4 步汇总展示 一次性批量批准通过AskQuestionallow_multiple: true向用户呈现分组汇总。可删除条目的选项标签需包含路径、分支、原因三要素示例merged: ../more-dcr-oauths (branch more-dcr-oauths) missing: ../old-experiment (branch old-experiment) dirty: ../wip-bar (branch wip/bar) — has uncommitted changes对于 dirty / unpushed 条目文档建议把它们单独列成可选选项默认不删但用户若确实想强删force-remove也可以主动勾选。也就是说这一批确认里同时容纳了默认安全删除项和需要用户特别授权的强删项但强删永远只能由用户显式开启。第 5 步执行确认过的删除对每个确认项安全路径与强删路径分开执行git worktree remove path # 安全路径 git worktree remove --force path # 仅当用户对 dirty 项明确 opt-in git branch -d branch # 安全删除未合并时拒绝 git branch -D branch # 仅当用户 opt-insquash-merged 或 force命令注释揭示了顺序敏感性的根源git branch -d在分支仍有提交被某 worktree 检出的情况下会以checked out at ...失败所以必须先 remove worktree、再删除分支对 squash 合并的分支拓扑上它仍领先于基准-d会拒绝必须先通过第 3 步的gh pr list证实 PR 已合并才能放心地在用户确认后使用-D。对missing条目跳过remove磁盘上本来就没有交给下一步的prune去清理登记元数据。第 6 步收尾与汇报git worktree prune git worktree list # 向用户展示最新状态git worktree prune专门清理登记了但磁盘路径已不存在的元数据条目正好补齐 missing 类的最后一步。最终汇报需包含三部分已移除的 worktree附分支名被跳过的 worktree 及原因dirty / unpushed / active任何删除失败的分支及失败原因。反模式清单四条最常见的错误做法文档用专门一节列举了该流程要主动规避的坏习惯每条都对应一个真实的 Git 语义陷阱不要对每个条目循环git worktree remove --force以求保险——force 只服务于用户显式 opt-in 的场景不要在删除 worktree 之前删除其检出的分支——git branch -d会以checked out at ...失败。正确顺序永远是先 remove worktree再删分支不要仅凭本地分支落后于 origin/main就推断已合并——squash merge 会让分支在拓扑上领先且未合并该场景必须使用gh pr list --state merged --head branch核实不要在 list 之前执行git worktree prune——否则会丢掉识别 missing 条目所需的元数据不要跳过git fetch --prune origin就动手——否则是否已合并进 origin/main的判断可能基于过期数据而失真。一个完整的示例会话文档最后给出了可直接对照的端到端会话用户指令只有一句clean up my worktrees。Agent 的完整动作序列如下git fetch --prune origingit worktree list --porcelain→ 得到 5 个 worktree1 个主检出 4 个其他分类结果../nv-100-x→ merged-direct普通合并拓扑判断即可../nv-200-y→ merged-squashPR #4321 已合并需要gh确认../nv-300-z→ dirty3 个文件被修改../old-experiment→ missing磁盘上不存在AskQuestion询问移除哪些多选dirty 条目预置警告标签用户勾选两个已合并项 missing 项git worktree remove ../nv-100-x git branch -d nv-100/xgit worktree remove ../nv-200-y git branch -D nv-200/ysquash 合并必须-D用户已确认git worktree prune顺带清理../old-experiment的登记元数据汇报移除 3 个、跳过 1 个dirty并用最终的git worktree list展示现场。注意第 4 步的多选框设计即使是 dirty 条目也被列出只是默认状态是不选 警告——这正是批量确认与强删需显式授权两条规则在 UI 层面上的落点。把清理放进完整的工作区生命周期这套清理流程并非孤立存在它处于 novu 仓库 worktree 工作流的回收站位置与上游技能形成闭环创建需要新任务工作区时由 nv-worktree-create 负责——git worktree add -b branch path建立新分支 兄弟检出复制本地.env*密钥文件不跑node scripts/setup-env-files.js避免重新生成STORE_ENCRYPTION_KEY而与主检出、共享的本地 Mongo/Redis 失配再接线 enterprise 子模块相关细节可参考 install.sh后移入该 worktree 开始工作命令参考路径命名、.env*复制、按锁文件选择包管理器novu 根目录使用pnpm-lock.yaml见仓库pnpm-workspace.yaml布局等细节汇总在 nv-worktree-commands轻量评审只需要对单条 commit 做代码评审时nv-park-and-review 会创建一个一次性review/branch-shaworktree评审落地后从主检出用git merge --ff-only快进合并并执行git worktree removegit branch -d直接拆除——那是本清理方法论在单条轻量工作区上的简化形态清理回收正是本文主讲的 nv-worktree-cleanup承接多任务积累后产生的 stale/merged/missing 检出回收。此外该技能头部的disable-model-invocation: true字段表明它是由用户显式触发例如 clean up my worktrees、prune worktrees、list stale worktrees、remove merged worktrees or branches、reclaim disk space from old checkouts而非模型自行随机调用的技能这一设计与每次删除都需用户签字的硬性规则相互呼应——回收磁盘空间的前提永远是用户知情并同意。如果你正在维护自己的多分支并行开发仓库完全可以原样采用这条流程以git worktree list --porcelain枚举、以origin/basemerge-base --is-ancestor判断普通合并、以gh pr list补判 squash 合并、以status --porcelain甄别脏检出最后把全部删除打包成一次多选确认再以prune收尾。安全边界不碰主检出、不碰所在检出、默认-d、force 必先授权比命令本身更值得复制到任何清理脚本中。【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考