
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との比較
| 項目 | 通常のGit | Git LFS |
|---|---|---|
| バイナリの保存先 | .git/objects/ に直接格納 | LFS ストレージに分離 |
| リポジトリ内の記録 | ファイル全体 | ポインタファイル(約130B) |
| clone 時 | 全履歴の全ファイルをダウンロード | ポインタのみ取得、実体は必要時にダウンロード |
| 差分管理 | バイナリ差分は非効率 | 世代ごとに独立管理 |
ポインタファイルの中身
LFS で管理されたファイルは、Git 上では以下のようなテキストファイルとして記録されます。
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 524288git checkout や git 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 の仕組み
アーキテクチャ
push 時の動作
git addで対象ファイルをステージング.gitattributesのパターンに一致するファイルは、LFS のcleanフィルタがポインタに変換git commitでポインタファイルがコミットされるgit push時に、Git の通常オブジェクト(ポインタ含む)に加えて、LFS オブジェクト(実体ファイル)がLFSストレージに転送される
pull / clone 時の動作
git pull/git cloneでポインタファイルが取得される- LFS の
smudgeフィルタが自動実行される - ポインタの
oid(SHA-256 ハッシュ)をキーに、LFS ストレージから実体をダウンロード - ワーキングツリーには実体ファイルが復元される
clean / smudge フィルタ
Git LFS の中核は、Git の filter 機能を利用した clean / smudge の仕組みです。
- clean フィルタ(
git add時): 実体ファイル → ポインタに変換 - smudge フィルタ(
git checkout時): ポインタ → 実体ファイルに復元
これらは .gitattributes に記述されたルールに基づいて自動的に動作します。
data/tts/*.wav filter=lfs diff=lfs merge=lfs -textこの1行で、以下が設定されます。
| 属性 | 意味 |
|---|---|
filter=lfs | clean/smudge で LFS フィルタを使用 |
diff=lfs | diff 表示時に LFS 用の処理を使用 |
merge=lfs | マージ時に LFS 用の処理を使用 |
-text | テキスト変換(改行コード変換等)を無効化 |
導入手順
1. Git LFS のインストール
brew install git-lfssudo apt install git-lfssudo yum install git-lfs2. Git LFS の初期化
インストール後、Git のグローバル設定に LFS フックを登録します。
git lfs installこのコマンドは ~/.gitconfig に以下を追加します。
[filter "lfs"]
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %f
process = git-lfs filter-process
required = true3. 追跡対象の設定
LFS で管理したいファイルパターンを git lfs track で指定します。
git lfs track "*.wav"
git lfs track "data/tts/*.wav"
git lfs track "assets/**/*.psd"実行すると .gitattributes ファイルが作成(または更新)されます。このファイルは必ず Git にコミットしてください。
git add .gitattributes
git commit -m "Git LFS tracking を設定"4. 通常どおり運用
あとは通常の Git 操作をするだけです。LFS 対象ファイルは自動的にフィルタされます。
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 したところ、以下のエラーが発生しました。
batch response: Repository or object not found:
https://git.example.com/repo.git/info/lfs/objects/batchgit-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 リポジトリの自前サーバーには存在しません。
batch request: command not found: git-lfs-authenticate: exit status 127なぜ動かないのか
Git LFS は Git 本体とは独立した HTTP API を持っています。push/pull 時の流れは以下のようになります。
- LFS クライアントが
POST /repo.git/info/lfs/objects/batchを送信 - サーバーがオブジェクトのアップロード/ダウンロード用 URL を JSON で返す
- クライアントがその URL に対してファイルを転送する
git-http-backend はステップ 1 の時点で「そんなエンドポイントは知らない」と 404 を返して終わりです。
解決策
自前サーバーで LFS を使うには、LFS の Batch API を処理する別のサーバープロセスが必要です。
| 実装 | 言語 | 特徴 |
|---|---|---|
| rudolfs | Rust | Docker で簡単に起動、ローカル/S3 対応 |
| Giftless | Python | プラグイン方式で柔軟 |
| Soft Serve | Go | Git サーバーごと置き換え |
Apache のリバースプロキシで /info/lfs/ パスだけ LFS サーバーに振り分ける構成が現実的です。
# 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 管理下のファイル一覧
git lfs ls-files追跡パターンの確認
git lfs trackLFS オブジェクトの状態確認
git lfs statusLFS オブジェクトの手動取得
clone 後に LFS オブジェクトが未取得の場合、明示的にダウンロードできます。
git lfs pull特定ファイルだけ取得
git lfs pull --include="data/tts/*.wav"LFS なしで clone(ポインタのみ)
CI 環境など、バイナリファイルが不要な場合は以下で高速に clone できます。
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 を確認します。
data/tts/*.wav filter=lfs diff=lfs merge=lfs -text手順3: 既存ファイルを LFS に移行
既に通常の Git オブジェクトとしてコミット済みのファイルがある場合、git lfs track を設定してからファイルを再度 add するだけで LFS に変換されます。
git add .gitattributes data/tts/
git commit -m "TTS音声キャッシュをGit LFSで管理"ローカルでは git lfs ls-files で全ファイルが LFS 管理下にあることを確認できました。
6ed42bdc10 * data/tts/0230b9fd...b2c0.wav
bf17502834 * data/tts/04e94f92...d140.wav
8ea6f11030 * data/tts/06a23e58...d140.wav
...手順4: push で失敗
git push origin masterbatch 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 追跡を解除
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 の容量制限が異なります。
| サービス | 無料枠ストレージ | 無料枠帯域(月) |
|---|---|---|
| GitHub | 1 GB | 1 GB |
| GitLab | 5 GB | 帯域制限なし |
| Bitbucket | 1 GB | 帯域制限なし |
| 自前サーバー | ディスク容量次第 | 制限なし |
自前サーバーの場合は容量制限がないため、大量のバイナリファイルも自由に管理できます。
CI/CD 環境での考慮
CI 環境で LFS ファイルが不要な場合は GIT_LFS_SKIP_SMUDGE=1 を設定して clone を高速化できます。LFS ファイルが必要な場合は、CI 環境にも git-lfs をインストールしておく必要があります。
git lfs migrate
過去の履歴に含まれる大容量ファイルを遡って LFS に変換するには git lfs migrate コマンドを使います。ただし、履歴の書き換えを伴うため、チームで共有しているリポジトリでは注意が必要です。
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 でバイナリを管理するコスト(リポジトリサイズの増加)を天秤にかけて判断するのがよいでしょう。