科技 · 实用指南
Git worktree 在移动文件夹后损坏?先审计双向链接,再修复、清理或移除
先保全未提交改动,绘制 Git 的两条路径记录,选择正确的修复分支,并在清理或移除前验证双向关系。

如果主仓库或链接 worktree 没有通过 `git worktree move` 就被移动,Git 保存的两条路径记录可能互相不一致。表现可能是“not a git repository”、worktree 被标为 `prunable`,或者文件夹仍然包含你的文件,却不再出现在 `git worktree list` 中。
不要先运行 `prune`、`remove --force`、`rm -rf`,也不要先手工编辑 `.git`。第一步是保全工作目录,并记录链接两端。修复命令通常很短;真正重要的是证明你修复了正确的关系,而且没有丢失未提交内容。
本文只处理 Git 自己的 worktree 元数据。桌面客户端、IDE、任务运行器或工作区数据库可能另外保存一条路径。Git 已经健康,并不代表那个独立登记表也已更新。
1. 冻结并保全证据
停止清理任务、垃圾回收、IDE 的“移除工作区”操作,以及会调用 `git worktree prune` 的脚本。审计期间不要再次移动任一目录。
如果受影响的工作目录包含未提交或未跟踪文件,先把整个目录复制到安全位置。登记关系损坏不代表这个文件夹可以丢弃。
从任何仍能执行 Git 命令的 worktree 保存以下输出:
```sh git --version git worktree list --porcelain git worktree prune --dry-run --verbose ```
试运行只用于诊断。如果它提议移除某个 worktree,而该 worktree 的文件仍存在于新路径,请立即停止:这说明登记路径过期,并不等于允许清理。
对每个受影响目录,还要记录绝对路径、该路径是否存在,以及 `git status --porcelain=v1` 能否运行。若能运行,保存状态输出;若失败,原样保存第一条错误。
不要把凭据、含令牌的远程 URL 或私有文件内容粘贴到支持工单。
2. 建立双向链接台账
链接 worktree 通常包含一个很小的 `.git` 文件,它指向主仓库 `.git/worktrees/` 下的私有目录;该私有目录里的 `gitdir` 记录又指回链接工作目录。因此,这段关系有两个方向。
每个 worktree 建一行记录:
| 字段 | 需要记录的内容 | | --- | --- | | Worktree 标签 | 中性名称,不使用秘密项目名 | | 当前目录 | 现在实际存在的路径 | | 列表目录 | `git worktree list --porcelain` 中的路径 | | 链接端 `.git` 目标 | 记录 `gitdir:` 目标,但不要编辑 | | 主仓库端 `gitdir` 目标 | `.git/worktrees/<id>/gitdir` 中的反向路径 | | 分支或 detached HEAD | 来自 porcelain 列表或已保存的 HEAD | | 已修改/未跟踪状态 | 精确状态摘要,或“无法读取状态” | | prune 试运行结果 | 无动作,或原样记录拟移除项 | | 第三方路径 | IDE/工作区在 Git 之外保存的路径 | | 判定 | 主仓库移动、链接目录移动、两端移动、孤立目录或未知 |
不要用目录名猜测对应的登记记录。两个 worktree 可能名称相近,管理目录名也不能替代当前实际路径。
3. 运行 repair 前先分类移动场景
根据台账选择下面的一个分支。
仅主 worktree 被移动
链接 worktree 的 `.git` 文件仍指向主仓库旧位置,因此链接目录内的 Git 命令可能失败;新主仓库仍保留私有 worktree 记录。
在新的主 worktree 中运行:
```sh git worktree repair ```
Git 官方文档说明,这会把链接 worktree 重新连接到已移动的主仓库。
仅一个链接 worktree 被移动
链接目录有时仍能执行 Git 命令,因为它的指针还能到达主仓库;但主仓库端的 `gitdir` 仍写着链接目录旧路径,主列表可能把旧路径标成 `prunable`。
在已移动的链接 worktree 内运行:
```sh git worktree repair ```
然后回到主 worktree,确认 porcelain 列表显示的是新路径。
多个链接 worktree 被移动
从能到达主仓库的 worktree 运行,并传入所有链接 worktree 的新路径:
```sh git worktree repair /new/path/alpha /new/path/beta ```
诊断时优先使用绝对路径;只有在你明确采用相对链接策略并验证过该策略时才例外。
主 worktree 和链接 worktree 都被移动
在新主 worktree 中运行,并传入每个链接 worktree 的新路径:
```sh git worktree repair /new/path/alpha /new/path/beta ```
这是两端都被手工移动后,明确修复两个方向的分支。
目录存在,但没有登记为 worktree
不要把“未登记为 worktree”理解为“可以删除”。即使管理记录缺失,目录里仍可能有未提交内容。先保全并检查文件,把它排除在自动删除之外。当身份关系不明时,`repair` 无法凭空创建正确的管理记录。
如果人工核实它确实只是可丢弃残留,移除也应是检查后的独立决定,而不是 Git 探测失败后的自动动作。
4. 验证修复后的双向关系
修复后,在主 worktree 和每个链接 worktree 重新执行同一组只读检查:
```sh git worktree list --porcelain git worktree prune --dry-run --verbose git status --porcelain=v1 git rev-parse --path-format=absolute --git-dir git rev-parse --path-format=absolute --git-common-dir ```
只有同时满足以下条件,才能接受修复:
1. 每个预期 worktree 只出现一次,并指向当前路径。 2. 没有存活的 worktree 被标记为 `prunable`。 3. 每个链接 worktree 都解析到预期的公共目录。 4. 分支或 detached HEAD 身份未改变。 5. 修复前记录的已修改和未跟踪文件仍然存在。 6. prune 试运行不提议移除任何存活 worktree。 7. 第三方工作区或 IDE 能打开正确路径,或者已被明确记录为另一个尚未解决的问题。
任一项失败就停止。保留证据包,不要升级使用强制参数。
5. 把 repair、prune、remove 与 lock 分开
`git worktree repair` 用于更新过期或损坏的管理链接,是主 worktree 或链接 worktree 被移动后的常规修复命令。
`git worktree prune` 用于删除目录已经消失的 worktree 管理信息。它不是处理“目录只是被移动”的第一步;务必先检查试运行。
`git worktree remove` 用于退役不再需要的已登记 worktree。Git 通常会拒绝移除不干净的 worktree;`--force` 会削弱这层保护,它不是修复手段。
`git worktree lock --reason "..."` 可保护移动硬盘或间歇挂载共享盘上的 worktree,防止它被自动清理。锁定不会修复错误路径;锁定后的 worktree 若要移动或移除,还会需要额外强制操作。
这些命令用途不同。Git 不支持用 `worktree move` 移动主 worktree 或包含子模块的链接 worktree;手册也不建议对超级项目进行多工作区检出。本文示例不能解决这些子模块限制,应保全证据并单独核查该布局。
6. 把第三方路径登记视为另一层
工作区产品可能在自己的数据库中保存 worktree 的绝对路径。即使通过受支持的 `git worktree move` 移动后,Git 已正确列出新路径,产品仍可能继续打开旧路径。
先用前面的验收项证明 Git 状态正常,再查该产品文档中的重新指向、导入或重建流程。不要为了迎合过期的应用记录,再次修改 Git 元数据。
单独记录第三方结果:
- Git 健康,产品路径已更新。 - Git 健康,但产品没有重新指向操作。 - Git 健康,需要重建产品记录,且应用状态可能丢失。 - Git 仍不健康,返回 Git 双向台账继续排查。
这条边界可避免把产品限制误诊为 Git 修复失败。
7. 一次性测试仓库验证了什么
在一个临时 Git 2.51.1 仓库中,我们创建了主 worktree 和一个链接 worktree,并在链接目录放入一个未跟踪文件。
两边目录都被手工移动后,主列表把链接目录旧路径显示为 `prunable`,prune 试运行提议删除其管理记录,移动后的链接目录中 `git status` 失败。从新主目录运行 `git worktree repair <新链接路径>` 后,两端路径均恢复,未跟踪文件仍然存在。
只移动链接目录时,链接目录自身的状态仍可读取,但主仓库端路径过期;在已移动的链接目录内运行 repair 后,主列表更新。只移动主目录时,链接端指针失效;从新主目录运行 repair 后恢复。
三种临时仓库场景在 Linux、Git 2.51.1 上实测;每次修复后,已修改文件和未跟踪文件的字节、HEAD 与分支身份均保持不变。当前文档已核到2.56.0,但未测试该版本程序。实验不能证明所有 Git 版本、网络文件系统、子模块布局、IDE 或工作区数据库的行为。
常见问题
我应该手工编辑链接 worktree 的 .git 文件吗?
把它保留为证据,但优先使用 `git worktree repair`。只编辑一端,可能让主仓库端的反向记录继续过期。
为什么移动后的链接目录能运行 git status,主列表却仍显示旧路径?
链接目录可能仍能通过 `.git` 指针到达主仓库,但主仓库中的反向 `gitdir` 仍指向链接目录旧位置。这属于单向修复场景。
标成 prunable 的条目可以安全清理吗?
只有确认对应 worktree 确实消失后才可以。仍存活但已移动的目录,会让旧路径看起来可清理。
repair 能恢复未登记目录吗?
只有当剩余管理信息足以让 Git 重连身份关系时才行。一个看起来像 worktree 的目录,并不能证明它拥有有效登记;删除前必须保全并检查。
repair 会同时更新 IDE 或工作区管理器吗?
不一定。Git 只修复 Git 元数据;其他工具可能保存独立绝对路径,需要使用各自支持的更新或重建流程。
