Unity のプロジェクトで Git LFS を使い始めた記録

Last updated 5 months ago

Git LFS を使いはじめた背景

結論から先に言うと、GitHub を使いたいけれど、リポジトリのサイズが大きくて扱いきれなかったからです。

これは会社での話なのですが、実は最初は Git リポジトリとして、 GitHub を使っていました。ただ、Asset Store のアセットをたくさん入れたりしている関係で、リポジトリのサイズが大きくなっていき、GitHub では容量・速度どちらでも辛くなってしまいました。(ちなみに、現在一番大きなリポジトリで 20GB くらいあります。)

そのため、BitBucket Server をプライベートクラウドに立てて使っていたのですが、ライセンス数が多くなると急に値段が上がってしまいます。さらに運用からも手を放したかったため、AWS の CodeCommit に移動しました。

CodeCommit はとても高速で維持費も安くよかったのですが、PR のマージが Fast Forward な状態でないとできなかったり、通知機能が弱かったり、コードレビューがしづらかったり、様々な問題がありました。

そこで、思い切って、レビューなどの機能が充実しており業界標準とも言える GitHub に、自前で立てた GitLFS サーバーを組み合わせて使い始めました。

サーバーの選択

まず、GitLFS のサーバーですが、できることなら東京リージョンに存在する何かしらの SaaS で済ませたかったのですが、見つけることができなかったため、自前で立てることにしました。

選んだのは AWS Lambda と S3 を使った、alanedwardes/Estranged.Lfs です。

これを選んだ理由としては、まだ会社が小さくインフラエンジニアがいないので、可能な限りマネージドなサービスが良い、つまり、Heroku や GAE で動くか FaaS が良いという視点で、公式のサーバー実装リストを見ていったところ、そもそもこれくらいしか選択肢が無かったからです。

なぜ他のはだめだったかというと、現在の Git LFS に対応していないものや、Git のホストと一緒になっている物(たとえば BitBucket や GitLab)ばかりだったためです。

ちなみに、現在の Git LFS に対応していない、というのはどういうことかというと、Git LFS はプロトコルが Legacy と Batch(v2) の2種類あり、そのうちの Batch に対応していなかったということです。なお、 Legacy API Support が消えたのは以下の PR です。

そうして、残ったのが alanedwardes/Estranged.Lfs だったわけです。

余談:Custom Transfer Agent

Git LFS には、LFS のファイル転送を自前で書けるCustom Transfer Agent と言う仕組みがあります。

これを使用した、sjansen/hoggle というツールがあり、これは LFS の通信を S3 への通信に置き換えて、いわばローカルで LFS サーバーの役割をやってしまうようなツールになっています。

この試み自体はとても素晴らしいと思うのですが、Windows だとうまく動かなかったり、Unity Cloud Build など他のサービスと組み合わせるときに対応が難しくなったりしてしまうので見送りました。

まずは作者の記事を読んでください。注意する点として、載っている CloudFormation のテンプレートはリージョンが違うため、そのままだと使用できません。

こちらの記事に従って、一番最後に載っている CloudFormation のテンプレートを使うことで、簡単に立てることができます。なお、中身が心配な方は自前で Lambda の関数の中身を準備して使うと良いと思います。

リポジトリでの設定

git lfs install

Git LFS のクライアントはもちろんインストール済みだと仮定します。 リポジトリ配下で以下のコマンドを叩いてフックをインストールします。

git lfs install

リポジトリルートに .lfsconfig ファイルを作る

これを作っておくと、リポジトリをクローンするときに自動的に LFS を使ってくれます。url は CloudFormation で作ったスタックの、出力タブに書いてあります。

.lfsconfig
[lfs]
url = https://**********************.execute-api.ap-northeast-1.amazonaws.com/lfs

なぜこれが必要かというと、git clone には LFS サーバーを指定するオプションがないためです。

デフォルトの挙動として.lfsconfig がない場合、Git LFS はリモートの URL から LFS サーバーの場所を推測して探そうとします。

そのとき、特定のパターンに従っていないと、LFSサーバーを見つけられず clone に失敗します。今回の場合、GitHub のアドレス、LFS サーバーの URL に全く関連性がないため、.lfsconfig が必要となります。

(新規リポジトリの場合だけ)LFS のトラック対象の追加

どのファイルを LFS の対象とするかを決めます。

やり方としては2種類あり、コマンドで叩く場合は、

git lfs track "*.png"
git lfs track "*.jpg"

ファイルを直接編集する場合は、.gitattributesというファイルを作ります。

.gitattributes
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text

と、ここで注意なのですが、なんと .gitattributes は Case Sensitive です(少なくとも LFS においては)。つまり、texture.pngは LFS 対象となりますが、 texture.PNG は LFS 対象となりません。

そのため、もし例えば Asset Store で買ったアセットを入れる予定があるなど、拡張子を小文字に揃えられない可能性がある場合には、以下のような書き方をするとよいそうです。(こちらはまだ未確認ですすみません)

git lfs track "*.[pP][nN][gG]"
git lfs track "*.[jJ][pP][gG]"
.gitattributes
*.[pP][nN][gG] filter=lfs diff=lfs merge=lfs -text
*.[jJ][pP][gG] filter=lfs diff=lfs merge=lfs -text

.gitattributes が準備できたら、あとはいつも通り add して commit して push するだけです。

既存リポジトリの LFS 化

いまのところ、20GB くらいあったリポジトリは、以下の手順で問題無く LFS 化することができました。なお、忘れやすいので先に上に書いてある .lfsconfig のコミットを積んでからのほうが良いと思います。

LFS 化には git lfs migrate というコマンドを使います。なお、コマンドの細かい使い方については公式のチュートリアルや、 git lfs migrate -h で見られるヘルプを見てください。

大きなファイルがどれかを調べる

リポジトリの移行の前に、まず大きなファイルを調べてみましょう。

なお、今回は全てを master ブランチ対象という前提で書きます。もし全ブランチを対象にしたい場合は、--everything オプションを検討してみてください。

現在のリポジトリで大きなファイルを調べるには以下のようなコマンドを叩きます。このコマンドでは 1MB 以上のファイルを持つ拡張子を抽出します。

git lfs migrate info --include-ref master --above 1MB

または、ファイル種別の合計サイズの大きい順も出せます。このコマンドでは上位 10 ファイル種別を出してくれます。

git lfs migrate info --include-ref master --top 10

過去のコミットも含めて LFS 対象にする

さきほどのコマンドによって、大きなサイズのファイルがわかります。そうしたら、それらをまとめて過去のコミットも含めて LFS 対象とします。

git lfs migrate import --include-ref master --include="*.cs,*.xml,*.mdb"

なお、新規リポジトリの場合自分で作った .gitattributes ですが、git lfs migrate import を使う場合には、過去のコミットからもともと .gitattributes があり、元々全てが LFS 対象だったかのようにコミットを書き換えてくれます。

なお、エラーが出た場合はもう一度同じコマンドをたたくとうまくいくと思います!

ポインターを正しくファイルにする

私が試したところ、git lfs migrate import を使うと、ワークツリーに LFS のポインターのファイルがそのまま置かれることがあります。そのため、一度ワークツリーを全て消して、git checkout .などで元に戻してあげることをおすすめします。

.git 内のお掃除

チュートリアルによると、これで LFS 化した元のオブジェクトが消えて、余分なファイルがなくなるそうです。

git reflog expire --expire-unreachable=now --all
git gc --prune=now

終わりに

まだ運用し始めて間もないので、なにか分かったらまた追記していきたいと思います。いまのところ順調に使えています!なお、株式会社ハシラスではUnityエンジニアを募集しております!詳しくは公式サイトをご覧ください!宣伝です!