Git LFS 1.2: クローンをもっと高速に

Git LFS 1.2 が登場しました。そこで、クローンにかかる時間を 10 倍以上改善するちょっとしたヒントを紹介したいと思います!

少し前にアナウンスした通り、アトラシアンは GitHub やコミュニティ内の多くの方と協力して Git LFS の開発に取り組んでいます。私たちは、Git リポジトリに巨大ファイルを保存せざるを得ないという問題の解決に取り組んでいます。それはたとえばゲーム開発、メディア作成、グラフィックデザインなどを扱うチームが抱えている問題です。

当社の Bitbucket ServerSourceTree は、プロフェッショナルチームに Git LFS サポートなどの最高の Git ソリューションを提供しています。

先週、Git LFS は バージョン 1.2 のリリースを行うことで新たなマイルストーンに達しました。今思えば、驚くほど多くの機能を詰め込んだと思います! 今回リリースした機能の 1 つを本記事で時間をかけて紹介しようと思ったのですが、Git LFS を使用されている皆さんには全リリースノートをご参照頂くことをお勧めします (この中で私は @sinbad として登場します 🙂 )。

git lfs clone であっという間にクローニング

非常に巨大なリポジトリを使用されている方、特に Windows ユーザーにとって、間違いなく便利な新機能が以下の専用 LFS コマンドです。

rsz_code1

git lfs clone コマンドは、git clone と全く同じように使われ、すべての同じ引数を使用しますが、重要な違いが 1 つあります。それは非常に高速であるということです! ユーザーが持つファイルの数によっては、10 倍以上高速になる場合も実際にあります。

どのように? この魔法は何?

その違いは、ユーザーがリポジトリをクローンする時、いかに正確に LFS コンテンツが調べられダウンロードされるかにあります。Git LFS では、巨大ファイルコンテンツは Git リポジトリとは別に保存され、小さなポインタだけがそこにコミットされます。編集用に有効なワーキングコピーを手に入れるには、いくつかのプロセスでポインタを実際のファイルコンテンツに変え、必要に応じてそのコンテンツをダウンロードします。このタスクは通常スマッジフィルター (smudge filter) と呼ばれているものによって行われます。これはユーザーが自身のワーキングコピーに何かをチェックアウトするたびに実行されます。

smudge_filter

ユーザーが git clone を使用する通常の場合では、最初に git がリポジトリオブジェクトをダウンロードします。LFS の場合これはポインタデータです。それが終わると、最初のワーキングコピーを作成するためにデフォルトブランチがチェックアウトされ、スマッジフィルターが各 LFS ファイルに実行されます。次に必要に応じて持っていないコンテンツをダウンロードします。

ここまでは良いでしょう。問題は、Git が 見つけるすべての LFS に対して個々にスマッジフィルターを実行するのですが、残念なことに、いくつかの理由でこれはあまり上手くいかないということです。

  1. 通常 Git LFS はサーバの LFS API を使用し、複数のファイルを一度に要求します。しかし、スマッジフィルターは一度に 1 つのファイルだけ扱うため、ずっと多くの API コールを発生させることになります。
  2. API からの応答があった後、通常 Git LFS は複数ファイルを並行してダウンロードしますが、この場合はそうではありません。
  3. 別の Git LFS プロセスがスマッジフィルターを介してフォークされ、各ファイルがチェックアウトされます。これは特に Windows ではコストがかかります。Windows では、新プロセスの開始に多大なパフォーマンスコストを要するためです。

'git lfs clone’ はどのようにして解決しているのか

私たちはいくつものアプローチを試し、バージョン 1.2 でようやく決まった解決方法は、グループで賢明に取り組むということです。それにより Git コアのパッチができて、この機能は最近リリースされた Git 2.8 で使用されるとさらに高速になります。

ステップ 1: カスタム設定で ‘git clone’ を実行する

新しい git lfs clone コマンドは、最初に git clone を実行しますが、以下のように Git の -c オプションを使うことで LFS スマッジフィルターが無効になります。

rsz_code2

Git の変更に対処するため、使用している Git のバージョンによってこのコマンドは少し異なります。Git 2.8 以上が最適です。

つまり、クローン操作の checkout に関しては、Git LFS で追跡されるファイルがワーキングコピー内に作成されると、スマッジフィルターが呼び出されるのではなく、Git がこれらのファイルを見つけるとすぐにディスクに出力します。たとえば以下のような何かを指すポインタファイルです。

rsz_code3

ステップ 2: ‘git lfs pull’ で大量ダウンロード

クローンが完了すると、git lfs clone は次のステップに移ります。それは git lfs pull と同じタスクを実行して、ユーザーのワーキングコピーと実際の LFS コンテンツをダウンロードおよび配置します。これ自体は以下のような動作をする複合操作です。

  1. git lfs fetch と同様に必要な LFS コンテンツをダウンロードする。
  2. git lfs checkout によって LFS ポインタファイルと実コンテンツをユーザーのワーキングコピーに移動する。

Git LFS では、フェッチングの概念はコア Git と変わりません。つまり必要なコンテンツをダウンロードしますが、ワーキングコピーは変更しません。git lfs clone を使うとこれはまとめて行われるので、LFS API の呼び出しが少なくなり、複数ファイルを並行してダウンロードします。これにより、ファイル毎のスマッジアプローチよりもずっと高速になります。

フェッチ操作は通常、現在のチェックアウトに必要な LFS コンテンツのダウンロードのみを行います。しかし、「最新」のコンテンツをフェッチするように Git LFS を設定すれば、他のコンテンツもダウンロードして最新ブランチへの変更やその後のコミットが高速になります。git lfs help fetch を実行して、「最近の変更」セクションで多くの情報をご確認ください。

まだあります

今後私たちは、クローン以外のケース、たとえば Git の変更のプルやブランチの切り替えなどのスマッジフィルターの処理が遅くなるところで同様の性能向上ができないか考える予定です。クローニングは極めてコストがかかる処理でしたが、それだけに最初に取り組むことは意味がありました。

この高速クローンをすぐに利用できるように SourceTree を更新します。そのためユーザーはこれを利用するために特に何かをする必要はありません。

まとめ

本記事に興味を持って頂ければ幸いです! Git LFS をチェックすることを強くお勧めします。また、今後もさらに便利な機能を提供したいと考えております。




*本ブログは Atlassian Developers の翻訳です。本文中の日時などは投稿当時のものですのでご了承ください。
*原文 : 2016 年 4 月 19 日 "Git LFS 1.2: Clone Faster"