如何清理 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 的 commit | git log @{upstream}..HEAD --oneline |
| 没有上游的分支 | git rev-parse --abbrev-ref @{upstream} 报错 |
| stash | git stash list(同一仓库的所有 worktree 共享) |
| 中断的 rebase、merge 或 cherry-pick | git 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 就是很有力的证据。
一套安全的清理流程
- 确认没有 agent 或编辑器还在这个目录里工作。
- 做完上面 6 项检查,处理查到的东西:提交、push,或者确认可以丢弃。
- 按分支的合并方式,确认它已经进了 main。
- 备份需要的被 ignore 的文件,比如
.env。 - (报错的处理见如何删除 git worktree)执行不带
--force的git worktree remove <路径>,有遗漏 git 会拦住你。 - 用
git branch -d删分支;squash 合并后 git 看不出已合并,要用-D。 - 对手动删掉的目录执行
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,并让那个分支可以在别处重新检出。