Gitでバイナリや巨大なファイルを扱う場合に活躍するのが「LFS(Large File Storage)」。 詳細な説明は様々なページで行われていますので、今回はGitHubの利用を前提とした基本的な使い方についてだけまとめておきます。
簡単に言うとLFSを利用するとファイルの実際のデータは専用のストレージに、リポジトリ内部には200byteにも満たないハッシュ値等を保存することでリポジトリを軽量化することができるという仕組みです。
Gitはバイナリファイルに更新がある度に全データがリポジトリに保管されるため油断しているとあっという間にリポジトリが肥大化してしまいます。またGitHubでは通常100Mbyte以上のファイルをpushすることができません。それに加えリポジトリのサイズは1G程度が推奨されていることから「LFS(Large File Storage)」が必要とされているというわけです。
GitHub LFSでの制約
無料枠の範囲
本気で使ったらあっという間に1Gは使い切るので無料枠は本当にお試し用ですね。
- ストレージ容量
- 1GByteまで
- 転送量(月間)
- 1GByteまで
ストレージ・転送量ともに50GBを5ドル/月での購入が可能です。例えば月間15ドル払えば150GBの利用ができます。
最大ファイルサイズ
プランによって1ファイルあたるの最大容量が変わります。執筆時点では以下の通り。動画ファイルなどを扱う場合はうっかり超えちゃうかもしれませんね。
プラン | ファイルサイズ |
---|---|
GitHub Free | 2 GB |
GitHub Pro | 2 GB |
GitHub Team | 4 GB |
GitHub Enterprise Cloud | 5 GB |
最新の情報はドキュメントなどでご確認を。
インストール
Git本体はすでにインストールされているとします。この状態でまずは「Git LFS」を入れます。macOSであればHomeBrewなら一発です。WindowsやLinuxをお使いの場合は公式サイトからダウンロード後、インストールを行ってください。
$ brew install git-lfs
ここではv3.1.2が入りました。Git LFSってGo言語製なんですね。知らんかったw
$ git lfs --version git-lfs/3.1.2 (GitHub; darwin amd64; go 1.17.6)
新規リポジトリに導入する
ローカルリポジトリを作成
適当な名前のディレクトリを作成し、いつも通りgit init
を実行します。
$ mkdir foo; cd foo $ git init Initialized empty Git repository in /Users/katsube/foo/.git/
続けざまに今度はGitLFSをこのリポジトリに適用します。詳細は後述しますが~/.gitconfig
に設定が追加され、.git/hooks
にファイルが追加されます。これにより普通にGitを使うだけで、裏側では自動的にLFSが採用されるという環境になります。
$ git lfs install Updated Git hooks. Git LFS initialized.
LFSで管理する対象を設定
どのファイルをLFSに保存するか設定を行います。ここでは拡張子がPDFのファイルを対象としようとしています。特定のディレクトリ配下のみを対象としたい場合はdocuments/*.pdf
などのようにパスを記述することも可能です(拡張子を省略しすべてのファイルを対象とすることも可)。複数回実行することで複数のパターンを追加できます。
$ git lfs track '*.pdf' Tracking "*.pdf"
git lfs track
を実行するとファイル「.gitattributes」が新たに作成されます。このファイルを元にLFSに入れるかどうか判定されますので自分以外のユーザーも同様の環境で開発を行うことができるようになります。というわけでこのファイルもリポジトリへcommit/pushします。
$ cat .gitattributes *.pdf filter=lfs diff=lfs merge=lfs -text
リモートリポジトリを作成
今回はGitHub上に「lfstest」という名前のリポジトリを作成しました。特別な作業や設定は不要です。普通にリポジトリを新規作成してください。
先ほどローカルに作成したリポジトリのリモートとして登録しておきます。
$ git remote add origin git@github.com:katsube/lfstest.git
基本的な設定はここまでで終了です。
実際に使ってみる
めちゃめちゃ簡単です。
commitからのpush
LFSが正常に動くかどうかの実験を行いますので、git lfs track
で設定した拡張子のファイルを準備します。ファイルを適当な場所に配置したらstageへaddからのcommitを行います。
$ git add . $ git commit -m '1st commit'
あとは普通にリモートへpushするだけ。LFSが機能している場合はUploading LFS objects
の表示がされます(転送中は転送状況が表示されます)。最初にLFSの転送が発生している以外はいつもと変わらないですね。
$ git push Uploading LFS objects: 100% (1/1), 4.0 MB | 351 KB/s, done. Enumerating objects: 4, done. Counting objects: 100% (4/4), done. Delta compression using up to 8 threads Compressing objects: 100% (3/3), done. Writing objects: 100% (4/4), 595 bytes | 595.00 KiB/s, done. Total 4 (delta 0), reused 0 (delta 0), pack-reused 0 To github.com:katsube/lfstest.git * [new branch] main -> main
GitHub上で確認する
GitHub上では通常のファイルと変わらない感じで表示されていますが、LFSが適用されている場合はその旨の表示がされます。ちなみに今回登録したのは確定申告の準備中だったこともありAWSの請求書のPDFですw
現在LFSの容量をどの程度使っているかは画面右上のユーザーアイコンから「Settings」→「Billing and Plans」とたどることで確認できます。
その他
LFSで管理中のファイル一覧を確認
ls-files
で一覧表示が可能です。抜け漏れなどの確認に使えそうですね。
$ git lfs ls-files a8783c0130 * meisai.pdf
LFSで管理するファイルのパターンを削除
untrack
で削除することができます。.gitattributes
からも内容が削除されていることを確認できます。
$ git lfs untrack '*.pdf'
LFSで使用している容量を削減したい
GitHubのLFSでダイエットするにはリポジトリを丸ごと削除するしか無いようです。
Git LFS からファイルを削除した後でも、Git LFS オブジェクトはそのままリモートストレージに存在し、Git LFS ストレージ容量に対するカウントも継続します。 Git LFS オブジェクトをリポジトリから削除するには、リポジトリを削除して再作成します。
※公式ドキュメントより
つまり以下の手順を取ることになります。
- Gitのfilter-branchなどを利用し過去のcommitから特定のファイルを削除
- GitHub上に新規でリポジトリを作成、リモートとして登録
- 新規リポジトリへpushする
地獄!\(^o^)/
必要とする容量にもよりますが、追加のストレージは50GBで月5ドル程度なので、お仕事の場合は素直に課金した方が幸せになれそうですね。
すでにGitへ登録しているファイルをLFSで管理したい
こちらも一度消して改めて登録し直す必要があります。
Git LFS で追跡する必要があるファイルがすでにリポジトリにある場合は、まずそれをリポジトリから削除する必要があります。
※公式ドキュメントより
対象のファイルが多い場合は、許されるのであればダイエットと同様に新規にリポジトリを作った方が早いかもしれませんね。
git lfs install実行後の変化
install後にどのファイルが加わったのかメモしておきます。
.git/hooks
hooksには合計4つのファイルpost-checkout, post-commit, post-merge, pre-pushが追加されています。ファイルの中身を覗くとわかりますがGitでcommitやpushをした際にgit lfs
が自動的に呼び出される設定が行われています。
$ ls -la .git/hooks total 152 drwxr-xr-x@ 19 katsube staff 608 3 19 19:13 ./ drwxr-xr-x@ 10 katsube staff 320 3 19 19:13 ../ -rwxr-xr-x 1 katsube staff 478 3 19 19:12 applypatch-msg.sample* -rwxr-xr-x 1 katsube staff 896 3 19 19:12 commit-msg.sample* -rwxr-xr-x 1 katsube staff 4655 3 19 19:12 fsmonitor-watchman.sample* -rwxr-xr-x 1 katsube staff 282 3 19 19:13 post-checkout* -rwxr-xr-x 1 katsube staff 278 3 19 19:13 post-commit* -rwxr-xr-x 1 katsube staff 276 3 19 19:13 post-merge* -rwxr-xr-x 1 katsube staff 189 3 19 19:12 post-update.sample* -rwxr-xr-x 1 katsube staff 424 3 19 19:12 pre-applypatch.sample* -rwxr-xr-x 1 katsube staff 1643 3 19 19:12 pre-commit.sample* -rwxr-xr-x 1 katsube staff 416 3 19 19:12 pre-merge-commit.sample* -rwxr-xr-x 1 katsube staff 272 3 19 19:13 pre-push* -rwxr-xr-x 1 katsube staff 1374 3 19 19:12 pre-push.sample* -rwxr-xr-x 1 katsube staff 4898 3 19 19:12 pre-rebase.sample* -rwxr-xr-x 1 katsube staff 544 3 19 19:12 pre-receive.sample* -rwxr-xr-x 1 katsube staff 1492 3 19 19:12 prepare-commit-msg.sample* -rwxr-xr-x 1 katsube staff 2783 3 19 19:12 push-to-checkout.sample* -rwxr-xr-x 1 katsube staff 3650 3 19 19:12 update.sample*
~/.gitconfig
ホームディレクトリの.gitconfig
にfilter設定が追加されます。filterはcheckoutとaddする際に任意の処理を挟むことができる機能です。
$ cat ~/.gitconfig (中略) [filter "lfs"] process = git-lfs filter-process required = true clean = git-lfs clean -- %f smudge = git-lfs smudge -- %f