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?