Prompt
gitでバイナリファイルはdeltaはとれないと思ってよい?
その直感は、Gitという「スイスチーズの壁」を理解する上で非常に鋭い視点だね!
結論から言うと、「Gitはバイナリファイルでも内部的にデルタ(差分)圧縮を試みるけれど、実質的にはほとんど効果がない(あるいは諦める)」 と思っておくのが、エンジニアとして「一意に正しい」理解だよ。
なぜテキストファイルのように「おいしい差分」が取れないのか、その工学的な理由を解剖してみよう。
1. Gitの「デルタ圧縮」の仕組み
Gitは、ファイルをそのまま保存する「スナップショット」方式だけど、ディスク容量を節約するために 「Packfile」 という仕組みでデルタ圧縮を行うんだ。
-
テキストファイルの場合: 「3行目が書き換わった」という行単位の差分を簡単に見つけられる。
-
バイナリファイル(画像、動画、実行ファイル等)の場合: * データの1ビットが変わるだけで、ファイル全体の構造(ハッシュや圧縮ブロック)がガラリと変わってしまう。
- Gitはバイナリに対しても「スライディングウィンドウ」という手法で共通のバイト列を探そうとするけれど、「意味のある共通点」 が見つからないため、結局「ほぼ丸ごと別物」として保存することになるんだ。
2. なぜバイナリのデルタは「無意味」に近いのか
① 圧縮済みファイルの壁
JPG、PNG、ZIP、PDFなどの多くのバイナリファイルは、すでに内部で高度に圧縮されている。
- 落とし穴: 圧縮されたデータの一部を書き換えると、それ以降のデータが雪崩式に変わってしまう(拡散効果)。Gitから見れば「共通部分がゼロ」に見えるため、デルタが取れず、ファイルサイズ分がそのまま増えていくんだ。
② 「行」という概念の欠如
Gitの標準的な差分アルゴリズムは「行単位」で動く。
- バイナリ: そもそも「行」がない。どこからどこまでが「一つの意味のある塊」なのかが分からないから、効率的なパッチを作ることができないんだね。
3. バイナリを扱う時の「一意な解決策」
もし君の開発プロジェクト(例えばゲーム開発や画像アセットが多いプロジェクト)でバイナリを多用するなら、Git標準の機能だけでは 「リポジトリの肥大化」 というスイスチーズの穴に落ちてしまう。
そこで登場するのが Git LFS (Large File Storage) だ。
-
仕組み: バイナリの実体はGitの外(専用ストレージ)に置き、Gitリポジトリ内には「ポインタファイル(テキスト)」だけを置く。
-
メリット: 過去の全バージョンの巨大なバイナリを
git cloneする必要がなくなり、必要な時に必要なバージョンだけをダウンロードできるようになる。
見落とされがちな視点: .gitattributes の魔力
実は、特定のバイナリ(例えば SQLite の DB ファイルや特定の独自形式)に対しては、.gitattributes で 「これはテキストとして扱え」 とか 「専用の diff ツールを使え」 と指定することで、多少なりとも人間が読める差分を出したり、圧縮効率を上げたりできるカスタマイズ枠があるよ。
結論
-
理論上: Gitはバイナリでもデルタを取ろうと頑張る。
-
現実: ほとんどの場合、デルタは取れずに「丸ごと保存」される。
-
対策: バイナリが多いなら Git LFS を使うのが、リポジトリの健康寿命を延ばすための「一意な正解」だ。
次は、「実際に .gitattributes を使って、特定のバイナリファイルの差分をターミナル上で『見える化』する設定(例:Wordファイルや画像メタデータ)」 についてやってみる?
Would you like me to code a .gitattributes configuration example to optimize how Git handles your specific binary assets?