Worktree 在分支删除后变成幽灵

git worktree 对应的分支被删除后,worktree 目录还在磁盘上,git worktree list 显示为损坏状态,相关 git 操作全部报错。本文给出清理幽灵 worktree 和预防悬空 worktree 的完整方案。

你用 git worktree add ../hotfix-2.1 hotfix/2.1 创建了一个 worktree 处理紧急修复,修复完合并后用 git branch -d hotfix/2.1 删除了分支。第二天发现 ../hotfix-2.1 目录还在,git worktree list 输出里那个 worktree 变成了「prunable: gitdir file points to non-existent location」,或者更糟糕的是——git status 在主仓库里报出一堆关于这个 worktree 的错误。这种「幽灵 worktree」不影响实际的代码,但会污染 git 命令的输出,并在某些 Git 版本里触发 bug。

常见原因

1. 删除分支时没有先删除对应 worktree

正确顺序应该是先 git worktree remove,再 git branch -d。反过来先删分支,worktree 就失去了分支引用,变成悬空状态。worktree 目录的 .git 文件指向主仓库的 .git/worktrees/<name> 目录,但那里记录的分支已不存在。

怎么判断git worktree list 若某行显示 prunablelocked(bare) 且路径对应不存在的目录,就是这种情况。

2. 直接删除了 worktree 物理目录而非用命令移除

执行了 rm -rf ../hotfix-2.1 直接删除目录,但 .git/worktrees/hotfix-2-1/ 里的元数据没有被清理,主仓库里还有这个 worktree 的记录,导致悬空引用。

怎么判断ls .git/worktrees/ 若有目录,但 git worktree list 里对应的物理路径不存在,说明是这种情况。

3. IDE 或 GUI 工具自动创建了 worktree 但未正确清理

某些 IDE(如 JetBrains 系列的 Git Worktree 功能)或 Git GUI 工具在背后创建 worktree,当项目关闭或分支切换时没有执行正确的清理步骤,留下了不完整的 worktree 记录。

怎么判断git worktree list --porcelain 查看 worktree 元数据,若有无法识别路径的 worktree,检查该路径是否曾被 IDE 使用。

4. 远端分支被删除但本地 worktree 追踪的是远端分支

worktree 追踪的是 origin/feature/x,远端合并后删除了这个分支,本地 worktree 对应的 remote-tracking ref 也消失了,worktree 处于「没有追踪分支」的 detached 状态。

怎么判断git -C <worktree-path> branch -vv 若上游分支显示「[gone]」,说明追踪的远端分支已删除。

5. 文件系统挂载点变化导致 worktree 路径失效

worktree 创建在外部磁盘或网络挂载点(如 NFS),挂载点取消后 worktree 路径不再可访问,Git 认为 worktree 已损坏。

怎么判断git worktree list 中的路径是否指向外部磁盘或网络路径,确认挂载点是否还在线。

6. Git 版本升级后 worktree 元数据格式不兼容

从旧版 Git(2.5 以下)升级到新版后,旧格式的 worktree 元数据可能无法被正确读取,导致 worktree 在 list 中显示为损坏。

怎么判断git --version 并查看 .git/worktrees/ 里的文件格式(gitdircommondir 文件是否存在且内容正确)。

最短修复路径

Step 1:查看所有 worktree 的状态

git worktree list
git worktree list --porcelain  # 更详细的机器可读格式

Step 2:自动清理所有 prunable(可修剪)的 worktree

git worktree prune
git worktree prune --verbose  # 显示被清理的内容

Step 3:手动移除特定的幽灵 worktree

# 若目录还存在
git worktree remove <worktree-path>

# 若目录已不存在,强制移除元数据
git worktree remove --force <worktree-path>

Step 4:手动清理 .git/worktrees/ 下的残留元数据

ls .git/worktrees/
# 逐一检查哪些目录对应的 worktree 已不存在
rm -rf .git/worktrees/<stale-worktree-name>

Step 5:若 worktree 有未提交改动,先救出来

# 检查 worktree 里是否有未保存的改动
git -C <worktree-path> status 2>/dev/null
git -C <worktree-path> stash
# 或者 cherry-pick 出来
git -C <worktree-path> log --oneline -5

Step 6:验证清理结果

git worktree list
# 应该只剩主 worktree(当前仓库本身)
ls .git/worktrees/  # 应该为空或只有有效条目

预防建议

  • 建立操作习惯:PR 合并后的清理顺序是「先 git worktree remove,再 git branch -d,再 git push origin --delete」,不要颠倒。
  • 永远不要用 rm -rf 直接删除 worktree 目录,始终用 git worktree remove 命令。
  • 创建 worktree 时用有意义的路径名,并在团队规范里约定 worktree 目录的命名和位置(如统一放在 ~/worktrees/<repo-name>/),便于管理和清理。
  • 定期(比如每周)执行 git worktree prune 清理已失效的 worktree 记录,可以加入 cron 任务或放进 post-merge hook。
  • CLAUDE.md / AGENTS.md 里写明:AI agent 操作完 worktree 后必须执行 git worktree remove,不能直接删除目录。
  • 若使用 IDE 的 worktree 功能,在 IDE 里关闭 worktree 而不是在系统文件管理器里删除目录。

常见问答 (FAQ)

Q: git worktree prunegit worktree remove 有什么区别? A: prune 自动清理所有已失效(目录不存在、引用已删除)的 worktree 记录,是批量清理工具;remove 是主动移除一个指定的 worktree,会同时删除工作目录和元数据(有未提交改动时会报错,需加 --force)。

Q: worktree 里有未提交的改动,但分支已经删了,还能救回来吗? A: 能。先 git -C <worktree-path> stash,再 git worktree remove <path>,stash 会被保存在主仓库的 stash 列表里,之后可以用 git stash apply 恢复。或者直接在 worktree 里创建新分支:git -C <worktree-path> checkout -b rescue/from-worktree,再从主仓库处理。

Q: 可以在同一个仓库创建多少个 worktree? A: Git 没有硬性限制,理论上可以创建任意数量的 worktree。实际上每个 worktree 独占一个分支(同一个分支不能被两个 worktree 同时 checkout),可用的 worktree 数量受限于仓库的分支数量。

Q: worktree 里能运行 git submodule update 吗? A: 可以,但 submodule 的状态是在每个 worktree 里独立的。git worktree add 时加 --no-checkout 然后手动 git submodule update --init 是更安全的做法,避免多个 worktree 的 submodule 状态互相干扰。

相关阅读

标签: #git #version-control #排查