クラウドMacで長期運用するGitリポジトリの保守

クラウドMacで長期運用するGitリポジトリの保守

クラウドMacで数週間にわたってビルドを継続すると、git fetch は高速なままでも、ブランチの切り替え、マージベースの計算、オブジェクトの走査が徐々に遅くなることがあります。一般的な原因はネットワークではありません。長期運用するリポジトリに大量のloose object、細分化された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 が大きい場合はloose objectが多く、packs が増え続けている場合は、過去のジョブによって小さなpackが繰り返し生成されていることを示します。worktreeを使用している場合、保守対象は git rev-parse --git-common-dir が返す共通ディレクトリです。現在のworktreeにある .git ポインターファイルだけを確認しても不十分です。

保守の前後で countin-packpackssize-pack を通常のテキストログに記録することを推奨します。ディスク使用量が減ったかどうかだけで成否を判断しないでください。コミットグラフを更新しても容量がほとんど減らない場合がありますが、履歴の走査やマージベースの検索は高速化できます。

強制的なクリーンアップではなく増分タスクを選ぶ

Gitの保守タスクは個別に実行できます。長期運用するビルドノードでは、まずコミットグラフ、loose objectの整理、増分repackを有効にします。

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

コミットグラフは履歴の到達可能性に関する検索を高速化します。multi-pack-indexにより、Gitはすべてのpackを個別に走査する必要がなくなります。増分repackはオブジェクトを段階的に統合するため、一度に完全な回収を行う場合の長い処理時間や大きな一時領域を避けられます。

git gc --prune=now を日常的な定期タスクとして使用しないでください。即時pruneは復旧のための猶予期間をなくし、古いオブジェクトへの参照を保持している並行プロセスと競合する可能性もあります。

リポジトリで大規模なブランチ削除が行われた直後でも、まずはデフォルトの猶予期間を維持してください。ビルド、チェックアウト、rebase、サブモジュール更新、アーカイブの各タスクが実行されていないことを確認できた場合に限り、より徹底した回収を検討します。

リモート参照は個別に管理する

長期間整理されていないリモート追跡ブランチは、走査範囲を広げます。最初に対象をプレビューし、その後で削除を実行します。

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はオブジェクトストアを共有するため、それぞれの作業ディレクトリに個別のロックを作成してはいけません。

手動調査に利用できるよう、ロックディレクトリ内にプロセスIDと開始時刻を書き込むこともできます。ただし、時刻が古いという理由だけでロックを削除しないでください。まず ps でプロセスが存在するか確認し、続いて git pack-objectsgit index-packgit maintenance が実行中でないか調べます。

インデックスとオブジェクトの接続性を検証する

保守コマンドの終了ステータスがゼロでも、それはコマンドが完了したことを示すだけです。リポジトリをビルドに再投入できる状態とは限りません。少なくとも次の3種類の検証を実行してください。

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

各チェックは、次のようにパイプラインへ組み込めます。

| チェック | 目的 | 失敗時の対応 | |---|---|---| | `commit-graph verify` | コミットグラフのレイヤーと参照を検証 | コミットグラフを書き直して再検証 | | `multi-pack-index verify` | multi-pack-indexの参照先を検証 | インデックスを再構築し、packは手動で削除しない | | `fsck --connectivity-only` | 到達可能なオブジェクトがすべて揃っているか確認 | リポジトリの再利用を停止し、信頼できるリモートから不足分を取得 | | `count-objects -vH` | 保守前後の構造を比較 | 推移を保存し、恣意的なしきい値は設定しない |

検証に失敗しても、最初からより強制的な回収を実行してはいけません。コマンド出力、Gitのバージョン、共通オブジェクトディレクトリのパス、最後に成功したビルドのコミットIDなど、現場の情報を保全してください。オブジェクトが不足している場合は、信頼できるリモートから再取得できます。それでも復旧できない場合は、元のディレクトリで削除を繰り返すのではなく、古いディレクトリを隔離して新しくクローンを作成します。

保守をノードのライフサイクルに組み込む

保守頻度は、すべてのプロジェクトで固定の分数を共有するのではなく、リポジトリの変更量に基づいて決めるべきです。コミット頻度の高いモノリポジトリでは、毎日の最終ジョブ終了後に増分保守を実行できます。更新頻度の低いプロジェクトでは、loose object数とpack数の推移を基準に実行します。また、repack中は新旧のpackが一時的に共存するため、実行前に空きディスク容量も確認する必要があります。

VMOakのクラウドMacでは、保守スクリプトをリポジトリ外の読み取り専用運用ディレクトリに配置し、スケジューラーから対象パスを渡す構成を推奨します。スクリプトはプロジェクトブランチ内にある同名ファイルを読み込まないようにしてください。通常のコード変更によって保守ロジックが書き換えられるのを防ぐためです。1台のノードで複数のリポジトリを運用する場合は、リポジトリごとにロックを取得し、個別に検証します。1つのリポジトリの失敗をきっかけに、ほかのリポジトリまでクリーンアップしてはいけません。

最終確認では、「容量が減った」ことだけを見てはいけません。リモート更新、対象コミットのチェックアウト、マージベースの検索、読み取り専用のビルド準備を一度ずつ実行し、保守前後のログを比較して、日常操作が劣化していないことを確認します。これにより、Git保守はディスク容量が逼迫したときに臨時で実行する削除コマンドではなく、監査可能なノード運用になります。

よくある質問

ビルド実行中にGitメンテナンスを動かせますか?

checkout、rebase、pack処理との同時実行は避けます。リポジトリ単位の原子的なロックを使い、作業領域が空いている時間に実行してください。

git gc --prune=nowを定期実行しない理由は何ですか?

即時削除は復旧猶予をなくし、並行プロセスが参照中のオブジェクトを失う危険を高めます。常設環境では増分再パックが安全です。

保守後に何を検証すべきですか?

commit-graph、multi-pack-index、オブジェクト接続性を検証し、pack数、loose object数、使用容量も記録します。

専用物理ノード

ビルド、自動化、リモート制作にクラウドMacを選ぶ

M4 16GBとM4 Pro 64GBの2構成を比較し、利用期間と選択可能なノードを確認してから注文してください。

クラウドMacの構成を選ぶ