如何清理 git worktree 而不丢工作

安全删除旧 git worktree 的检查清单:藏在里面的 6 类工作、为什么 git branch --merged 认不出 squash 合并,以及每一类该怎么查。

一句话回答:删 worktree 之前,先查 6 类还没落地的工作:未提交改动、未跟踪文件、没 push 的 commit、没有上游的分支、stash、中断的 rebase 或 merge。然后确认分支真的进了 main。只看 git branch --merged 不够,因为 squash 合并和 rebase 合并都会生成新的 commit。

为什么旧 worktree 删起来有风险

worktree 建起来很便宜,所以会越积越多,尤其是给每个 AI 编码 agent 各开一个的时候。一周后,有的分支在 GitHub 上 squash 合并了,有的 commit 只存在你这台 Mac 上,还有的是 agent 改了但没人提交的文件。git worktree remove --force 或 rm -rf 会永久删掉未提交和未跟踪的文件:它们从没进过 git 的对象库,reflog 也救不回来。

6 类没落地的工作

类型怎么查
未提交的改动git status --porcelain 里以 M、A、D 开头的行
未跟踪的文件同一输出里以 ?? 开头的行
没 push 的 commitgit log @{upstream}..HEAD --oneline
没有上游的分支git rev-parse --abbrev-ref @{upstream} 报错
stashgit stash list(同一仓库的所有 worktree 共享)
中断的 rebase、merge 或 cherry-pickgit status 会提示;或 worktree 的 git 目录里有 rebase-merge、MERGE_HEAD、CHERRY_PICK_HEAD

另外看看有没有你在意的被 ignore 的文件,比如 .env。上面的检查都看不到它们,它们也不在任何 commit 里。

分支已经进 main 了吗?

即使 worktree 里没有只在本地的工作,它所在的分支也可能从没合并过。怎么查取决于分支是怎么合并的。

普通合并或快进

git merge-base --is-ancestor feat/search main && echo merged

分支最新的 commit 如果是 main 的祖先,所有 commit 就都在 main 里。git branch --merged main 用的就是这个判断。

rebase 合并或 cherry-pick

git cherry main feat/search

main 里的 commit 哈希不同,祖先判断会失败。git cherry 改为比较 patch-id:以 - 开头的 commit 表示改动已经在 main 里,+ 表示还没有。

squash 合并

最难的情况,也是很多 GitHub 仓库的默认方式。所有 commit 合成一个新 commit,祖先判断和逐个 commit 的 patch-id 都对不上。可以拿分支的整体 diff 和 squash 出来的 commit 对比,或者直接问 GitHub:

gh pr list --state merged --head feat/search

只要分支在合并之后没有新 commit,这个分支有一个已合并的 PR 就是很有力的证据。

一套安全的清理流程

  1. 确认没有 agent 或编辑器还在这个目录里工作。
  2. 做完上面 6 项检查,处理查到的东西:提交、push,或者确认可以丢弃。
  3. 按分支的合并方式,确认它已经进了 main。
  4. 备份需要的被 ignore 的文件,比如 .env。
  5. (报错的处理见如何删除 git worktree)执行不带 --force 的 git worktree remove <路径>,有遗漏 git 会拦住你。
  6. 用 git branch -d 删分支;squash 合并后 git 看不出已合并,要用 -D。
  7. 对手动删掉的目录执行 git worktree prune。

对每个 worktree 自动做这些检查

这套流程查一个 worktree 还好,查二十个就很累。Orchard 会持续为你 Mac 上的每个 worktree 做这些检查。判断工作是否已在 main 用的是三层检查:提交层的祖先关系、补丁层的 patch 与 squash 比对(并用 GitHub 上的 PR 交叉验证)、以及对未提交改动的比对,每个结论都附依据。清理前会先把未提交改动、未跟踪文件和分支 HEAD 快照下来并保留 7 天,一键就能恢复。有 agent 正在运行、或有中断 rebase 的 worktree 一律不清理。

刚接触 worktree?先看 git worktree 实用指南。

常见问题

为什么 git branch --merged 看不到我 squash 合并过的分支?

squash 合并会在 main 上生成一个全新的 commit,你分支上的 commit 永远不是 main 的祖先,而 git branch --merged 只看祖先关系。应该去查已合并的 PR,或者拿分支的整体 diff 和 main 对比。

git worktree remove --force 之后还能找回文件吗?

已提交的工作还在分支和 reflog 里。未提交和未跟踪的文件就找不回了:它们从没存进 git,除非删之前做过备份或快照。

git worktree prune 是做什么的?

它删除 git 为已经不存在的 worktree 目录保留的记录,比如你用 rm -rf 删掉目录之后。它不会动现有的 worktree、分支或 commit,并让那个分支可以在别处重新检出。