.gitディレクトリの掃除:Git GCとGit Pruneでリポジトリを軽量化する方法

Git tutorial - IT technology blog
Git tutorial - IT technology blog

.gitディレクトリが突然ストレージの「お荷物」になったとき

数ヶ月前、チームのCI/CDシステムが夜中に突然 No space left on device エラーを吐きました。Jenkinsサーバーを急いで確認したところ、ディスク容量が100%使い切られていました。驚いたことに、犯人はログファイルやDockerイメージではなく、長年運用してきたプロジェクトの .git ディレクトリでした。なんと5GB近くまで膨れ上がっていたのです。

開発者が git fetch を実行するたびに、長年蓄積されたゴミデータが複製され続けます。もしプロジェクトのクローン速度が亀のように遅かったり、 .git の容量が異常に大きかったりする場合は、 Git GCGit Prune を使った「大掃除」の時期かもしれません。

なぜ.gitディレクトリは肥大化するのか?

基本的にGitは、現在のコードだけでなく変更履歴のすべてを保存します。リポジトリが肥大化する主な理由は3つあります。

  1. Loose Objects(バラバラのオブジェクト): git add をするたびに、Gitは新しいzlib圧縮ファイルを作成します。数千のコミットを繰り返すと、これらの小さなファイルが爆発的に増え、システムを低速化させます。
  2. Dangling Commits(宙に浮いたコミット): rebasecommit --amend などの操作を行うと、古いコミットが残されることがあります。これらはデータベース内に存在し続けますが、どのブランチにも属していません。
  3. Reflogのデータ: reflog メカニズムは、誤操作時のデータ復旧を助けるためにあらゆる行動を記録します。デフォルトでは、Gitはこれらの記録を30日から90日間保持します。

私のチームでも実際に、誤って .jar などの重いバイナリファイルやRAW画像をコミットしてしまったことがありました。その後ファイルを削除してコミットし直したとしても、それらの巨大なファイルはGitの履歴の中に密かに残り続け、リポジトリの容量が減ることはありません。

ステップ1:リポジトリの「健康状態」を測定する

まず、以下のコマンドでどれだけの余剰データがあるかを確認しましょう。

git count-objects -vH

count(loose objectsの数)と size-pack の項目に注目してください。もし count が数万に達しているなら、それはリポジトリが非常に散らかっている明らかな兆候です。

ステップ2:データ圧縮(Git GC – Garbage Collection)

git gc コマンドは、いわば専用のゴミ収集車のようなものです。バラバラのオブジェクトをパックファイル(packfiles)にまとめ、参照されなくなったオブジェクトを削除し、インデックスを更新します。

最大限に最適化するために、私はよく --aggressive フラグを使用します。

git gc --aggressive --prune=now

--aggressive フラグを使用すると、Gitはファイル間のデルタ(差分)をより効率的に探し出し、最大限に圧縮します。ただし、 --prune=now には注意が必要です。このコマンドは宙に浮いたコミットを即座に完全に削除するため、誤ってブランチを消してしまった場合に reflog でデータを救出することができなくなります。

ステップ3:Git Pruneで徹底的に掃除する

reflog の保護メカニズムがあるため、 git gc だけではすべてを削除できないことがあります。古いデータを本当に一掃するために、私は以下の強力な「粛清」コマンドセットを使用しています。

# 期限切れのreflogレコードを即座に強制終了させる
git reflog expire --expire=now --all

# 参照されていないオブジェクトを削除する
git prune --verbose --progress

# すべてを一つのパックファイルにまとめる
git repack -ad

結果は驚くべきものでした。先ほどの5GBのプロジェクトでこのコンボを実行したところ、 .git ディレクトリの容量は800MB未満まで減少しました。 git status コマンドの実行速度も明らかに速くなりました。

リポジトリの肥大化を未然に防ぐ戦略

掃除はあくまで一時的な対処法です。クリーンなリポジトリを維持するために、私のチームでは2つの厳格なルールを適用しています。

1. バイナリファイルを厳格に管理する

ビルドファイル(node_modules、targetなど)、ログファイル、巨大なバイナリファイルをGitに入れてはいけません。もし誤ってコミットしてしまった場合、履歴からそれらを削除するのは非常に複雑で、通常は BFG Repo-Cleaner のような専門的なツールが必要になります。

2. CI/CDでの定期的なメンテナンス

ディスク容量の警告が出るのを待つのではなく、大規模プロジェクト向けに週末に実行されるメンテナンススクリプトを追加しています。

#!/bin/bash
git gc --prune=today --quiet
git remote prune origin

git remote prune origin コマンドは非常に便利です。サーバー上で削除されたブランチへの参照をクリーンアップし、メンバーのブランチリストを常にすっきりと保つことができます。

おわりに

Gitリポジトリの管理は、家の掃除と同じです。定期的に行うことで、すべてが軽くスムーズに保たれます。もしリポジトリが重いと感じたら、今すぐ git gc --aggressive を試してみてください。節約できる容量の多さに驚くはずです!

もし、誤って重いファイルをGitにコミットして困った経験があれば、ぜひコメント欄で共有してください!

Share: