科技 · 實用指南
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 中繼資料;其他工具可能儲存獨立絕對路徑,需要使用各自支援的更新或重建流程。
