web-dev-qa-db-ja.com

gitバイナリdiffアルゴリズム(デルタストレージ)は標準化されていますか?

Gitは差分圧縮を使用して、互いに類似したオブジェクトを格納します。

このアルゴリズムは標準化されており、他のツールでも使用されていますか?形式を説明するドキュメントはありますか? xdelta/VCDIFF/RFC 3284と互換性がありますか?

51
Thilo

pack files に使用されるdiffアルゴは delta encoding の1つにリンクされていたと思います: initialally(2005)xdelta そして、- libXDiff
しかし、次に詳しく説明するように、カスタム実装に移行しました。

とにかく、 ここで言及

Gitはパックファイルでデリティフィケーションのみを行います。
しかし、SSH経由でプッシュすると、gitは反対側にないコミットでパックファイルを生成し、それらのパックはシンパックであるため、デルタも含まれますが、リモート側は次にベースを追加します。それらをスタンドアロンにする薄いパック。

(注:多くのパックファイルを作成する、または巨大なパックファイルで情報を取得するのはコストがかかるであり、gitが巨大なファイルや巨大なリポジトリをうまく処理できない理由を説明します。
詳細については、「 大きなファイルを含むgit )」を参照してください

このスレッド も思い出させます:

実際にパックファイルとデリティフィケーション(LibXDiff、xdeltaではない)は、私が覚えて理解していることから、もともとネットワーク帯域幅のため(ディスク領域よりもはるかにコストがかかる)であり、 I/Oパフォーマンス非常に多数のルーズオブジェクトの代わりに単一のmmappedファイルを使用する場合。

LibXDiffはこの 2008スレッド で言及されています。

しかし、それ以来、このカスタムは、おそらく 2011スレッドの例 として、そして diff-delta.c 指摘:

つまり、厳密に言うと、Gitの現在のコードは、libxdiffコードとまったく似ていません。
ただし、両方の実装の背後にある基本的なアルゴリズムは同じです

これがどのように機能するかを理解するためには、おそらくlibxdiffバージョンを調べる方が簡単です。

/*
 * diff-delta.c: generate a delta between two buffers
 *
 * This code was greatly inspired by parts of LibXDiff from Davide Libenzi
 * http://www.xmailserver.org/xdiff-lib.html
 *
 * Rewritten for GIT by Nicolas Pitre <[email protected]>, (C) 2005-2007
 */

packfiles the Git Book の詳細:

packfile format


Git 2.18はデルタの説明に追加されます この新しい ドキュメントセクション で、現在(2018年第2四半期)に次のように記載されています。

オブジェクトタイプ

有効なオブジェクトタイプは次のとおりです。

  • OBJ_COMMIT(1)
  • OBJ_TREE(2)
  • OBJ_BLOB(3)
  • OBJ_TAG(4)
  • OBJ_OFS_DELTA(6)
  • OBJ_REF_DELTA (7)

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

細分化された表現

概念的には、オブジェクトタイプはコミット、ツリー、タグ、ブロブの4つだけです。
スペースを節約するために、オブジェクトは別の「ベース」オブジェクトの「デルタ」として保存できます。
これらの表現には、新しいタイプのs-deltaとref-deltaが割り当てられています。これらは、パックファイルでのみ有効です。

どちらも ofs-deltaおよびref-deltaオブジェクトを再構築するために別のオブジェクト(「ベースオブジェクト」と呼ばれる)に適用される「デルタ」を格納します.
それらの違いは、

  • ref-deltaは、20バイトの基本オブジェクト名を直接エンコードします。
    • ベースオブジェクトが同じパックにある場合、ofs-deltaは代わりにパックのベースオブジェクトのオフセットをエンコードします。

ベースオブジェクトは、同じパックに含まれている場合、デリティファイされる可能性もあります。
Ref-deltaは、パック外のオブジェクト(つまり、いわゆる「シンパック」)を参照することもできます)。ただし、ディスクに格納する場合、循環依存を回避するために、パックは自己完結型である必要があります。

delta dataは、ベースオブジェクトからオブジェクトを再構築するための一連の命令です。
ベースオブジェクトが細分化されている場合は、最初に標準オブジェクトに変換する必要があります。各命令は、完了するまで、より多くのデータをターゲットオブジェクトに追加します。
これまでに2つのサポートされている手順があります。

  • 1つは、ソースオブジェクトからバイト範囲をコピーし、
  • 命令自体に埋め込まれた新しいデータを挿入するためのもの。

各命令には可変長があります。命令タイプは、最初のオクテットの7番目のビットによって決定されます。次の図は、RFC 1951(Deflate圧縮データ形式)の規則に従います。

64
VonC

Gitデルタエンコーディングはコピー/挿入ベースです。

つまり、派生ファイルは、コピー命令(例:ベースファイルからオフセットxから始まるyバイトをターゲットバッファーにコピー)または挿入命令(例:次のxバイトをターゲットバッファ)。

非常に単純な例として(ペーパー「デルタ圧縮のファイルシステムサポート」から)、テキスト「プロキシキャッシュ」を「キャッシュプロキシ」に変換するデルタバッファを作成することを検討してください。結果の指示は次のようになります。

  1. オフセット7から5バイトをコピー(ベースバッファーから 'キャッシュ'をコピー)
  2. 2つのスペースを挿入する
  3. オフセット0から5バイトをコピー(ベースバッファーから 'proxy'をコピー)

Gitのエンコーディングに変換されたものは次のようになります。

(バイト1〜3は最初の命令を表します)

  • 0x91(10010001)、これはに分割されます
    • 0x80(10000000)(最上位ビットセットにより、これは「ベースから出力へのコピー」命令になります)
    • 0x01(00000001)(「1バイト進み、それをベースオフセットとして使用することを意味します)
    • 0x10(00010000)(1バイト進め、長さとして使用)
  • 0x07(オフセット)
  • 0x05(長さ)

(バイト4-6は2番目の命令を表します)

  • 0x02(MSBが設定されていないため、これは「次の2バイトを出力に挿入する」ことを意味します)
  • 0x20(スペース)
  • 0x20(スペース)

(バイト7-8は最後の命令を表します)

  • 0x90(10010000)、これはに分割されます
    • 0x80(10000000)(「コピー」を意味します)
    • 0x10(00010000)(1バイト進め、長さとして使用)
  • 0x05(長さ)

最後のコピー命令では、オフセット0を意味するオフセットを指定していないことに注意してください。より大きなオフセット/長さが必要な場合は、コピーオペコードの他のビットも設定できます。

この例の結果のデルタバッファーは8バイトですが、ターゲットバッファーが12バイトであるため、これはあまり圧縮にはなりませんが、このエンコードを大きなテキストファイルに適用すると、大きな違いが生じる可能性があります。

私は最近 node.jsライブラリ をgithubにプッシュしました。これはgit deltaエンコーディングを使用して両方のdiff/patch関数を実装します。 code は、大幅に最適化されているgitソースのコードよりも読みやすく、コメント化されているはずです。

また、各例で使用されている出力オペコードを上記と同様の形式で説明する tests もいくつか書いています。

22

このアルゴリズムは標準化されており、他のツールでも使用されていますか?

パック形式はパブリックAPIの一部です。プッシュおよびフェッチ操作に使用される転送プロトコルは、それを使用して、ネットワーク経由で送信するデータを減らします。

これらは、リファレンス以外に、少なくとも2つの主要なGit実装で実装されています: JGit および libgit2

したがって、形式に後方互換性のない変更が行われることはほとんどなく、その意味で「標準化」されていると見なすことができます。

ドキュメントからのこの驚くべきファイルは、Linusによる電子メールの面白いコメントとしてパックアルゴリズムで使用されるヒューリスティックを説明しています: https://github.com/git/git/blob/v2.9.1/Documentation/technical/ pack-heuristics.txt