Git バンドルは、 パック・ファイルと追加のメタ・データを格納するファイルです。 これには、 一連の ref と (場合によっては空の) 必要なコミットのセットが含まれます。 詳細については、 git-bundle(1) および gitformat-bundle(5) を参照してください。
バンドル URI は、 remote からバンドル以外の残りのオブジェクトをフェッチするのに先立って、 オブジェクト・データベースをゼロから立ち上げる(bootstrap)ために、 Git が 1 つ以上のバンドルをダウンロードできる場所です。
最大の目標(one goal)として、 orogin サーバーへのネットワーク接続が不十分なユーザーのクローンとフェッチを高速化することを目指しています。 別の利点としては、 CI ビルド・ファームなどのヘビー・ユーザーが Git データの大部分にローカル・リソースを使用できるようにすることで、 origin サーバーの負荷を軽減できることです。
バンドル URI 機能を有効にするには、ユーザーがコマンドライン・オプションを使用してバンドル URI を指定するか、 origin サーバーがプロトコル v2 機能を介して 1 つ以上の URI を広告(advertise)します。
Design Goals
バンドル URI 標準は、 複数の作業(workload)を満たすのに十分な柔軟性を目指しています。 バンドル提供側(bundle provider)と Git クライアントには、 バンドル URI を作成および使用する方法についていくつかの選択肢があります。
-
バンドルには、 サーバーが望む名前を付けることができます。 この名前によって、 バンドル・コンテンツのハッシュを使用して不変データを参照できます。 ただし、 これは、 コンテンツが更新されるたびに新しい URI が必要になることを意味します。 これは、 サーバーが URI を広告(advertise)している (そしてサーバーが新しいバンドルが生成されていることを認識している) 場合は許容できますが、 コマンド・ライン・オプションを使用するユーザーにとっては使いやすくありません(not ergonomic)。
-
バンドルは、 完全なクローンをゼロから作成(bootstrap)するために特別に編成できますが、 増分フェッチを始める(bootstrap)目的で編成することもできます。 バンドル提供側は、 増分フェッチ中のクライアントのダウンロードを最小限に抑えるために、 いくつかの構成スキーム(organization schemes)のいずれを使うかを決定する必要がありますが、 Git クライアントは、これらの操作のいずれかにバンドルを使用するかどうかを選択することもできます。
-
バンドル提供側は、 完全なクローン(full clones)や部分的なクローン(partial clones)またはその両方をサポートすることを選択できます。 クライアントは、 リポジトリの部分クローン・フィルタに適したバンドルがあれば、それを検出できます。
-
バンドル提供側は、 単一のバンドル(クローンのみ)またはバンドルのリストを使用できます。 バンドルのリストを使用する場合、 提供側は、 クライアントが完全なクローン(full clone)のためにバンドル URI の「全て(all)を必要とするか」、 あるいはバンドル URI の「いずれ(any)かで十分か」どうか、を指定できます。 これにより、 バンドル提供側は異なる地域に対して異なる URI を使用できます。
-
バンドル提供側は、 作成トークン(creation token)などのヒューリスティックを使用してバンドルを整理し、 クライアントが不要なバンドルをダウンロードしないようにすることができます。 バンドル提供側がこれらのヒューリスティックを提供しない場合は、 クライアントは最適化を使用して、 ダウンロードされるデータの量を最小限に抑えることができます。
-
バンドル提供側を Git サーバーに関連付ける必要はありません。 クライアントは、Git サーバーによって広告(advertise)れることなく、 バンドル提供側を使用することを選択できます。
-
クライアントは、 Git サーバーによって広告(advertise)されるバンドル提供側を検出することを選択できます。 これは、
git clone
中やgit fetch
中またはその両方で発生する可能性があり、 どちらでも発生しない可能性があります。 ユーザーは、 自分に最適な組み合わせを選択できます。 -
クライアントは、 いつでもバンドル提供側を手動で構成することを選択できます。 クライアントは
git clone
のコマンドライン・オプションとして、バンドル提供側を手動で指定することもできます。
各リポジトリは異なり、そして、各Git サーバーごとに異なるニーズがあります。 うまくいけば、 バンドル URI 機能は、 すべてのニーズを満たすのに十分柔軟です。 そうでない場合は、 バージョン管理メカニズムを通じて機能を拡張できる訳です。
Server requirements
バンドル・サーバーのサーバー側実装を提供するために、 Git プロトコルの他の部分は必要ありません。 これにより、 サーバー管理者は CDN などの静的コンテンツ・ソリューションを使用してバンドル・ファイルを提供できます。
バンドル URI 機能の現在のスコープでは、 すべての URI が HTTP(S) URL であると想定され、 その URL への GET リクエストを使用してコンテンツがローカル・ファイルにダウンロードされます。 サーバーは、 安全なアクセスのために構成された資格情報ヘルパー(configured credential helper)をトリガーする目的で、 これらのリクエストに認証要件(authentication requirement)を含めることができます。 (将来の拡張では、 file://
URI または SSH URI を使用できるようになる予定です。)
サーバーからの 200 OK
応答が得られたとして、 URL の指すコンテンツを調べます。 最初に、 Git はファイルをバージョン 2 以降のバンドル・ファイルと見なしてパースしようとします。 ファイルがバンドルでない場合、 ファイルは Git の構成パーサー(config parser)を使用してプレーン・テキスト・ファイルと見なしてパースし、 その構成ファイルのキーと値のペアは、バンドル URI のリストを記述することが期待されます。 これらのパースの試みがいずれも成功しない場合、 Git は、 バンドル URI が誤ったデータを提供したというエラーをユーザーに報告します。
サーバーによって提供されるその他のデータは、エラーと見なされます。
Bundle Lists
Git サーバーは、一連の key=value
ペアを使用してバンドル URI を広告(advertise)できます。 バンドル URI は、 これらの同一の key=value
ペアを含む Git 構成形式のプレーン・テキスト・ファイルを提供することもできます。 どちらの場合も、 これを バンドル・リスト(bundle list) と見なします。 key=value
ペアは、 クライアントがダウンロードするバンドルと無視するバンドルを決定するために使用できる、 バンドルに関する情報を指定します。
いくつかのキーは、 リスト自体の属性(property)に焦点を当てています。
- bundle.version
-
(必須)この値は、 バンドル・リストのバージョン番号を提供します。 将来の Git の変更により、 Git クライアントがバンドル・リスト・ファイル内の新しいキーに対応する必要がある機能が有効になる場合、 このバージョン番号はインクリメントされます。 現在のバージョン番号は 1 だけです。 それ以外の値が指定されている場合、 Git はこのファイルを使用できません。
- bundle.mode
-
(必須) この値は、
all
とany
のどちらかです。all
が指定されている場合、 クライアントは、 リポジトリの要件に一致する、 リストされているすべてのバンドル URI が必要であると想定する必要があります。any
が指定されている場合、 クライアントは、 リポジトリの要件に一致するバンドル URI のいずれかで十分であると想定する必要があります。 通常、any
オプションは、異なる地域にある多数の異なるバンドル・サーバーを一覧表示するために使用されます。 - bundle.heuristic
-
この文字列値のキーが存在する場合、 バンドル・リストは増分
git fetch
コマンドで適切に機能するように設計されています。 heuristic は、 クライアントがダウンロードする必要があるバンドルのサブセットを決定するのに役立つ、 バンドルごとに使用可能な追加のキーがあることを示します。 現在策定されている唯一の heuristic はcreationToken
です。
以下のキー達には、 利用可能な各バンドルのサーバー指定名である <id>
セグメントが含まれます。 <id>
には、英数字(alphanumeric)と -
文字のみを含める必要があります。
- bundle.<id>.uri
-
(必須)この文字列値は、 バンドル
<id>
をダウンロードするための URI です。 URI がプロトコル(http://
またはhttps://
)で始まる場合、絶対(absolute)URI です。 それ以外の場合、 URI は、 バンドル・リストに使用される URI への相対(relative)として解釈されます。 URI が/
で始まる場合、その相対パスは、 バンドル・リストに使用されるドメイン名からの相対パスです。 (この相対パスの使用は、異なるドメイン名を持つ多数のサーバーまたは CDN にバンドルの組を簡単に配布できるようにすることを目的としています。) - bundle.<id>.filter
-
この文字列値は、 このバンドルのヘッダーにも表示されるオブジェクト・フィルターを表します。 サーバーはこの値を使用して、 異なる種類のバンドルを区別し、 そこからクライアントが自分のオブジェクト・フィルタに一致するものを選択できるようにします。
- bundle.<id>.creationToken
-
この値は、 リストのバンドルをソートするために使用される非負(nonnegative)の 64 ビット整数です。 これは、
bundle.heuristic=creationToken
の場合において、フェッチ中にバンドルのサブセットをダウンロードするために使用されます。 - bundle.<id>.location
-
この文字列値は、 バンドル URI が提供される実際の場所(real-world location)を広告(advertise)します。 これは、 どのバンドルURIを使うかというオプションをユーザーに提示するため、 あるいは、単にGitによってどのバンドルURIが選択されたかという情報インジケーターとして使用することができます。 これは、 bundle.mode の値が
any
の場合にのみ有効です。
Git 構成形式(Git config format)を使用したバンドル・リストの例を以下に示します:
[bundle]
version = 1
mode = all
heuristic = creationToken
[bundle "2022-02-09-1644442601-daily"]
uri = https://bundles.example.com/git/git/2022-02-09-1644442601-daily.bundle
creationToken = 1644442601
[bundle "2022-02-02-1643842562"]
uri = https://bundles.example.com/git/git/2022-02-02-1643842562.bundle
creationToken = 1643842562
[bundle "2022-02-09-1644442631-daily-blobless"]
uri = 2022-02-09-1644442631-daily-blobless.bundle
creationToken = 1644442631
filter = blob:none
[bundle "2022-02-02-1643842568-blobless"]
uri = /git/git/2022-02-02-1643842568-blobless.bundle
creationToken = 1643842568
filter = blob:none
この例では、 bundle.mode=all
はもちろん、 bundle.<id>.creationToken
ヒューリスティックも使用しています。 また、 bundle.<id>.filter
オプションを使用して、 バンドルの 2 つの並列セットを提示します。 1 つは完全なクローン用(full clone)で、 もう 1 つはブロブn無し部分クローン用(blobless partial clones)です。
このバンドル・リストは URI https://bundles.example.com/git/git/
で見つかったので、2 つのブロブ無しバンドル(blobless bundles)には以下の完全に展開された URI があると見なします。
-
https://bundles.example.com/git/git/2022-02-09-1644442631-daily-blobless.bundle
-
https://bundles.example.com/git/git/2022-02-02-1643842568-blobless.bundle
Advertising Bundle URIs
ユーザーがクローンしているリポジトリのバンドル URI を知っている場合、 コマンドライン・オプションを使用してその URI を手動で指定できます。 けれども、 Gitホストとしては、 クローン操作中にバンドル URI を広告(advertise)して、 この機能を知らないユーザーを支援したいと思うかもしれません。
この機能に必要なのは、 サーバーが 1 つ以上のバンドル URI を広告(advertise)可能であることだけです。 この広告(advertisement)は、 バンドル URI を検出するために、特別に、新しいプロトコル v2 機能の形式を取ります。
クライアントはオプションとして任意(arbitrary)のバンドル URI を選択するか、または、いくつかの探索的チェック(exploratory checks)によって最高のパフォーマンスを持つ URI を選択できます。 複数の URI を持つことが、 サーバー側のインフラストラクチャを通じて地理的に分散された単一の URI よりも望ましいかどうかを判断するのは、バンドル提供側次第です。
Cloning with Bundle URIs
バンドル URI の主な需要はクローンを高速化することです。 Git クライアントは、 以下の流れに従ってバンドル URI と対話します:
-
ユーザーが
--bundle-uri
コマンドライン・オプションを使用してバンドル URI を指定するか、 クライアントが Git サーバーによって広告(advertise)されたバンドル・リストを検出します。 -
バンドル URI からダウンロードされたデータがバンドルの場合、 クライアントはバンドル・ヘッダーを調べて、 前提条件のコミット OID がクライアント・リポジトリに存在することを確認します。 一部が欠落している場合、 クライアントは他のバンドルがバンドル解除(unbundle)されるまでそのバンドルのバンドル解除(unbundle)を遅らせ、 それらの OID が存在するようにさせます。 必要な OID がすべて存在する場合、 クライアントは refspec を使用してそのデータをバンドル解除します。 デフォルトの refspec は
+refs/heads/*:refs/bundles/*
ですが、これは構成で変更できます。 これらの ref は保存されるため、 その後のgit fetch
ネゴシエーションでバンドルされた各 ref を「所持している」ものとして通信でき、 Git プロトコルでのフェッチのサイズを削減できます。 この ref 名前空間から ref を刈り取る(prune)ために、 Gitは古いバンドル ref を削除できるような番号付きの名前空間(refs/bundles/<i>/*
など)を導入することがあります。 -
ファイルが代わりにバンドル・リストである場合、 クライアントは
bundle.mode
を調べて、リストがall
とany
のどちらの形式であるかを確認します。-
bundle.mode=all
の場合、 クライアントは全バンドル URI を考慮します。 クライアント・リポジトリの部分(partial)クローン・フィルターに一致するbundle.<id>.filter
オプションに基づいてリストが削られます。 それから、 全バンドル URI を要求します。bundle.<id>.creationToken
ヒューリスティックが提供されている場合、 バンドルは作成トークン(creationToken)の降順でダウンロードされ、 バンドルに必要な OID がすべて揃った時点で停止(stop)します。 その後、 作成トークンの昇順でバンドルをバンドル解除(unbundle)できます。 クライアントは、 バンドル・リストがより大きな作成トークンを含むバンドルを広告(advertise)しない場合、 将来のダウンロードを回避するためのヒューリスティックとして最新の作成トークンを保存します。 -
bundle.mode=any
の場合、 クライアントは調べたバンドル URI のいずれかを選択できます。 クライアントは、 さまざまな方法でこれらの URI を選択できます。 最初の選択で結果が返されなかった場合、 クライアントは別の URI にフォールバックすることもできます。
-
注意: クローン中はすべてのバンドルが必要になることが予想されます。 また、 bundle.<uri>.creationToken
などのヒューリスティックを使用して、 バンドルを時系列または並列でダウンロードできます。
特定のバンドル URI が bundle.heuristic
値を持つバンドル・リストである場合、 クライアントはその URI を選択したバンドル URI として保存することを選択できます。 その後、クライアントは、 後の git fetch
呼び出し中にその URI に直接移動できます。
クライアントは、 バンドル URI をダウンロードするとき、 コンテンツ全体のダウンロードをお願いする前に、 最初のコンテンツを調査することを選択できます。 これにより、 URI がバンドル・リストかバンドルかを判断するのに十分な情報が得られる場合があります。 バンドルの場合、 クライアントはバンドル・ヘッダーを調べて、 広告(advertise)されたすべての先端(tips)がクライアント・リポジトリに既にあることを確認し、 残りのダウンロードをキャンセルすることができます。
Fetching with Bundle URIs
クライアントが新しいデータをフェッチするとき、 origin のリモートから取得する前に、バンドル・サーバーから取得するよう決めておく事ができます。 これはコマンド・ライン・オプションを介して行うこともできますが、 クローン中に指定したような構成値を使用する方が便利な場合があります。
フェッチ操作は、 バンドル・リストからバンドルをダウンロードするのと同じ手順に従います(ただし、 私達は、ここでは並列ダウンロードを使用することを「望んでいません」)。 薄いバンドル(thin bundle)内のすべての前提条件のコミット OID がオブジェクト・データベースに既にある場合、 私達は処理の終了(end)を期待します。
creationToken
ヒューリスティックを使用する場合、 それらの作成トークン(creation tokens)が格納されている作成トークン(creation tokens)以下の数値の場合、 クライアントはバンドルのダウンロードを回避できます。 新しいバンドルをフェッチした後、 Git はこのローカル作成トークン(creation token)を更新します。
バンドル提供側(bundle provider)がヒューリスティックを提供しない場合、 クライアント・リポジトリにバンドル先端(bundle tips)が既に存在する場合に、 クライアントは全バンドル・データをダウンロードする前にバンドル・ヘッダーの調査を試みるべきです。
Error Conditions
バンドル URI またはその場所で見つかったバンドル・リストに従って情報をダウンロードしているときに、 Git クライアントが予期しないものを発見した場合、 Git はそのデータを無視して、 バンドル URI が指定されていないかのように処理を続行できます。 バンドル URI ではなく、リモート Git サーバーは、最終的に信頼できる情報源です。
以下にエラー状態の例をいくつか示します:
-
クライアントが指定された URI でサーバーとの接続に失敗するか、または、接続が失われて回復する機会がありません。
-
クライアントは 400番台のレスポンス (
404 Not Found
や401 Not Authorized
など) を受け取りました。 クライアントは、 資格情報ヘルパー(credential helper)を使用して URI の資格情報(credential)を見つけて提供する必要がありますが、 特定の 400番台のエラーの処理に関しては、 Git の他の HTTP プロトコルのセマンティクスと一致させる必要があります。 -
サーバがその他の「失敗」応答(failure response)を返しました。
-
クライアントが受け取ったデータがバンドルまたはバンドル・リストとしてパースできませんでした。
-
バンドルに期待したものでないフィルターが含まれています。
-
前提条件のコミット OID がオブジェクト・データベースになく、 ダウンロードするバンドルがこれ以上ないため、 クライアントはそのバンドルをバンドル解除(unbundle)できません。
また、以下は無駄だと見なされる状況な事もありますが、エラー状態ではありません:
-
ダウンロードされたバンドルには、 クローンまたはフェッチ要求によって要求されたよりも多くの情報が含まれています。 あるあるな例として、 ユーザーが
--single-branch
を使用してクローンを要求するが、 すべてのrefs/heads/*
参照から到達可能なすべてのコミットを格納するバンドルをダウンロードする場合です。 これは最初は無駄かもしれませんが、おそらくこれらのオブジェクトは、 クライアントが気にする後々 ref 更新によって到達可能オブジェクトに成る事でしょう。 -
git fetch
中のバンドルのダウンロードには、 オブジェクト・データベースに既にあるオブジェクトが含まれています。 リモート・サーバーへの「追随」(catch-up)フェッチを実行した時は、 ほとんどの場合、 クライアントはバンドル・サーバーよりもわずかに先行するため、 フェッチにバンドルを使用している場合、 これはおそらく避けられません。 この余分な作業は、 サーバーがバンドルを計算するよりも、クライアントがはるかに頻繁にフェッチしている場合に最も無駄になります。 たとえば、 サーバーは週イチでバンドルを計算のに、 クライアントがバックグラウンド・メンテナンスで 1 時間ごとのプリフェッチを行っている場合です。 このため、サーバーがbundle.heuristic
値を通じて明示的に推奨しない限り、 クライアントは fetch にバンドル URI を使用しないでください。
Example Bundle Provider organization
バンドル URI 機能は、バンドル提供側(bundle provider)がオブジェクト・データを編成するさまざまな方法に柔軟に対応できるように意図的に設計されています。 ただし、 以下で説明する完全な編成モデルを用意しておけば、 提供側(provider)がそのベースから始める事ができるので、役に立つことがあります。
この編成例は、 GVFS キャッシュ・サーバー (このドキュメントの最後のセクションを参照) で使用されるものの単純化されたモデルであり 、Git 以外の追加のソフトウェアを使用していますが、 非常に大きなリポジトリ(very large repositories)のクローンとフェッチを高速化するのに役立ちました。
バンドル提供側(bundle provider)は、 サーバーを複数の地域に展開します。 各サーバーは独自のバンドル・セットを管理します。 サーバーは多数の Git リポジトリを追跡できますが、 バンドル・リストの提供は各々のパターンに基づいています。 たとえば、 https://<domain>/<org>/<repo>
でリポジトリをミラーリングする場合、 バンドル・サーバーはそのバンドル・リストを https://<server-url>/<domain>/<org>/<repo>
で使用できるようにすることができます。 origin の Git サーバーは、 これらすべてのサーバーを any
モードでリストできます:
[bundle]
version = 1
mode = any
[bundle "eastus"]
uri = https://eastus.example.com/<domain>/<org>/<repo>
[bundle "europe"]
uri = https://europe.example.com/<domain>/<org>/<repo>
[bundle "apac"]
uri = https://apac.example.com/<domain>/<org>/<repo>
この「リストのリスト」は静的であり、 バンドル・サーバーが追加または削除された場合にのみ変更されます。
各バンドル・サーバーは、 独自のバンドルの組を管理します。 最初のバンドル・リストには、 単一のバンドルのみが含まれており、 origin サーバーからリポジトリをクローンして受け取ったすべてのオブジェクトが含まれています。 リストは creationToken
ヒューリスティックを使用し、 creationToken
はサーバーのタイムスタンプに基づいてバンドル用に作成されます。
バンドル・サーバーは、 1 日 1 回など、 定期的にスケジュールされたバンドル・リストの更新を実行します。 このタスクの処理中、 サーバーは origin サーバーから最新のコンテンツをフェッチし、 最新の origin ref から到達可能であるが、 以前に計算されたバンドルには含まれていないオブジェクトを含むバンドルを生成します。 このバンドルは、 creationToken
値が以前の最大 creationToken
値より必ず大きくなるように注意してリストに追加されます。
バンドルリストが大きくなりすぎたとき、 つまり、 バンドルの数が30を超えると、 「N - 30」(N minus 30)より古いバンドル達を1つのバンドルにまとめます。 このバンドルの creationToken
は、 まとめられたバンドルの中の最大の creationToken
と等しくなります。
以下にバンドル・リストの例を示しますが、日毎のバンドルは2つしかなく、リストは30個にはとうてい足りていません:
[bundle]
version = 1
mode = all
heuristic = creationToken
[bundle "2022-02-13-1644770820-daily"]
uri = https://eastus.example.com/<domain>/<org>/<repo>/2022-02-09-1644770820-daily.bundle
creationToken = 1644770820
[bundle "2022-02-09-1644442601-daily"]
uri = https://eastus.example.com/<domain>/<org>/<repo>/2022-02-09-1644442601-daily.bundle
creationToken = 1644442601
[bundle "2022-02-02-1643842562"]
uri = https://eastus.example.com/<domain>/<org>/<repo>/2022-02-02-1643842562.bundle
creationToken = 1643842562
origin サーバーで到達不能になったにもかかわらず、 永続的にオブジェクト・データを保存および提供することを避けるために、 このバンドルのマージをより慎重に行うことができます。 古いバンドル達の絶対結合(absolute union)を得る代わりに、 より新しいバンドル達を調べて、 このマージされたバンドルで必要なコミットが全て利用可能であることを確認することでバンドルを作成できます。 これにより、 この期間内に新しいコミット達で使用されていないオブジェクト・データを「期限切れにする」ことができます。 そのデータは、その後のプッシュによって再導入される可能性があります。
このデータ編成は主に2つの事を意図しています。 ひとつ目は、 リポジトリの最初のクローンは、 より近いソースから事前計算(precompute)されたオブジェクト・データをダウンロードすることで高速に行われます。 ふたつ目は、 特に、 クライアントが数日間フェッチしていない場合は、 git fetch
コマンドの方が高速ですが、 クライアントが 30 日間フェッチしないと、 バンドル・リストの編成により、 大量のオブジェクト・データが再ダウンロードされます。
頻繁にフェッチするユーザーにとってこの編成をより便利にする方法のひとつは、 より頻繁にバンドルを作成することです。 たとえば、 バンドルを 1 時間ごと(every hour)に作成し、 1 日に 1 回これらの「毎時」(hourly)バンドルを、「日次」(daily)バンドルにマージすることができます。 日次(daily)バンドルは、 30 日後に「最古のバンドル」(the oldest bundle)にマージされます。
このリポジトリのクライアントがブロブ無し部分クローン(blobless partial clones)を使用することを期待している場合は、 このバンドル戦略を blob:none
フィルターで繰り返すことをお勧めします。 この ブロブ無しバンドル(blobless bundle)のリストは、 完全なバンドル(full bundle)と同一のリストに入りますが、 bundle.<id>.filter
キーを使用することで 2 つのグループに分離します。 非常に大きなリポジトリの場合、 バンドル提供側(bundle provider)はブロブ無しバンドル(blobless bundle)のみを提供したい場合があります。
Implementation Plan
この設計ドキュメントは、 いくつかのパッチ・シリーズの過程で言及されたすべてのクライアント機能を実装することを目標として、 野心的なドキュメントとして単独で提案されています。 提案に値する見込みのある機能(概要)は以下のとおり:
-
--bundle-uri
オプションを使用して、バンドル URI をgit clone
に統合します。 これには、git clone
の下位実装として使用するための新しいgit fetch --bundle-uri
モードが含まれます。 この初期バージョンは、 与えられた URI で単一のバンドルが指定されること期待しています。 -
バンドル URI からバンドル・リストをパースする機能を実装し、
bundle.mode
オプションを適切に区別するためにgit fetch --bundle-uri
ロジックを更新します。 設定形式(config format)のパースでキーと値のペアのリストがバンドル・リスト・ロジックに送られるように、 機能を具体的に設計します。 -
bundle-uri
プロトコル v2 コマンドを作成して、 Git サーバーがキーと値のペアを使用してバンドル URI を広告(advertise)できるようにします。 バンドル・リスト・ロジックへの既存のキー値入力に組み込みます。git clone
がこれらのバンドル URI を検出し、 バンドル・データからクライアント・リポジトリをゼロから作成(bootstrap)できるようにします。 (この選択は、 構成オプションとコマンドライン・オプションにより、 ユーザーに許可を求める方式(opt-in) です。) -
クライアントが
bundle.heuristic
構成キーとbundle.<id>.creationToken
ヒューリスティックを理解(understand)できるようにします。git clone
がbundle.heuristic
でバンドル URI を検出すると、 その後のgit fetch <remote>
コマンドでそのバンドル URI をチェックするようにクライアント・リポジトリを設定します。 -
クライアントが
git fetch
中にバンドル URI を検出できるようにし、bundle.heuristic
が設定された場合は後でフェッチするためにバンドル URI を設定できるようにします。 -
bundle.<id>.creationToken
ヒューリスティックが利用できない場合にデータのダウンロードを減らすために、「ヘッダー調査」(inspect headers)ヒューリスティックを実装します。
これらの機能が見直されるにつれて、 この計画は更新される可能性があります。 また、 この機能が成熟し、 実際のシナリオで使用されるようになるにつれて、 新しい設計が発見され、 実装されることも期待しています。
Related Work: Packfile URIs
Git プロトコルには、 Git サーバーがクライアントのリクエストを処理するときに、 パックファイル応答(response)と共に URL の組をリストできる機能が既に備わっています。 クライアントは、 レスポンスを受け取った後、 レスポンスを完全に理解するために、 それらの URL の場所にあるパックファイルをダウンロードする必要があります。
このメカニズムは(JGit で実装された) Gerrit サーバーによって使用され、 クローン時の、 CPU 負荷の削減とユーザー・パフォーマンスの向上に効果的です。
このメカニズムの主な欠点は、 origin サーバーがこれらのパックファイルの内容を「正確に」知る必要があり、 サーバーが応答した後しばらくの間、 ユーザーがパックファイルを利用できる必要があることです。 この、 origin と パックファイル・データの間の結合の管理は困難です。
さらに、 この実装はフェッチで機能させるのが非常に困難です。
Related Work: GVFS Cache Servers
GVFS プロトコル [2] は、Git の部分(partial)クローンが作成される以前に、 Git プロジェクトとは別に設計された一連の HTTP 通信端点(endponts)です。 このプロトコルの特徴の 1 つは、 中央サーバーに過負荷をかけずに Git データを転送するために、 ビルド・マシンまたは開発者オフィスと同じ場所に配置できる「キャッシュ・サーバー」のアイデアです。
VFS for Git で有名な通信端点(endpoint)は、 オンデマンドでオブジェクトをダウンロードできる GET /gvfs/objects/{oid}
通信端点(endpoint)です。 これは、 当該製品のファイル・システム仮想化の重要な部分です。
けれども、 GET /gvfs/prefetch?lastPackTimestamp=<t>
通信端点(endpoint)は必要性が薄いです。 オプションのタイムスタンプを指定すると、 キャッシュ・サーバーは、 それらの時間間隔で導入されたコミットとツリーを含む、事前計算されたパックファイルのリストで応答します。
キャッシュ・サーバーは、 以下の戦略を使用してこれらの「プリフェッチ」パックファイルを計算します: The cache server computes these "prefetch" packfiles using the following strategy:
-
1 時間ごとに指定のタイムスタンプで「毎時」(hourly)パックが生成されます。
-
毎晩、それ以前の 24 時間分の毎時パックが「日次」(daily)パックにまとめられます。
-
毎晩、 30 日以上経過したすべてのプリフェッチ・パックが 1 つのパックにまとめられます。
ユーザーがキャッシュ・サーバーを使用して、リポジトリ対して gvfs clone
または scalar clone
を実行すると、 クライアントはすべてのプリフェッチ・パック・ファイルを要求します。これは、コミットとツリーのみをダウンロードする、最大で「24 + 30 + 1」の数のパックファイルです。 次に、 クライアントは origin サーバーに参照を要求し、その先端参照(tip reference)のチェックアウトを試みます。 (コミットがまだプリフェッチ・パック・ファイルに含まれていない場合に備えて、 特定のコミットから到達可能なすべてのツリーを取得するのに役立つ追加の通信端点(endpoint)があります。)
git fetch
中、 フックは、 以前にダウンロードされたプリフェッチ・パック・ファイルからの最新のタイムスタンプを使用して、 プリフェッチ通信端点(endnpoint)を要求します。 タイムスタンプが新しいパック・ファイルのリストのみがダウンロードされます。 ほとんどのユーザーは 1 時間ごとにフェッチするため、 1 時間ごとに最大 1 つのプリフェッチ・パックを取得します。 マシンの電源がオフになっているか、 30 日以上フェッチされていないユーザーは、 すべてのプリフェッチ・パック・ファイルを再ダウンロードする可能性がありますが、これはまれな事です。
クライアントは常に refs 広告(advertisement)のために origin サーバーに接続するため、 refs はプリフェッチされたパック・データよりも「先に」なることが多いことに注意することが重要です。 不足しているオブジェクトは、 git checkout
や git log
などのコマンドで必要になったときに、 GET gvfs/objects/{oid}
リクエストを使用してオンデマンドでダウンロードされます。 一部の Git 最適化は、これらのオンデマンド・ダウンロードに積極的になりすぎる原因となるチェックを無効にします。
See Also
[1] https://lore.kernel.org/git/RFC-cover-00.13-0000000000-20210805T150534Z-avarab@gmail.com/ An earlier RFC for a bundle URI feature.
[2] https://github.com/microsoft/VFSForGit/blob/master/Protocol.md The GVFS Protocol