Git は blob 上の SHA-1 衝突をどのように処理しますか? 質問する

Git は blob 上の SHA-1 衝突をどのように処理しますか? 質問する

これはおそらく現実世界ではまだ起こったことがなく、今後も起こらないかもしれませんが、次のことを考えてみましょう。Git リポジトリがあり、コミットを行い、非常に不運なことに、BLOB の 1 つが、リポジトリにすでに存在する別の BLOB と同じ SHA-1 を持つことになります。問題は、Git がこれをどのように処理するかです。単純に失敗するのでしょうか。それとも、2 つの BLOB をリンクして、コンテキストに応じてどちらが必要かを確認する方法を見つけるのでしょうか。

実際の問題というよりは頭の体操ですが、この問題は興味深いと思いました。

ベストアンサー1

この場合に Git がどのように動作するかを正確に調べるために実験を行いました。これはバージョン 2.7.9~rc0+next.20151210 (Debian バージョン) です。基本的には、次の diff を適用して Git を再構築することで、ハッシュ サイズを 160 ビットから 4 ビットに減らしました。

--- git-2.7.0~rc0+next.20151210.orig/block-sha1/sha1.c
+++ git-2.7.0~rc0+next.20151210/block-sha1/sha1.c
@@ -246,6 +246,8 @@ void blk_SHA1_Final(unsigned char hashou
    blk_SHA1_Update(ctx, padlen, 8);

    /* Output hash */
-   for (i = 0; i < 5; i++)
-       put_be32(hashout + i * 4, ctx->H[i]);
+   for (i = 0; i < 1; i++)
+       put_be32(hashout + i * 4, (ctx->H[i] & 0xf000000));
+   for (i = 1; i < 5; i++)
+       put_be32(hashout + i * 4, 0);
 }

その後、いくつかのコミットを実行して、次のことに気付きました。

  1. 同じハッシュを持つ BLOB がすでに存在する場合、警告はまったく表示されません。すべて問題ないように見えますが、プッシュしたり、誰かがクローンを作成したり、元に戻したりすると、最新バージョンが失われます (上記の説明どおり)。
  2. ツリー オブジェクトがすでに存在し、同じハッシュで BLOB を作成した場合: プッシュを試みるか、誰かがリポジトリをクローンするまで、すべてが正常に見えます。その後、リポジトリが破損していることがわかります。
  3. コミットオブジェクトが既に存在し、同じハッシュでBLOBを作成した場合: #2と同じ - 破損
  4. BLOB がすでに存在し、同じハッシュを持つコミット オブジェクトを作成すると、「ref」の更新時に失敗します。
  5. すでに BLOB が存在し、同じハッシュを持つツリー オブジェクトを作成すると、コミットの作成に失敗します。
  6. ツリー オブジェクトがすでに存在し、同じハッシュを持つコミット オブジェクトを作成すると、"ref" の更新時に失敗します。
  7. ツリー オブジェクトがすでに存在し、同じハッシュでツリー オブジェクトを作成すると、すべて正常に見えます。ただし、コミットすると、リポジトリ全体が間違ったツリーを参照することになります。
  8. コミット オブジェクトがすでに存在し、同じハッシュでコミット オブジェクトを作成すると、すべて正常に見えます。ただし、コミットすると、コミットは作成されず、HEAD ポインターは古いコミットに移動されます。
  9. コミット オブジェクトがすでに存在し、同じハッシュを持つツリー オブジェクトを作成すると、コミットの作成に失敗します。

2 の場合、通常、「git push」を実行すると次のようなエラーが発生します。

error: object 0400000000000000000000000000000000000000 is a tree, not a blob
fatal: bad blob object
error: failed to push some refs to origin

または:

error: unable to read sha1 file of file.txt (0400000000000000000000000000000000000000)

ファイルを削除してから「git checkout file.txt」を実行します。

4 番目と 6 番目の場合、通常は次のようなエラーが発生します。

error: Trying to write non-commit object
f000000000000000000000000000000000000000 to branch refs/heads/master
fatal: cannot update HEAD ref

「git commit」を実行するとき。この場合、通常は「git commit」をもう一度入力するだけで、新しいハッシュが作成されます(タイムスタンプが変更されたため)。

#5 と#9 の場合、通常は次のようなエラーが発生します。

fatal: 1000000000000000000000000000000000000000 is not a valid 'tree' object

「git commit」を実行するとき

誰かが破損したリポジトリを複製しようとすると、通常は次のような画面が表示されます。

git clone (one repo with collided blob,
d000000000000000000000000000000000000000 is commit,
f000000000000000000000000000000000000000 is tree)

Cloning into 'clonedversion'...
done.
error: unable to read sha1 file of s (d000000000000000000000000000000000000000)
error: unable to read sha1 file of tullebukk
(f000000000000000000000000000000000000000)
fatal: unable to checkout working tree
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry the checkout with 'git checkout -f HEAD'

私が「心配」しているのは、2 つのケース (2、3) では警告なしにリポジトリが破損し、3 つのケース (1、7、8) ではすべてが正常のように見えるが、リポジトリの内容が期待したものと異なることです。クローンまたはプルする人は、あなたが持っているものとは異なる内容を持つことになります。4、5、6、および 9 のケースは、エラーで停止するので問題ありません。少なくともすべてのケースでエラーで失敗する方がよいと思います。

おすすめ記事