GitLFSは遅いというイメージを払拭したい

これまで GitLFS は遅いだとか大量のファイル管理に向いていないという印象を持っていました。
ですが、まあ大きいバイナリを扱うし遅いのは遅いんですが、やり方によっては現時点でもギリギリ実務に耐えれる程度だと今では思っています。

というわけで、遅さをなんとかする方法をいくつか（少ないですが）書いてみたいと思います。

## GitLFSのファイル管理
その前に、GitLFS のファイル管理イメージを持っておくと、そのあとの理解がしやすいのでそこから説明します。

GitLFS は Git の [smudge/clean filter](https://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes#filters_a) の機能を使っています。

`git checkout` の際に GitLFS のトラッキング対象としているファイルが含まれていれば、smudge filter を介して GitLFS のストア先から実際のコンテンツを取得し、ポインタファイルをそのコンテンツに置き換えます。
<img src="https://qiita-image-store.s3.amazonaws.com/0/37435/2f8b3162-3988-63f7-97ef-5586ddac5a9d.png" width="450" />

```bash:ポインタファイル例
version https://git-lfs.github.com/spec/v1
oid sha256:4665a5ea423c2713d436b5ee50593a9640e0018c1550b5a0002f74190d6caea8
size 36
```

次に、`git add` の際に GitLFS のトラッキング対象としているファイルが含まれていれば、clean filter を介して GitLFS のストア先にある実際のコンテンツを更新し、そのコンテンツから算出したポインタファイルに置き換えます。
<img src="https://qiita-image-store.s3.amazonaws.com/0/37435/e27b1ab8-72f7-4354-55bf-8790af46b0f4.png" width="450" />

これにより、Git 上はポインタファイルだけが管理され、ローカルにはコンテンツそのものがあるという形を実現しています。

## なるべく最新のバージョンにする
できるだけ GitLFS のクライアントを最新のバージョンにしましょう。これはとても重要です。

1.0.0 だと push した時に壊れてしまうことがかなりの頻度で発生していたのですが（なぜか Object not found on the server. になったりする）、現時点で最新の 1.4.1 にしたらかなり安定しました。
1.3.0 でレジュームアップロード/ダウンロードができるようになったためかもしれません。

後述の `git lfs clone` も 1.2.0 で実装された機能です。

## git cloneは使わない
clone する際は `git clone` を使わずに、`git lfs clone` を使うようにしましょう。

```bash
git lfs clone git@github.com:shibukk/lfs-pull-test.git
```

普通に `git clone` した場合、smudge filter が一度に1つのファイルだけしか扱えないため、GitLFS のストアに対し大量のAPIコールが発生してしまいます。
`git lfs clone` はこれを解決するために用意されたコマンドで、smudge filter に依存しません。
なので、複数ファイルを並行ダウンロードすることができ、結果高速に clone できます。

## smudgeはskipする
git lfs 上にあるコンテンツが何らかの理由で破損している場合、git pull がエラーになってしまいます。
これでは困るので、壊れててもいいからポインタファイルだけを取得するようにしておきます。

```bash
git config filter.lfs.smudge "git-lfs smudge --skip %f"
# --globalオプションをつければ全てのリポジトリに対して設定される
```

このコマンドを実行しておくと smudge が skip されるようになります。

smudge によるコンテンツ置き換えが実行されないので、ローカルはポインタファイルのままですが、`git lfs pull` を実行すればコンテンツに置き換えてくれます。
これは同時に高速化も果たします。（clone と同様の理由です）

一点気をつけないといけないのが、こんな感じで手動でコマンドを実行しないといけないので、手順から抜けてしまう可能性があります。

```bash
# git pullする場合
git pull
git lfs pull

# git checkoutする場合
git checkout
git lfs pull # git lfs fetch; git lfs checkout; とほぼ等価
```

なので、githooks の `post-merge` と `post-checkout` に `git lfs pull` を仕込んでおくか、一連のコマンドをまとめたエイリアスを作っておくとよいです。

## 最後に
<del>これらの Tips は今後変わる可能性があります。
というのも、GitLFS は stream 読み込みなど意欲的な Pull Request が出されているからです。</del>

<del>[WIP: long running clean/smudge filter protocol](https://github.com/github/git-lfs/pull/1382)

<del>もっと早くなれば個人的にすごく嬉しいので、引き続き GitLFS の動向は追ってきたいと思います。</del>

2017.02.09 追記

<blockquote class="twitter-tweet" data-conversation="none" data-lang="ja"><p lang="en" dir="ltr">Git LFS checkout times are now up to 80x faster with support for the new filter-process mechanism in Git 2.11 <a href="https://t.co/MkfT8VzwRT">https://t.co/MkfT8VzwRT</a> <a href="https://t.co/J61mReGh0i">pic.twitter.com/J61mReGh0i</a></p>&mdash; GitHub (@github) <a href="https://twitter.com/github/status/803872412777385984">2016年11月30日</a></blockquote>

stream 読み込みの Pull Request が取り込まれて checkout はとても早くなっています。
こんな感じで GitLFS はどんどんよくなっていっています。
ここに記載している Tips はもう必要なくなっていたりしますが、今後も楽しみです！