世界を動かす技術を、日本語で。

大きなファイルの未来はGitにある

概要

Gitは大きなファイルの扱いが苦手で、従来はGit LFSで対応してきた。 Git LFSにはベンダーロックインやコストなどの課題が存在。 近年はGit本体が「パーシャルクローン」や「ラージオブジェクトプロミッサー」を導入。 これらの新機能により、今後はGit LFSが不要になる可能性。 現時点では一部制約が残るが、将来的にはよりシンプルに大容量ファイル管理が可能に。

Gitと大きなファイル問題

  • Git大きなファイル の管理が苦手なバージョン管理システム
  • 大きなファイルは リポジトリ肥大化cloneの遅延ホスティングコスト増 の原因
  • 2015年、 GitHubGit LFS (Large File Storage)を公開し、大きなファイル問題を回避
  • Git LFS 導入により新たな セットアップの手間ストレージコスト が発生
  • 一方、Git本体も 大きなファイル対応 を静かに進行中

Git LFSの仕組みと課題

  • Git LFS は大きなファイルを リポジトリ外部 に保存し、必要な時だけダウンロード
  • clone時には 履歴と小さなファイル のみ取得し、大きなファイルは後から取得
  • LFS利用には GitHub独自サーバー依存有料プラン の制約
  • 50GB のLFSストレージは 年間$40 (GitHub)、Amazon S3なら 年間$13
  • LFS導入後の 元に戻す作業 は履歴書き換えが必要で困難
  • コラボレーター全員が LFSのセットアップ 必須、未導入だと混乱発生

パーシャルクローンの登場

  • 2017年、 Git 本体が partial clone (パーシャルクローン)機能を導入
  • --filter オプションで、特定サイズ以上のファイルをclone時に除外可能
    • 例:git clone --filter='blobs:size=100k' <repo>
  • 必要になったときだけ オンデマンドで大きなファイルを取得
  • 小さなチェックアウト、 高速なclone、全履歴保持が可能
  • 一部コマンド(git diff, git blame等)は 都度サーバーアクセス が必要
  • LFSと同様、 大きなファイルの扱い に最適

パーシャルクローンの効果

  • 25MBのPNGファイルを50回保存したリポジトリの例
    • 通常clone: 1.3GB/4分
    • パーシャルクローン: 49MB/6秒
  • クローン速度97%短縮ディスク使用量96%削減
  • 実際の運用でLFSとほぼ同等の利便性

Git LFSのデメリットまとめ

  • ベンダーロックイン :GitHub独自仕様のサーバー依存
  • コスト増 :無料枠に制限、商用利用は有料
  • 履歴の巻き戻し困難 :LFS導入後の元戻しは事実上不可能
  • セットアップコスト :全員がLFS導入必須

ラージオブジェクトプロミッサーの未来

  • GitHubGitLab も大きなファイルに制限(100MB上限)
  • LFSは CDNを活用 しサーバーコストを抑制
  • 2025年、Git本体に large object promisor (ラージオブジェクトプロミッサー)機能がマージ
  • 仕組み:
    • 大きなファイルは 専用リモート に自動オフロード
    • clone時に Gitクライアントが自動で大きなファイルを取得
  • まだ開発途上だが、将来的にはLFS不要な世界へ
  • GitHubの100MB制限撤廃 も期待

まとめと今後の展望

  • Git本体 が大きなファイル管理を進化させつつある現状
  • 現時点では Git LFS が必須だが、将来的には パーシャルクローンプロミッサー が主流に
  • 大きなファイル管理の障壁は 徐々に解消 される見込み
  • ただし、Gitで音楽ライブラリや動画を管理するのは 依然として非推奨

Hackerたちの意見

TFAは、Git LFSが悪い理由をいくつか挙げていて、その中にはベンダーロックインのような独自性があるからって言ってるけど、それはちょっと unfair だと思う。GitHubはオープンなクライアントとサーバーを提供してるから、その点は否定されるよね。LFSはオフラインやスニークネットの操作を壊しちゃうけど、それはニッチなワークフローだし、あんまり言及されてないのが残念。でも、プロミサーでも同じように壊れそうだね。git partial cloneの例は面白い!Large Object Promisorsの説明を読むと、LFSのクライアント側の複雑さをサーバー側に移して、さらに複雑にしてるように聞こえる。クライアントがgitサーバーにアップロードして、そこからオブジェクトストアにアップロードする代わりに、クライアントはオブジェクトストアから直接ダウンロードするってこと?明らかにトレードオフがあるよね。公共のgitサーバーにアップロードすることで、隠れたプロミサーリモートに引っかかることがどれくらいあるのか気になるな。

LFSは悪いよ。サーバーの実装もひどいし、オブジェクトの内容とストレージ方法を混同してる。オプトインがひどい形で行われてて、明らかなことをすると、実際に欲しいファイルの代わりに小さなテキストファイルが出てくる。彼らの解決策がどれだけ良いかは分からないけど、LFSが悪いってことは明らかだね。

Git LFSはSSHで動かなかったから、SSL証明書を取得しなきゃいけなかったんだよね。GitHubもそれが自宅でホスティングしてる人にとっては障壁になるって分かってたと思う。でも、GitLabはついにSSH用にパッチを当てたみたいだね。

最近気づいたLFSのもう一つの悪い点は、移行するとLFSオブジェクトを含まない祖先コミットの.gitattributesが汚染されることだね。つまり、A->B->Cというコミットがあるリポジトリを移行して、Cで大きなファイルを追加した場合、AとBのコミットには存在しない大きなファイルを参照する.gitattributesが追加されるってこと。これは、移行機能が履歴を遡る際にその~gitattributes構造を持っていくからで、現在のコミットと照合しないんだよね。

いや、これは解決策じゃないよ。今のところgit LFSはただの妥協策だし、クローン操作中にフィルター引数を書くのも長期的な解決策じゃない。Git cloneは、ほとんどの人がgitの使い方を学ぶときに最初に実行するコマンドだからね。強調しておくけど、最初のコマンドだよ。フィルターを書くことを覚えてるかな?多分、アクセスしようとしてるクールなコードベースのチュートリアルに書いてあれば覚えるかも。でも、書いてなかったらどうなる?明らかに何かが足りないのに、長い時間待たされるかもしれないし。もし書けたとしても、クローンしたリポジトリがコンパイルできなかったり使えなかったりするかもしれない。もしうまくいったとしても、理解できるかな?たぶん無理だろうね。最初に学ぶコマンドでgitの内部動作をさらけ出してるんだから。ブロブって何?なんでフィルターが必要なの?ブロブはどこに保存されてるの?これは典型的な抽象化の漏れだよ。この問題は解決済みで、rsyncがやってることなんだから。実装をそのまま移植すればいいだけなのに。代替表現をサポートするか、ブロブから完全に離れる必要があるけど、gitのメンテナーたちはそれをやりたがってないみたい。

まったく同意だわ。これは、99%のユーザーが絶対に気づかないフラグを追加してGitが「修正」するという長い伝統に従ってる。デフォルトを修正することはないし、そう、デフォルトを壊さずに修正することはできるよ。

これは解決済みの問題だ:rsyncがやってる。解決策が何か説明してくれる?rsyncアルゴリズムの詳細じゃなくて、ユーザーの視点からどう見えるかを知りたいんだ。「git clone」を実行したとき、ローカルファイルシステムにはどんなファイルがあるの?

これは解決策だよ。初心者が理解できないかもしれないってことはあんまり関係ないし、解決策はそれだけで消える必要はないから。クローンは、リポジトリを設定するときに通常一度だけ実行するコマンドだからね。この動作がデフォルトになるべきだって主張することもできるかもしれないけど、フルクローンはオプトインにすべきだっていうのは別の問題だね。

クローンしたリポジトリは、ブロブが欠けてるからコンパイルできないかもしれない。ブロブの履歴だけがフィルタリングされてるんだよ。

ほとんどの膨張が過去のリビジョンに関係してるって言ったら間違いかな?もしそうなら、最新のファイルバージョンから始めるrsyncみたいな動作が一番いいスタート地点かもね。(大抵の人にはそれで十分だし。)

ちょっとした指摘:> もし25MBのうるさいPNGファイルの多くのリビジョンを持つリポジトリをgit cloneしたら、FYI「うるさい」は「ノイジー」の同義語じゃないよ。「有害な」って意味で、何かが悪臭を放ってるってことだよ。

それが著者の意図だったと思うよ。

Gitのコアで大きなファイルのサポートが見られて本当に嬉しいよ。外部のソリューションも同じようなオプトイン手続きが必要だしね。できるだけ少ない追加コマンドでシームレスに動いてほしかったから、APIは「.gitattributes」ファイルのsmudgeとcleanフィルターに制約をかけたんだ。でも、プロセスの初期段階でAtlassianやMicrosoftと直接協力して、ベンダーロックインを取り除くために頑張ったよ。特にファイルロックAPIについてはAtlassianからたくさん助けてもらったし、素晴らしい関係だった。LFSはオープンソースで、3つの異なるGitホストで互換性のあるサポートが提供されたんだ。

大きなオブジェクトのプロミッサーは、大きなファイルだけを扱う特別なGitリモートなんだ。このアプローチ、いいと思う。もしS3みたいなものを使えるようにリポジトリを設定できたら、LFSを使うのをやめるかも。S3はVCSの大きなバイナリにとって本当に良いシナジーのように思えるし、インテリジェントなティアリング機能は、歴史が自然に蓄積されて古いものが忘れられるにつれて、データを冷たいストレージに移動してくれる。10年前のデータを引っ張るために、歴史的なチェックアウトに半日かかっても気にしないよ(ロボティックテープライブラリから復元する場合ね)。

現在の仕事では、コストの理由からすべてのLFSオブジェクトをバケットにキャッシュすることにしたんだ。PRが実行されるたびに、git lfs ls-filesでオブジェクトのリストを取得して、GCPから同期させて、git lfs checkoutでオブジェクトストアからリポジトリを実際に埋めて、最後にgit lfs pullでキャッシュされていないものを取得するんだ。もしキャッシュされていないオブジェクトがあれば、gcloud storage rsyncで再アップロードする。シンプルで、開発者のための設定も不要(新しいオブジェクトをプルするだけで済むし)、GitHubのUIもリポジトリの状態に混乱しない。最初はLFSバックエンドを立ち上げるのに苦労したけど、これで今のところ主要な痛点は解決できた。GitHubはCI用のLFSファイルを引っ張るのに高額な料金を請求してきたから、各チェックアウトが新しいから、キャッシュモデルは理想的じゃない(最大10GBのキャッシュで、ブランチ間で共有できないから)、結局LFSにあるデータを毎回引っ張る羽目になって、場合によっては何度も。だから、彼らはその帯域幅に対して喜んで料金を請求してくるんだ。帯域幅を減らすためのツールを提供してないから(もっとキャッシュサイズを払わせてくれたり、全キャッシュディスクで作業者を温めたり、より良いキャッシュ管理をさせてくれたり…)。もしこれを開発者向けに有効にしたいなら、ローカルで同じ操作をする新しいgitフックを追加するだけで簡単にできるよ。

同じく、最初からデフォルトにしなかった理由が全然わからないけど、最初に出た時はそんなに一般的じゃなかったのかな。これが理由で小さなgit LFSサーバーを運営してるけど、gitがS3をネイティブにサポートするようになったらすぐにでも切り替えたいな。

Gitがシャロークローンとパーシャルクローンを直してくれたらどうかな?そうすれば、ポインタやプロミスの歴史を完全にクローンした後に大きなコンテンツを不正に追加するための複雑な回避策が必要なくなるよ。ファイルタイプやサイズごとにデフォルトのクローン深度を設定できるようにしたいし、Git属性で(もしくはリポジトリの上に存在できるファイルで、.gitconfigの場所にサポート属性を持つファイルとか?)設定できるといいな。通常の設定は、最新のコンテンツをシャロークローンしつつ、全履歴やテキストファイルの歴史的コンテンツも取得するって感じで。理想的には、クローン深度設定に合わせてプルーニングもできるといいね。なんでまだ大きなファイルポインタの話をしてるんだろう?シャロークローンとパーシャルクローンを直せば、どんなリポジトリも効率的なファイルミラーになれるよね?

この記事はLFSを不当に扱ってるよ。GitHubにロックインされるわけじゃないし、プロトコルはオープンなんだから。LFSの欠点はGit拡張として避けられないものだよ。プロミッサーは基本的にLFSと同じ概念だけど、Gitに組み込まれてるから、拡張としては実現できないより良いUXを提供できるんだ。

リポジトリで一度LFSを使うと、永久にロックされちゃうんだ。スペースを削除するにはGitHubからリポジトリを消さないといけない。これ、全然スタート地点にも立てないよね。こういう挙動はどこにも明記されてないし。以前はGitHubでGit LFSを使って、ユーザーやリポジトリの大きな圧縮データベースを保存して、会社のGitHub統計の研究をしてたんだ。

oxenを開発中で、gitやgit-lfsで直面した多くの問題を解決しようとしてるよ。gitをミラーリングするオープンソースのCLIとサーバーがあって、大きなファイルや何百万ものファイルを持つモノリポをもっとパフォーマンス良く扱えるんだ。興味があったらフィードバックもらえると嬉しいな! https://github.com/Oxen-AI/Oxen

ソフトウェアエンジニアリングのクラスでおすすめしてたのは、大きなファイル(メディアとか)をGitに入れるんじゃなくて、アーティファクトリポジトリ(Artifactoryとか)に入れること。そうすれば、ビルドシステムが自動で取得するスナップショット依存関係として公開できるし、どれだけの履歴を保持するかをコントロールできる。さらに、同僚には最新バージョンだけを取得させればいいから、古いバージョンが使ってたスペースを解放するためにビルドシステムのキャッシュを簡単にクリーンできるんだ。

クラスでCI/CDシステムのアーキテクチャを教えてる?最近雇ったジュニアエンジニアたちがそれを欠いてる気がするんだ。GitLabやArtifactory、CodeSonar、Anchoreなんかと結びつけて。

3Dモデル用にPlastic SCMの代替が本当に欲しいな。S3でもいいけど、ちょっと物足りないんだよね。