SYNOPSIS

$GIT_DIR/objects/pack/pack-*.{pack,idx}
$GIT_DIR/objects/pack/pack-*.rev
$GIT_DIR/objects/pack/pack-*.mtimes
$GIT_DIR/objects/pack/multi-pack-index

DESCRIPTION

Git パック形式は、 Git がリポジトリーの主要なデータを保存する方法です。 リポジトリーの存続期間中に、 (もしあれば)緩いオブジェクト(loose objects)や小さなパックは、 より大きなパックに統合されます。 詳細は git-gc(1)git-pack-objects(1) を参照してください。

パック形式は通信(over-the-wire)でも使用されます。 たとえば gitprotocol-v2(5) を参照して下さい。 そして、 gitformat-bundle(5) では他のコンテナ形式の一部です。

Checksums and object IDs

従来のSHA-1を使用するリポジトリでは、以下で説明するパックチェックサム、インデックスチェックサム、およびオブジェクトID(オブジェクト名)はすべてSHA-1を使用して計算されます。 同様に、SHA-256リポジトリでは、これらの値はSHA-256を使用して計算されます。

CRC32 checksums are always computed over the entire packed object, including the header (n-byte type and length); the base object name or offset, if any; and the entire compressed object. The CRC32 algorithm used is that of zlib.

pack-*.pack files have the following format:

  • ヘッダーは最初にあらわれ、以下のもので構成されます

    4-byte シグネチャ

    The signature is: {P, A, C, K}

    4-byte version number (ネットワーク・バイト・オーダー)

    Gitは現在バージョン番号2または3を受け入れますが、バージョン2のみを生成します。

    4-byte パックに含まれるオブジェクトの数(ネットワーク・バイト・オーダー)

    所見: このバージョンでは、パック内のオブジェクトは4Gを超えることはできず、パックも4Gを超えることはできません。

  • ヘッダーの後ろには多くのオブジェクト・エントリが続き、 それぞれは以下のようになります

    (非デルタ化表現)

    n-byte type and length (type:3ビット幅、length: (n-1)*7+4 ビット幅)

    圧縮データ

    (デルタ化表現)

    n-byte type and length (type:3ビット幅、length: (n-1)*7+4 ビット幅)

    OBJ_REF_DELTAの場合はベースオブジェクト名、OBJ_OFS_DELTAオブジェクトの場合はパック内のデルタオブジェクトの位置からの負の相対オフセット

    圧縮デルタデータ

  • 所見: 各オブジェクトの長さは可変長形式でエンコードされ、32ビットなどに制限されません。

  • トレーラー(trailer)は、上記のすべてのパックチェックサムを記録します。

Object types

有効なオブジェクトタイプは以下のとおりです:

  • OBJ_COMMIT (1)

  • OBJ_TREE (2)

  • OBJ_BLOB (3)

  • OBJ_TAG (4)

  • OBJ_OFS_DELTA (6)

  • OBJ_REF_DELTA (7)

タイプ5は、将来の拡張用に予約されています。 タイプ0は無効です。

Object encoding

Unlike loose objects, packed objects do not have a prefix containing the type, size, and a NUL byte. These are not necessary because they can be determined by the n-byte type and length that prefixes the data and so they are omitted from the compressed and deltified data.

The computation of the object ID still uses this prefix by reconstructing it from the type and length as needed.

Size encoding

このドキュメントでは、負でない整数で、「サイズエンコーディング」(size encoding)を使用します。つまりそれは、 各バイトから、下位7ビットを使用して結果の整数を形成します。 最上位ビットが1である限り、この処理は続行されます。 MSB 0 のバイトは、最後の7ビットを提供します。これら7ビットのチャンクは連結されます。 後の値の方が上位です。

このサイズエンコーディング(size encoding)を、このドキュメントでも使用されている「オフセットエンコーディング」(offset encoding)と混同しないでください。

When encoding the size of an undeltified object in a pack, the size is that of the uncompressed raw object. For deltified objects, it is the size of the uncompressed delta. The base object name or offset is not included in the size computation.

Deltified representation(デルタ化表現)

概念的には、commit、tree、tag、blobの4つのオブジェクトタイプしかありません。 ただし、スペースを節約するために、オブジェクトを別の「ベース」(base)オブジェクトの「デルタ」(delta)として格納できます。 これらの表現には、パックファイルでのみ有効な新しいタイプの ref-delta および ofs-delta が割り当てられます。

ofs-deltaとref-deltaはどちらも、オブジェクトを再構築するために別のオブジェクト(「ベースオブジェクト」と呼ばれる)に適用される「デルタ」を格納します。 それらの違いは、ref-deltaがベースオブジェクト名を直接エンコードすることです。 ベースオブジェクトが同じパックにある場合、ofs-deltaは代わりにパック内のベースオブジェクトのオフセットをエンコードします。

同一パックに含まれている場合は、ベースオブジェクトを削除することもできます。 ref-deltaは、パック外のオブジェクト(つまり、いわゆる「薄いパック」(thin pack))を参照することもできます。 ただし、ディスクに保存する場合は、循環依存を回避するためにパックを自己完結型にする必要があります。

デルタ・データは、 ベース・オブジェクトのサイズと再構築されるオブジェクトのサイズから始まります。 これらのサイズは、 上記サイズ・エンコーディングを使用してエンコードされます。 デルタ・データの残りの部分は、 ベース・オブジェクトからオブジェクトを再構築するための一連の命令です。 ベース・オブジェクトが削除されている場合は、 最初に標準形に変換する必要があります。 各命令は、 完了するまでターゲット・オブジェクトにどんどんデータを追加します。 現時点でサポートされている命令は2つあります。 1つはソース・オブジェクトからバイト範囲をコピーするためのもので、 もう1つは命令自体に埋め込む新しいデータを挿入するためのものです。

各命令の長さは可変です。 命令タイプは、最初のオクテット(訳注:1バイト(8ビット))のビット7(訳注:つまりこのバイトの最上位ビット)によって決定されます。 以下の図は、RFC 1951(Deflate compressed data format;圧縮データ形式の解凍)の規則に従います。

ベースオブジェクトからのコピー命令

+----------+---------+---------+---------+---------+-------+-------+-------+
| 1xxxxxxx | offset1 | offset2 | offset3 | offset4 | size1 | size2 | size3 |
+----------+---------+---------+---------+---------+-------+-------+-------+

これは、ソースオブジェクトからバイト範囲をコピーするための命令です。 コピー元のオフセットとコピーするバイト数をエンコードします。 オフセットとサイズはリトルエンディアンです。

すべてのオフセットバイトとサイズバイトはオプションです。 これは、小さなオフセットまたはサイズをエンコードするときに命令サイズを減らすためです。 最初のオクテットの最初の7つのビット(訳注:bit6〜0)は、次の7つのオクテットのどれが存在するかを決定します。 ビットゼロが設定されている場合、offset1が存在します。 ビット1が設定されている場合、offset2が存在します。

注意: よりコンパクトな形式は、オフセットとサイズのエンコーディングを変更しないことに注意してください。 たとえば、以下のようにoffset2のみが省略されている場合でも、offset3にはビット16〜23が含まれています。 それはoffset1の隣に続くからoffset2という訳ではなくて、(offset3の)ビット8〜15が含まれています。

+----------+---------+---------+
| 10000101 | offset1 | offset3 |
+----------+---------+---------+

最もコンパクトな形式では、この命令はオフセットとサイズの両方が省略された1バイト(0x80)のみを使用し、デフォルト値はゼロになります。 もうひとつ例外があります。サイズゼロは自動的に 0x10000 に変換されます。

新データ追加命令

+----------+============+
| 0xxxxxxx |    data    |
+----------+============+

これは、 ベース・オブジェクトなしでターゲット・オブジェクトを構築するための命令です。 続くデータがターゲット・オブジェクトに追加されます。 最初のオクテットの最初の7つのビット(訳注:bit6〜0)は、 データのサイズをバイト単位で決定します。 サイズはゼロ以外でなければなりません。

Reserved instruction

+----------+============
| 00000000 |
+----------+============

これは、将来の拡張のために予約されている命令です。

Original (version 1) pack-*.idx files have the following format:

  • ヘッダーは、256個の4バイトのネットワークバイトオーダー整数で構成されます。このテーブルのN番目のエントリは、対応するパック内のオブジェクトの数を記録します。オブジェクト名の最初のバイトはN以下です。これは、「first-level fan-out」テーブルと呼ばれます。

  • ヘッダーの後には、ソートされた24バイトのエントリが続きます(パック内のオブジェクトごとに1つのエントリ)。 各エントリは以下のとおりです:

    • 4-byte ネットワークバイトオーダー整数で、オブジェクトが格納されている場所をパックファイル先頭からのオフセットとして記録します。

    • 適切なサイズの1つのオブジェクト名。

  • ファイルはトレーラーで締めくくられています:

    対応するパックファイルの最後にあるパックチェックサムのコピー。

  • 上記すべてのインデックスチェックサム。

パックIdxファイル:

        --  +--------------------------------+
fanout      | fanout[0] = 2 (for example)    |-.
table       +--------------------------------+ |
            | fanout[1]                      | |
            +--------------------------------+ |
            | fanout[2]                      | |
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
            | fanout[255] = total objects    |---.
        --  +--------------------------------+ | |
main        | offset                         | | |
index       | object name 00XXXXXXXXXXXXXXXX | | |
table       +--------------------------------+ | |
            | offset                         | | |
            | object name 00XXXXXXXXXXXXXXXX | | |
            +--------------------------------+<+ |
          .-| offset                         |   |
          | | object name 01XXXXXXXXXXXXXXXX |   |
          | +--------------------------------+   |
          | | offset                         |   |
          | | object name 01XXXXXXXXXXXXXXXX |   |
          | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   |
          | | offset                         |   |
          | | object name FFXXXXXXXXXXXXXXXX |   |
        --| +--------------------------------+<--+
trailer   | | packfile checksum              |
          | +--------------------------------+
          | | idxfile checksum               |
          | +--------------------------------+
          .-------.
                  |
Pack file entry: <+

パックされたオブジェクトのヘッダー:

byte size extension bit (MSB)
  • type (next 3 bit)

  • size0 (lower 4-bit)

  • n-byte sizeN (MSBがセットされている限り。各7ビット) size0..sizeN form 4+7+7+..+7 ビット整数で、size0 は最も下位で、 sizeN が最も上位です。

パックされたオブジェクトのデータ
  • DELTAでない場合は、解凍されたバイト(上記のサイズは圧縮前のサイズです)。

  • REF_DELTAの場合、ベースオブジェクト名(上記サイズは後続のデルタデータのサイズです)。

  • 圧縮されたデルタデータ。

  • OFS_DELTAの場合、nバイトオフセット(以下参照)は、ofs-deltaエントリのヘッダーのタイプバイトからの負のオフセットとして解釈されます(上記サイズは、後続のデルタデータのサイズです)。

  • 圧縮されたデルタデータ。

offset encoding

最後の1つを除くすべてにMSBが設定されたnバイト。 オフセットは、各バイトの下位7ビットを連結して作成された数値であり、n >= 2 の場合、結果に 2^7 + 2^14 + ... + 2^(7*(n-1)) を加算します。

バージョン2 pack-*.idx ファイルは4GiBより大きいパックをサポートし、他のいくつかの再編成があります。それらの形式は以下のとおりです:

  • 4-byte マジックナンバー \377tOc (訳注: 0xFF, 0x74, 0x4F, 0x63)は、 unreasonable fanout[0] 値です。

  • 4-byte バージョン番号 (= 2)

  • v1と同様の256エントリのファンアウトテーブル。

  • ソートされたオブジェクト名のテーブル。 これらはオフセット値なしで一緒にパックされ、特定のオブジェクト名のバイナリ検索のキャッシュフットプリント(cache footprint)を削減します。

  • パックされたオブジェクトデータの4バイトCRC32値のテーブル。 これはv2の新機能で、再パック中に、未検出データ破損無しで圧縮データをパックからパックに直接コピーできます。

  • 4バイトのオフセット値のテーブル(ネットワークバイトオーダー)。 これらは通常31ビットパックファイルオフセットですが、ラージオフセットは、msbitが設定された次のテーブルへのインデックスとしてエンコードされます。

  • 8バイトのオフセットエントリのテーブル(2 GiB未満のパックファイルの場合は空)。 パックファイルは、頻繁に使用されるオブジェクトを手前に配置するように編成されているため、ほとんどのオブジェクト参照はこのテーブルを参照する必要はありません。

  • v1パックファイルと同一のトレーラー:

    対応するパックファイルの最後にあるパックチェックサムのコピー。

  • 上記すべてのインデックスチェックサム。

pack-*.rev ファイルは以下の形式です:

  • 4-byte マジックナンバー 0x52494458 (RIDX).

  • 4-byte バージョンID(= 1)。

  • 4-byte ハッシュ機能ID(= 1:SHA-1, 2:SHA-256)。

  • インデックス位置のテーブル(パックされたオブジェクトごとに1つ、合計 num_objects、それぞれネットワークオーダーで4バイトの符号なし整数)。パックファイル内の対応するオフセットでソートされます。

  • トレーラーは、対応するパックファイルのチェックサムと、上記のすべてのチェックサムを含みます。

全ての 4-byte 数値はネットワークオーダーです。

pack-*.mtimes files have the format:

全ての 4バイト数はネットワーク・バイト・オーダーです。

  • 4バイト。マジック・ナンバー 0x4d544d45 (MTME)

  • 4-byte バージョンID(= 1)。

  • 4-byte ハッシュ機能ID(= 1:SHA-1, 2:SHA-256)。

  • 4バイトの符号なし整数の表。 i 番目の値は、 対応するパック内の i 番目のオブジェクトの変更時間(mtime)であり、 辞書順(インデックス順)です。 mtimes は、 標準エポック秒をカウントします。

  • 対応するパックファイルのチェックサムと上記すべてのチェックサムを含むトレーラー (それぞれ指定されたハッシュ関数に応じた長さ)。

multi-pack-index (MIDX) ファイルの形式は以下の通り:

multi-pack-indexファイルは、複数のパックファイル(pack-files)と緩いオブジェクト(loose objects)を参照します。

MIDXにデータを追加する拡張機能を使用できるようにするために、ボディを「チャンク」に編成し、ボディの先頭にルックアップテーブルを提供します。 ヘッダーには、パックの数、ベースMIDXファイルの数、ハッシュの長さ、タイプなど、特定の長さ値達が含まれます。

全ての 4-byte 数値はネットワークオーダーです。

HEADER:

4-byte シグネチャ

The signature is: {M, I, D, X}

1-byte バージョン番号

Gitはバージョン 1 のみを書き込みまたは認識します。

1-byte オブジェクトIDバージョン(= 1: SHA-1, 2: SHA-256)

この値からオブジェクトID(OID)の長さを推測します。 ハッシュタイプがリポジトリのハッシュアルゴリズムと一致しない場合は、multi-pack-indexファイルを無視して、ユーザーに警告を表示する必要があります。

1-byte チャンクの数

チャンクの数

1-byte 「ベース multi-pack-index ファイル」の数

この値は現在のところ常にゼロです。

4-byte パックファアイルの数

パックファアイルの数

CHUNK LOOKUP:

  • (C + 1) * 12 bytes はチャンクオフセットを提供します

    最初の4バイトはチャンクIDです。値 0 はラベル終端です。

    他の8バイトは、現在のファイルでチャンクを開始するためのオフセットを提供します。(チャンクはファイル順に提供されるため、必要に応じて次のチャンク位置を使用して長さを推測できます。)

  • CHUNK LOOKUP は、チャンク・ベースのファイル形式の目次と一致します。 gitformat-chunk(5) を参照してください。

  • ボディの残りのデータは一度に1つのチャンクで記述され、これらのチャンクは任意の順序で指定できます。 特に指定がない限り、チャンクは必要です。

CHUNK DATA:

Packfile Names (ID: {P, N, A, M})

パックファイル名達を連結されたnullで終了する文字列として格納します。名前による高速ルックアップを行うには、パックファイルを辞書式順序でリストする必要があります。 これは、長さが4バイトの倍数であることが保証されていない唯一のチャンクであるため、アライメント上の理由から最後のチャンクにする必要があります。

OID Fanout (ID: {O, I, D, F})

i番目のエントリF[i]は、最初のバイトが最大iのOIDの数を格納します。 したがって、F[255]はオブジェクトの総数を格納します。

OID Lookup (ID: {O, I, D, L})

MIDX内のすべてのオブジェクトのOIDは、このチャンクに辞書式順序(lexicographic order)で格納されます。

Object Offsets (ID: {O, O, F, F})

オブジェクトごとに2つの4バイト値を格納します。

  1. このオブジェクトを格納するパックの pack-int-id。

  2. パック内オフセット。 すべてのオフセットが 2^32 未満(less than)の場合、 巨大オフセット・チャック(large offset chunk)は存在せず、 IDX v1 としてオフセットが格納されます。 2^32-1 を超える(larger than)オフセット値が少なくとも 1 つある場合、 巨大オフセット・チャック(large offset chunk)が存在する必要があり、 2^31-1 を超えるオフセットはそこに格納されなければなりません。 巨大オフセット・チャック(large offset chunk)が存在し、 31 番目のビットがオンの場合、 そのビットを削除すると、 このオブジェクトの 8バイト・オフセットを含む、巨大オフセット(large offsets)の行位置が判ります。

[オプション] Object Large Offsets (ID: {L, O, F, F})

8-byte 大きなパックファイル(large packfiles)へのオフセット。

[Optional] Bitmap pack order (ID: {R, I, D, X})

MIDX 位置のリスト(MIDX 内のオブジェクトごとに 1 つ、 合計で num_objects 個、 それぞれがネットワーク・バイト順で 4 バイトの符号なし整数)で、相対的な ビットマップ/疑似パック 位置に従って並べ替えられます。

TRAILER:

(C + 1) * 12 bytes はチャンクオフセットを提供します

最初の4バイトはチャンクIDです。値 0 はラベル終端です。

他の8バイトは、現在のファイルでチャンクを開始するためのオフセットを提供します。(チャンクはファイル順に提供されるため、必要に応じて次のチャンク位置を使用して長さを推測できます。)

CHUNK LOOKUP は、チャンク・ベースのファイル形式の目次と一致します。 gitformat-chunk(5) を参照してください。

ボディの残りのデータは一度に1つのチャンクで記述され、これらのチャンクは任意の順序で指定できます。 特に指定がない限り、チャンクは必要です。

CHUNK DATA:

Packfile Names (ID: {P, N, A, M})

パック・ファイルの名前を、 NUL文字で終わる文字列のシーケンスとして保存します。 ファイル名とファイル名の間には余分なパディングはなく、 辞書順(lexicographic order)にリストされます。 チャンク自体は、 チャンク・サイズが4バイトの倍数になるように、 末尾に0~3個のNULバイトでパディングされます。

Bitmapped Packfiles (ID: {B, T, M, P})

2つの4バイトの符号なし整数をネットワークバイトオーダーで格納するテーブルを保存します。 各テーブル・エントリは、 PNAM チャンクで上記に記載された順序で単一のパックに対応します。 各テーブル・エントリの値は以下の通りです:

  • そのパックに含まれるオブジェクトを含む最初のビット位置(下記の擬似パック順序(pseudo-pack order)で)。

  • そのパックから選択されるオブジェクトのビット数。

OID Fanout (ID: {O, I, D, F})

i番目のエントリF[i]は、最初のバイトが最大iのOIDの数を格納します。 したがって、F[255]はオブジェクトの総数を格納します。

OID Lookup (ID: {O, I, D, L})

MIDX内のすべてのオブジェクトのOIDは、このチャンクに辞書式順序(lexicographic order)で格納されます。

Object Offsets (ID: {O, O, F, F})

オブジェクトごとに2つの4バイト値を格納します。

  1. このオブジェクトを格納するパックの pack-int-id。

  2. パック内オフセット。 すべてのオフセットが 232 未満(less than)の場合、 巨大オフセット・チャンク(large offset chunk)は存在せず、 IDX v1 としてオフセットが格納されます。 232-1 を超える(larger than)オフセット値が少なくとも 1 つある場合、 巨大オフセット・チャンクが存在する必要があり、 2^31-1 を超えるオフセットはそこに格納されなければなりません。 巨大オフセット・チャンクが存在し、 31 番目のビットがオンの場合、 そのビットを削除すると、 このオブジェクトの 8バイト・オフセットを含む、 巨大オフセット(large offsets)の行(row)位置が判ります。

[オプション] Object Large Offsets (ID: {L, O, F, F})

8-byte 大きなパックファイル(large packfiles)へのオフセット。

[Optional] Bitmap pack order (ID: {R, I, D, X})

MIDX 位置のリスト(MIDX 内のオブジェクトごとに 1 つ、 合計で num_objects 個、 それぞれがネットワーク・バイト順で 4 バイトの符号なし整数)で、相対的な ビットマップ/疑似パック 位置に従って並べ替えられます。

TRAILER:

  • 上記の内容のインデックスチェックサム。

multi-pack-index reverse indexes

パックベースのリバースインデックスと同様に、マルチパックインデックスを使用してリバースインデックスを生成することもできます。

この逆インデックスは、offset、pack-、index の位置の間でマッピングする代わりに、MIDX内のオブジェクトの位置と、MIDXが記述する疑似パック内のそのオブジェクトの位置の間でマッピングします(つまり、マルチパック逆インデックスのi番目のエントリは、i番目のオブジェクトのMIDX位置を疑似パック順に保持します)。

これらの順序の違いを明確にするために、マルチパック到達可能性ビットマップ(まだ存在していませんが、現在これを目指して開発中です)を検討してください。 各ビットはMIDX内のオブジェクトに対応する必要があるため、ビット位置からMIDX位置への効率的なマッピングが必要です。

解決策の一つは、ビットがMIDXによって格納された、oidソートされたインデックスの同じ位置を占めるようにすることです。 ただし、oidは事実上ランダムであるため、結果として得られる到達可能性ビットマップには局所性がなく、圧縮が不十分になります。 (これが、シングルパックビットマップが同じ目的で、 .idx順序ではなく、パック順序を使用する理由です。)

そのため、パックの順序に基づいてMIDX全体の順序を定義します。これにより、局所性が大幅に向上します(したがって、より効率的に圧縮されます)。 MIDX内のすべてのパックを連結して作成された疑似パックを考えることができます。 たとえば、3つのパック(a、b、c)、それぞれ10、15、および20個のオブジェクトを含むMIDXがある場合、以下のようなオブジェクトの順序を想像できます:

|a,0|a,1|...|a,9|b,0|b,1|...|b,14|c,0|c,1|...|c,19|

ここで、パックの順序はMIDXのパックリストによって定義され、各パック内のオブジェクトの順序は実際のパックファイルでの順序と同じです。

パックのリストとオブジェクトの数を考えると、その疑似パックの順序を簡単に再構築できます(たとえば、パック「a」と「b」がスロットの25を消費したため、位置27のオブジェクトは(c、1)でなければなりません)。 しかし、落とし穴があります。 オブジェクトはパック間で複製される可能性があるのです。その場合、MIDXはオブジェクトへのポインターを1つだけ格納します(したがって、ビットマップに1つのスロットのみが必要です)。

呼び出し元は、ビット位置の順にオブジェクトを読み取ることで重複を処理できますが、オブジェクトの数は直線的であり、通常のビットマップルックアップにはコストがかかりすぎます。 逆インデックスを作成すると、これが解決されます。これは、インデックスの論理的な逆であり、そのインデックスはすでに重複を削除しているためです。 ただし、その場で逆インデックスを作成すると、コストがかかる可能性があります。 パックベースの逆インデックス用のオンディスク形式がすでにあるので、MIDXの疑似パックにも再利用する事しましょう。

MIDXのオブジェクトは、疑似パックをつなぎ合わせるために次のように順序付けられます。 pack(o) がMIDXによって o が選択されたパックを返し、(MIDXによって保存された)数値IDに基づいてパックの順序を定義します。 offset(o) が pack(o) 内の o のオブジェクトオフセットを返すようにします。 次に、o1 と` o2`を以下のように比較します:

  • pack(o1) と pack(o2) の一方が優先され、もう一方が優先されない場合、優先される方が最初にソートされます。

    (詳細に言うと、これは、MIDXビットマップがビット位置0にあるオブジェクトを含むパックをMIDXに求めることができるので、パック再利用メカニズムによって使用されるべきパックを決定することを可能にします)。

  • pack(o1) ≠ pack(o2) の場合、パックIDに基づいて2つのオブジェクトを降順で並べ替えます。

  • それ以外の場合、 pack(o1) = pack(o2) であり、オブジェクトはパック順に並べ替えられます(つまり、 offset(o1) < offset(o2) の場合、o1o2 よりも先に並べ替えられます)。

要するに、MIDXの擬似パックは、MIDXによって保存されたパック内のオブジェクトをパック順に並べ、パックをMIDX順(優先パックが先に来る)に並べたものを重複排除して連結したものです。

MIDX の逆インデックスは、 MIDX 自体内の、オプションの RIDX チャンクに格納されます。

BTMP chunk

ビットマップ付きパック・ファイル(BTMP)チャンクは、 マルチパック・インデックスの到達可能性ビットマップにおけるオブジェクトに関する追加情報をエンコードします。 MIDXのオブジェクトは、 到達可能性ビットマップのために「擬似パック」(pseudo-pack)順序(上記参照)で配置されていることを思い出してください。

上記の例にならって、 仮にパック「a」と「b」と「c」があり、 それぞれ 10、 15、 20個のオブジェクトを持つとします。 擬似パック順序では、 それらは以下のように配置されます:

|a,0|a,1|...|a,9|b,0|b,1|...|b,14|c,0|c,1|...|c,19|

単一パック・ビットマップ(または、 優先パックを持つマルチパック到達可能性ビットマップと同等)を使用する場合、 git-pack-objects(1) は「そのままの」(verbatim)再利用を行い、 ビットマップ付きまたは優先パックファイルのチャンクを再利用しようと試み、 パッキング・リストにオブジェクトを追加する代わりにします。

既存のパックからバイトのチャンク(chunk of bytes)が再利用されると、 そこに含まれるオブジェクトはパッキング・リストに追加する必要がないため、 メモリとCPU時間を節約できます。 ただし、 既存のパック・ファイルからのチャンクは、 以下の条件が満たされた場合にのみ再利用できます:

  • チャンクには、 呼び出し元が要求したオブジェクトのみが含まれている(つまり、 呼び出し元が明示的または暗黙的に要求してい無いオブジェクトを含ま無い)。

  • 非・薄いパック(non-thin packs)にオフセット・デルタまたは参照デルタとして格納されているすべてのオブジェクトは、 結果として得られるパックにそのベース・オブジェクトを含めます。

BTMP チャンクは、 上記のように一連のパック・ファイルに対してマルチ・パックの再利用を実装するために必要な情報をエンコードします。 具体的には、BTMP チャンクは、 MIDXに格納されている各パック・ファイル p に対して、 以下の3つの情報(すべてネットワーク・バイト・オーダーの32ビット符号なし整数)をエンコードします:

bitmap_pos

マルチパック・インデックスの到達可能性ビットマップにおいて、 p からのオブジェクトが占める最初のビット位置(擬似パック(pseudo-pack)順序で)。

bitmap_nr

そのパック p からのオブジェクトをエンコードするビット位置の数(bitmap_pos の位置を含む)。

たとえば、 パック「a」と「b」と「c」がある、 上記の例に対応する BTMP チャンクは以下のようになります:

bitmap_pos

bitmap_nr

packfile “a”

0

10

packfile “b”

10

15

packfile “c”

25

20

この情報があれば、 各パック・ファイルを個別に再利用可能として扱うことができます。 これは、 BTMP チャンクが実装される前に、 個々のパックに対して行われていたそのままの(verbatim)パック再利用と同じ方法です。

cruft packs

残り物パック(cruft pack)機能は、 到達不能オブジェクトを削除するという Git の従来のメカニズムに代わる手段を提供します。 このドキュメントでは、 Git の刈り込み(pruning)メカニズムの概要と、 代わりに残り物パックを使用して同一のことを実現する方法について説明します。

Background

到達不能なオブジェクトをリポジトリから削除するために、 Git は git repack -Ad を提供しています(git-repack(1) を参照)。 以下、ドキュメントから引用します:

[...] 以前のパックの到達不能オブジェクトは、 古いパックに残されるのではなく、
パック解凍された緩いブジェクトになります。 [...] 到達不能なオブジェクトは、
次の `git gc` 呼び出しで通常の有効期限ルールに従って削除されます。

到達不能なオブジェクトはすぐには削除されません。 すぐに削除してしまうと、 削除しようとしているオブジェクトを参照する可能性のある後続のプッシュと競合する可能性があるためです。 代わりに、 これらの到達不能オブジェクトは緩いオブジェクトとして保存され、 期限切れウィンドウ(expiration window)より古くなるまでそのままの状態で保管されます。 期限切れになった時点で git-prune(1) で削除されます。

Git は、オブジェクトごとの mtime を追跡するために、 これらの到達不能オブジェクトを緩い(loose)オブジェクトに保存する必要があります。 これらの到達不能オブジェクトが 1 つの大きなパックに書き込まれた場合、 そのパックを(その中に含まれるオブジェクトが書き直されたため)リフレッシュするするか、 または到達不能オブジェクトの新しいパックを作成すると、 パックの mtime が更新され、 その中のオブジェクトが有効期限ウィンドウから離脱することは決してありません。 代わりに、 個々のオブジェクトの mtimes を追跡し、 すべての残り物(cruft)オブジェクトが一度に更新される状況を回避するために、 オブジェクトは緩いオブジェクトとして格納されます。

これにより、猶予期間を過ぎていない到達不能オブジェクトがリポジトリに多数含まれている場合に、望ましくない状況が発生する可能性があります。 .git/objects の一部(shard)に大きなディレクトリがあると、 リポジトリのパフォーマンスが低下する可能性があります。 また、 到達不能なオブジェクトがとても沢山ある場合、 i ノードの枯渇につながり、 システム全体のパフォーマンスを低下させる可能性があります。 これらのオブジェクトをパックすることはできないため、 これらのリポジトリは多くの場合、 大量のディスク領域を占有します。 それらは zlib 圧縮することしかできず、デルタ・チェーンに格納することはできないためです。

Cruft packs

残り物パック(cruft pack)は、 すべての緩い(loose)オブジェクトを含む単一のパックと一緒に、 オブジェクトごとの mtimes を別のファイルに含めることで、 到達不能なオブジェクトを緩いオブジェクト状態で保存する必要をなくします。

残り物パックは、 新しいパックを生成するときに git repack --cruft によって書き込まれます。 注意: git repack --cruft は古典的なオールインワンの再パックであることに注意してください。 つまり、結果のパック内のすべてが到達可能であり、 他のすべては到達不能です。 --cruft オプションを記述すると、git repack に対して、前のステップでパックされなかったオブジェクトのみを含む別のパックを生成するように指示されます (これは、すべての到達不能オブジェクトを一緒にパックすることと同じです)。 これは以下のとおりに進行します:

  1. すべてのオブジェクトを列挙し、 (a)「keep-pack に含まれていないオブジェクト」および、 (b)「mtime がトラバーサル・ヒントとしての猶予期間内にあるオブジェクト」をマークします。

  2. 前のステップで収集したヒントに基づいて到達可能性のトラバーサルを実行し、 その途中ですべてのオブジェクトをパックに追加します。

  3. オブジェクトごとのタイムスタンプを記録する .mtimes ファイルとともに、 パックを書き出します。

このモードは、 残り物パック(cruft pack)を作成するように指示されたときに、 git-repack(1) によって内部的に呼び出されます。 重要なことは、コア内に保持されたパックのセットは、 再パックによって削除されないパックのセットであることで、 つまり、リポジトリの到達可能なすべてのオブジェクトが含まれています。

リポジトリに既に残り物パック(cruft pack)がある場合、 git repack --cruft は通常、 それにオブジェクトを追加するだけです。 これに対する例外は、 git repack--cruft-expiration オプションが与えられた場合です。 これにより生成された残り物パックは、 後で git-gc(1) がそれらのオブジェクトを期限切れにするのを待つ代わりに、 期限切れのオブジェクトを省略できます。

通常、 期限切れの到達不能オブジェクトの削除を担当するのは git-gc(1) です。

Alternatives

この設計の注目すべき代替案は以下のとおりです:

  • オブジェクトごとの mtime データの配置。

mtime データの配置においては、 .idx 形式の複雑化を避けるために、 パックに関連付けられた新しい補助ファイルが選択されました。 .idx 形式がオプションのデータ・チャンクのサポートを獲得したならば、 .mtimes 形式を .idx 自体に統合することは理にかなっているかもしれません。

GIT

Part of the git(1) suite