Git LFS 完全ガイド - 大容量ファイルをGitで効率的に管理する

Git LFS 完全ガイド - 大容量ファイルをGitで効率的に管理する

作成日:

Gitリポジトリに音声ファイルや画像などのバイナリファイルをコミットすると、リポジトリサイズが急激に膨らみ、clone や pull が遅くなります。Git LFS(Large File Storage)はこの問題を解決する公式の拡張機能です。

この記事では、Git LFS の仕組みから導入手順、自前Gitサーバーでの設定まで、実際のプロジェクトへの適用事例を交えて解説します。


Git LFS とは

Git LFS は、大容量ファイルの実体をGitリポジトリの外部に保存し、リポジトリ内には軽量なポインタファイル(約130バイト)のみを記録する仕組みです。

GitHub が開発し、2015年にリリースされました。現在は GitHub、GitLab、Bitbucket をはじめ、多くの Git ホスティングサービスが対応しています。

通常のGitとの比較

項目通常のGitGit LFS
バイナリの保存先.git/objects/ に直接格納LFS ストレージに分離
リポジトリ内の記録ファイル全体ポインタファイル(約130B)
clone 時全履歴の全ファイルをダウンロードポインタのみ取得、実体は必要時にダウンロード
差分管理バイナリ差分は非効率世代ごとに独立管理

ポインタファイルの中身

LFS で管理されたファイルは、Git 上では以下のようなテキストファイルとして記録されます。

LFS ポインタファイルの例
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 524288

git checkoutgit pull のタイミングで、LFS クライアントがこのポインタを読み取り、対応する実体ファイルを自動的にダウンロードして復元します。開発者は通常のファイルと同じ感覚で操作できます。


Git LFS が有効なケース

以下のようなファイルをリポジトリで管理する場合に Git LFS が効果的です。

  • 音声ファイル: .wav, .mp3, .ogg
  • 画像ファイル: .psd, .png, .jpg(高解像度)
  • 動画ファイル: .mp4, .mov
  • 機械学習モデル: .h5, .onnx, .pt
  • ビルド成果物: .zip, .tar.gz
  • フォントファイル: .woff2, .otf

逆に、テキストベースのファイル(ソースコード、JSON、Markdownなど)は通常の Git で十分であり、LFS にする必要はありません。


Git LFS の仕組み

アーキテクチャ

Loading diagram...

push 時の動作

  1. git add で対象ファイルをステージング
  2. .gitattributes のパターンに一致するファイルは、LFS の clean フィルタがポインタに変換
  3. git commit でポインタファイルがコミットされる
  4. git push 時に、Git の通常オブジェクト(ポインタ含む)に加えて、LFS オブジェクト(実体ファイル)がLFSストレージに転送される

pull / clone 時の動作

  1. git pull / git clone でポインタファイルが取得される
  2. LFS の smudge フィルタが自動実行される
  3. ポインタの oid(SHA-256 ハッシュ)をキーに、LFS ストレージから実体をダウンロード
  4. ワーキングツリーには実体ファイルが復元される

clean / smudge フィルタ

Git LFS の中核は、Git の filter 機能を利用した clean / smudge の仕組みです。

  • clean フィルタgit add 時): 実体ファイル → ポインタに変換
  • smudge フィルタgit checkout 時): ポインタ → 実体ファイルに復元

これらは .gitattributes に記述されたルールに基づいて自動的に動作します。

.gitattributes
data/tts/*.wav filter=lfs diff=lfs merge=lfs -text

この1行で、以下が設定されます。

属性意味
filter=lfsclean/smudge で LFS フィルタを使用
diff=lfsdiff 表示時に LFS 用の処理を使用
merge=lfsマージ時に LFS 用の処理を使用
-textテキスト変換(改行コード変換等)を無効化

導入手順

1. Git LFS のインストール

macOS(Homebrew)
brew install git-lfs
Ubuntu / Debian
sudo apt install git-lfs
CentOS / RHEL
sudo yum install git-lfs

2. Git LFS の初期化

インストール後、Git のグローバル設定に LFS フックを登録します。

Shell
git lfs install

このコマンドは ~/.gitconfig に以下を追加します。

~/.gitconfig に追加される設定
[filter "lfs"]
    clean = git-lfs clean -- %f
    smudge = git-lfs smudge -- %f
    process = git-lfs filter-process
    required = true

3. 追跡対象の設定

LFS で管理したいファイルパターンを git lfs track で指定します。

Shell
git lfs track "*.wav"
git lfs track "data/tts/*.wav"
git lfs track "assets/**/*.psd"

実行すると .gitattributes ファイルが作成(または更新)されます。このファイルは必ず Git にコミットしてください。

Shell
git add .gitattributes
git commit -m "Git LFS tracking を設定"

4. 通常どおり運用

あとは通常の Git 操作をするだけです。LFS 対象ファイルは自動的にフィルタされます。

Shell
git add data/tts/sample.wav
git commit -m "音声ファイルを追加"
git push

自前 Git サーバーでの利用(git-http-backend では動かない)

GitHub や GitLab を使っていれば LFS は標準で対応済みです。しかし、Apache + git-http-backend による自前 Git サーバーでは、git-lfs をインストールしただけでは LFS は動きません。

実際に起きたエラー

サーバーに git-lfs をインストールして push したところ、以下のエラーが発生しました。

HTTPS経由でのpush時のエラー
batch response: Repository or object not found:
  https://git.example.com/repo.git/info/lfs/objects/batch

git-http-backend は Git Smart HTTP プロトコル専用の CGI プログラムであり、LFS の Batch API(/info/lfs/objects/batch)には一切対応していません。LFS クライアントが送信する JSON ベースの REST API リクエストを処理できず、404 が返ります。

SSH 経由も試しましたが、こちらは git-lfs-authenticate コマンドが必要です。これは GitHub や GitLab のようなホスティングサービスが提供するもので、bare リポジトリの自前サーバーには存在しません。

SSH経由でのpush時のエラー
batch request: command not found: git-lfs-authenticate: exit status 127

なぜ動かないのか

Git LFS は Git 本体とは独立した HTTP API を持っています。push/pull 時の流れは以下のようになります。

  1. LFS クライアントが POST /repo.git/info/lfs/objects/batch を送信
  2. サーバーがオブジェクトのアップロード/ダウンロード用 URL を JSON で返す
  3. クライアントがその URL に対してファイルを転送する

git-http-backend はステップ 1 の時点で「そんなエンドポイントは知らない」と 404 を返して終わりです。

解決策

自前サーバーで LFS を使うには、LFS の Batch API を処理する別のサーバープロセスが必要です。

実装言語特徴
rudolfsRustDocker で簡単に起動、ローカル/S3 対応
GiftlessPythonプラグイン方式で柔軟
Soft ServeGoGit サーバーごと置き換え

Apache のリバースプロキシで /info/lfs/ パスだけ LFS サーバーに振り分ける構成が現実的です。

Apache 設定例(rudolfs をバックエンドに使う場合)
# LFS リクエストを rudolfs に転送(ScriptAlias より先に評価される)
ProxyPass "/repo.git/info/lfs/" "http://127.0.0.1:8080/repo.git/info/lfs/"
ProxyPassReverse "/repo.git/info/lfs/" "http://127.0.0.1:8080/repo.git/info/lfs/"
 
# 通常の Git 操作は既存の git-http-backend で処理
SetEnv GIT_PROJECT_ROOT /srv/git
SetEnv GIT_HTTP_EXPORT_ALL 1
ScriptAlias / /usr/lib/git-core/git-http-backend/

ただし、LFS サーバーの運用・管理コストが増えるため、ファイル数やサイズが許容範囲内であれば LFS を使わず通常の Git でバイナリを管理する選択肢も十分にあります。


便利なコマンド

LFS 管理下のファイル一覧

Shell
git lfs ls-files

追跡パターンの確認

Shell
git lfs track

LFS オブジェクトの状態確認

Shell
git lfs status

LFS オブジェクトの手動取得

clone 後に LFS オブジェクトが未取得の場合、明示的にダウンロードできます。

Shell
git lfs pull

特定ファイルだけ取得

Shell
git lfs pull --include="data/tts/*.wav"

LFS なしで clone(ポインタのみ)

CI 環境など、バイナリファイルが不要な場合は以下で高速に clone できます。

Shell
GIT_LFS_SKIP_SMUDGE=1 git clone https://example.com/repo.git

既存リポジトリへの導入(実践事例)

ここからは、実際にプロジェクトへ Git LFS を導入した事例を紹介します。

背景

きかんしゃトーマスのキャラクター図鑑 Web アプリで、TTS(Text-to-Speech)機能により生成した音声ファイル(WAV 形式)をリポジトリで管理する必要がありました。

項目
ファイル数約180件
合計サイズ約90MB
ファイル形式WAV(24kHz, 16bit, モノラル)
命名規則SHA-256ハッシュ値 .wav

音声ファイルはデプロイ先のサーバーにも必要で、git pull で取得できるようにしたい。しかし、WAV ファイルをそのまま Git に入れるとリポジトリが重くなる。そこで Git LFS の導入を試みました。

手順1: サーバー側に git-lfs を導入

リモートは Apache + git-http-backend による自前 Git サーバーです。

リモートサーバー
sudo apt install git-lfs
sudo git lfs install --system

手順2: ローカルで LFS を設定

ローカル
git lfs install
git lfs track "data/tts/*.wav"

生成された .gitattributes を確認します。

.gitattributes
data/tts/*.wav filter=lfs diff=lfs merge=lfs -text

手順3: 既存ファイルを LFS に移行

既に通常の Git オブジェクトとしてコミット済みのファイルがある場合、git lfs track を設定してからファイルを再度 add するだけで LFS に変換されます。

Shell
git add .gitattributes data/tts/
git commit -m "TTS音声キャッシュをGit LFSで管理"

ローカルでは git lfs ls-files で全ファイルが LFS 管理下にあることを確認できました。

git lfs ls-files の出力(一部)
6ed42bdc10 * data/tts/0230b9fd...b2c0.wav
bf17502834 * data/tts/04e94f92...d140.wav
8ea6f11030 * data/tts/06a23e58...d140.wav
...

手順4: push で失敗

Shell
git push origin master
エラー出力
batch response: Repository or object not found:
  https://git.example.com/repo.git/info/lfs/objects/batch
error: failed to push some refs to 'https://git.example.com/repo.git'

前述のとおり、git-http-backend は LFS の Batch API に対応していないため push が失敗しました。SSH 経由でも git-lfs-authenticate が存在せず同様に失敗します。

結論: LFS を断念し通常の Git で管理

LFS サーバー(rudolfs 等)を別途立てれば解決できますが、運用コストを考慮して LFS を解除し、WAV ファイルを通常の Git オブジェクトとして管理する方針に切り替えました。

LFS 解除の手順
# LFS 追跡を解除
git lfs untrack "data/tts/*.wav"
rm .gitattributes
 
# 履歴内の LFS ポインタを実体ファイルに戻す
git lfs migrate export --include="data/tts/*.wav" --everything
 
# 不要な LFS オブジェクトを削除
git reflog expire --expire-unreachable=now --all
git gc --prune=now
 
# LFS を無効化
git lfs uninstall
 
# 履歴が書き換わるため force push
git push --force origin master

ファイル数が約180件、合計約90MB 程度であれば、通常の Git でも十分に運用可能です。git clone の速度に体感できるほどの差はありませんでした。


注意点

.gitattributes は最初にコミットする

git lfs track を実行した後、.gitattributes先にコミットしてから対象ファイルを add するのがベストです。順序が逆だと、LFS フィルタが適用されない場合があります。

LFS 容量制限

ホスティングサービスによって LFS の容量制限が異なります。

サービス無料枠ストレージ無料枠帯域(月)
GitHub1 GB1 GB
GitLab5 GB帯域制限なし
Bitbucket1 GB帯域制限なし
自前サーバーディスク容量次第制限なし

自前サーバーの場合は容量制限がないため、大量のバイナリファイルも自由に管理できます。

CI/CD 環境での考慮

CI 環境で LFS ファイルが不要な場合は GIT_LFS_SKIP_SMUDGE=1 を設定して clone を高速化できます。LFS ファイルが必要な場合は、CI 環境にも git-lfs をインストールしておく必要があります。

git lfs migrate

過去の履歴に含まれる大容量ファイルを遡って LFS に変換するには git lfs migrate コマンドを使います。ただし、履歴の書き換えを伴うため、チームで共有しているリポジトリでは注意が必要です。

Shell
git lfs migrate import --include="*.wav" --everything

まとめ

Git LFS は大容量バイナリファイルの管理に有効な仕組みですが、利用環境によっては導入のハードルがあります。

Git LFS が適しているケース:

  • GitHub、GitLab、Bitbucket などの LFS 対応ホスティングを使っている
  • リポジトリに GB 単位のバイナリが含まれ、clone が著しく遅い
  • バイナリファイルの更新が頻繁で、差分履歴が膨らんでいる

LFS を使わなくてもよいケース:

  • 自前 Git サーバー(git-http-backend)で LFS サーバーを別途立てたくない
  • バイナリの合計サイズが数十〜100MB 程度で、clone 速度に実害がない
  • ファイルの更新頻度が低く、履歴の肥大化が限定的

自前サーバーで LFS を使うには git-http-backend とは別に LFS Batch API を処理するサーバー(rudolfs 等)が必要になります。その運用コストと、通常の Git でバイナリを管理するコスト(リポジトリサイズの増加)を天秤にかけて判断するのがよいでしょう。