雲端 Mac 長期 Git 儲存庫維護:提交圖、多包索引與安全回收

雲端 Mac 長期 Git 儲存庫維護:提交圖、多包索引與安全回收

一台雲端 Mac 連續承擔數週建置工作後,git fetch 可能依然很快,但切換分支、計算合併基準與走訪物件的速度會逐漸下降。常見原因並非網路,而是長期駐留的儲存庫累積了大量鬆散物件、零散的 pack 檔案、失效的遠端參照,以及多次中斷後遺留的維護鎖。直接刪除 .git 再重新複製雖然簡單,卻會失去既有的物件快取,也只是把問題延後到下一輪。

更穩妥的做法,是把儲存庫維護視為獨立作業:先量測,再以互斥方式執行增量任務,最後驗證物件連通性。以下流程適用於固定工作區,也適合使用多個 worktree 的建置節點。

先建立可比較的儲存庫基準

不要先看目錄總大小。Git 物件的數量、組織方式與可達性,更能反映實際問題。進入儲存庫後,記錄下列結果:

git count-objects -vH
git rev-parse --git-dir
git rev-parse --git-common-dir
git for-each-ref --format='%(refname)' refs/remotes | wc -l
find "$(git rev-parse --git-common-dir)/objects/pack" -name '*.pack' | wc -l

count 偏高表示鬆散物件較多;packs 持續增加,則代表歷次作業不斷產生小型 pack 檔案。使用 worktree 時,維護目標應以 git rev-parse --git-common-dir 傳回的共用目錄為準,不能只檢查目前 worktree 下的 .git 指標檔案。

建議將每次維護前後的 countin-packpackssize-pack 寫入一般純文字記錄。不要只以磁碟占用是否下降來判斷成功:更新提交圖可能幾乎不會減少空間,卻能改善歷史走訪與合併基準查詢的效能。

選擇增量任務,而非激進清理

Git 的維護任務可以分開執行。長期運作的建置節點通常會先啟用提交圖、鬆散物件整理與增量重新封裝:

git config maintenance.strategy incremental
git maintenance run \
  --task=commit-graph \
  --task=loose-objects \
  --task=incremental-repack

提交圖可加速歷史可達性查詢;多包索引讓 Git 不必逐一掃描所有 pack;增量重新封裝則會逐步合併物件,避免一次完整回收占用過多時間與暫存空間。

不要把 git gc --prune=now 當作日常排程任務。立即清除會移除可供復原的緩衝期,也可能與仍持有舊物件參照的並行程序發生衝突。

如果儲存庫剛經歷大規模分支刪除,仍應先保留預設寬限期。只有確認目前沒有建置、簽出、重定基底、子模組更新或封存任務正在執行時,才考慮進一步回收。

遠端參照應分開治理

長期未清理的遠端追蹤分支會擴大走訪範圍。先預覽,再執行清理:

git remote prune origin --dry-run
git remote prune origin

這只會處理遠端追蹤參照,不會取代物件回收,也不會刪除本機開發分支。如果建置指令碼依賴已從遠端刪除的參照,應先修正指令碼,而不是永久保留失效參照。

使用原子鎖隔離建置與維護

維護任務與建置任務必須共用同一把儲存庫層級的鎖。mkdir 在同一個檔案系統內具有原子性,適合作為簡單的鎖定原語:

common_dir="$(git rev-parse --git-common-dir)"
lock_dir="$common_dir/maintenance.lock"

if ! mkdir "$lock_dir" 2>/dev/null; then
  printf '%s\n' "maintenance skipped: repository is busy"
  exit 0
fi

trap 'rmdir "$lock_dir"' EXIT INT TERM

git maintenance run \
  --task=commit-graph \
  --task=loose-objects \
  --task=incremental-repack

建置進入點也應檢查或持有同一把鎖,否則只有維護指令碼單方面加鎖並無意義。多個 worktree 不能各自在工作目錄下建立鎖,因為它們共用同一個物件庫。

可以在鎖目錄內寫入程序編號與啟動時間,供人工疑難排解使用,但不要只因時間看起來很舊就直接刪除。應先使用 ps 確認程序是否仍存在,再檢查是否有 git pack-objectsgit index-packgit maintenance 正在執行。

驗證索引與物件連通性

維護命令以零值結束,只代表命令已完成,不代表儲存庫一定適合繼續投入建置。至少應執行三類驗證:

git commit-graph verify
git multi-pack-index verify
git fsck --connectivity-only

各項檢查可依下表納入流水線:

| 檢查 | 目的 | 失敗後的動作 | |---|---|---| | `commit-graph verify` | 驗證提交圖分層與參照 | 重新寫入提交圖後再次檢查 | | `multi-pack-index verify` | 驗證多包索引指向 | 重建索引,不要手動刪除 pack | | `fsck --connectivity-only` | 檢查可達物件是否完整 | 停止重複使用儲存庫,從可信任的遠端補齊 | | `count-objects -vH` | 比較維護前後的結構 | 保存變化趨勢,不設定武斷門檻 |

如果驗證失敗,不要先執行更激進的回收。應保留現場資訊,包括命令輸出、Git 版本、共用物件目錄路徑,以及最近一次成功建置的提交編號。物件缺失時,可從可信任的遠端重新擷取;如果仍無法復原,應隔離舊目錄並建立新的複製,而不是反覆嘗試在原目錄中刪除檔案。

將維護納入節點生命週期

維護頻率應由儲存庫的變更量決定,而不是所有專案共用固定的分鐘間隔。提交頻繁的單一儲存庫可以每天在最後一項任務結束後執行增量維護;變更頻率較低的專案,則可依鬆散物件與 pack 數量的趨勢觸發。執行前也要確認可用磁碟空間,因為重新封裝期間,新舊 pack 會短暫共存。

在 VMOak 的雲端 Mac 上,建議將維護指令碼放在儲存庫外部、唯讀的維運目錄中,再由排程任務傳入目標路徑。指令碼不應讀取專案分支內的同名檔案,以免一般程式碼變更改寫維護邏輯。若節點承載多個儲存庫,應逐一加鎖、逐一驗證;單一儲存庫失敗,不應觸發其他儲存庫的清理。

最終驗收不能只看「空間變小」,還要確認日常操作沒有退化:執行一次遠端更新、目標提交簽出、合併基準查詢與唯讀建置準備,再比較維護前後的記錄。如此一來,Git 維護才會成為可稽核的節點操作,而不是遇到磁碟空間壓力時臨時執行的刪除命令。

常見問題

建置工作執行時可以進行 Git 維護嗎?

不建議讓回收工作與檢出、變基或封裝同時執行。應使用儲存庫層級的原子鎖,並安排在工作區閒置時維護。

為什麼不直接定期執行 git gc --prune=now?

立即清除會移除復原緩衝期,也可能刪除並行程序仍在參照的物件。長期節點更適合使用增量重新封裝。

維護完成後要檢查哪些項目?

至少驗證提交圖、多包索引與物件連通性,並記錄封裝檔數量、鬆散物件數量及磁碟用量。

獨享物理節點

為建置、自動化或遠端創作選擇雲端 Mac

比較 M4 16GB 與 M4 Pro 64GB 兩種設定,確認租用週期與可訂購節點後再提交訂單。

選擇雲端 Mac 設定