これはおそらく現実の世界では決して起こらなかったし、起こらないかもしれませんが、考えてみましょう:あなたがgitリポジトリを持っていて、コミットして、非常に不運になったとしましょう。すでにリポジトリに入っているものとして。質問は、Gitはこれをどのように処理するのでしょうか。単に失敗する? 2つのBLOBをリンクする方法を見つけて、コンテキストに従ってどちらが必要かを確認しますか?
実際の問題よりも頭がおかしいですが、私は問題が面白いと思いました。
この場合の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);
}
それから私はいくつかのコミットをして、次のことに気づきました。
#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の場合はエラーで停止するので問題ありません。少なくともすべてのケースでエラーで失敗した方が良いと思います。
によると プロGit :
たまたまリポジトリにある以前のオブジェクトと同じSHA-1値にハッシュされているオブジェクトをコミットした場合、Gitは以前のオブジェクトをGitデータベースに既に見て、それがすでに書き込まれていると見なします。ある時点でそのオブジェクトをもう一度チェックアウトしようとすると、必ず最初のオブジェクトのデータが取得されます。
それで失敗することはありませんが、新しいオブジェクトも保存されません。
コマンドラインでそれがどのように見えるかわかりませんが、それは確かに混乱を招くでしょう。
もう少し言えば、同じ参照がそのような衝突の可能性を説明しようとしている:
これが、SHA-1の衝突を起こすのに必要なことを理解するための例です。地球上の65億人の人間全員がプログラミングをしていて、1秒ごとにLinuxカーネルの全歴史(100万Gitオブジェクト)に相当するコードを作成し、それを1つの巨大なGitリポジトリに押し込めばそのリポジトリには、単一のSHA-1オブジェクトが衝突する確率が50%になるのに十分な数のオブジェクトが含まれていました。あなたのプログラミングチームのすべてのメンバーが、同じ夜に無関係の事件でオオカミによって攻撃され殺される可能性がより高いです。
私は暗号学者たちが祝うだろうと思います。
SHA-1に関する ウィキペディアの記事からの引用 :
2005年2月、Xiaoyun Wang、Yiqun Lisa Yin、Hongbo Yuによる攻撃が発表されました。攻撃は、フルバージョンのSHA-1で衝突を見つけることができ、2 ^ 69未満の操作しか必要としません。 (ブルートフォース検索では2 ^ 80の操作が必要になります。)
SHA-1のようなハッシュのためのいくつかの異なる攻撃モデルがありますが、通常議論されるものはMarc Stevensの HashClash ツールを含む衝突検索です。
皆さんが指摘したように、あなたはgitとハッシュ衝突を強いることができますが、そうしても別のリポジトリの既存のオブジェクトを上書きすることはありません。 git Push -f --no-thin
でも既存のオブジェクトを上書きしないと思いますが、100%確実というわけではありません。
そうは言っても、もしあなたがリモートリポジトリにハックしたら、あなたの偽のオブジェクトをもっと古いものにすることができます。 githubなどあなたが注意深かったならば、多分あなたは新しいユーザーがダウンロードしたハッキングされたバージョンを紹介することができます。
しかし私は、プロジェクトの開発者がすることができる多くのことがあなたの数百万ドルものハックを露呈させるか偶然に破壊する可能性があると思います。特に、あなたがハックしていない開発者が、影響を受けたファイルを修正した後に前述のgit Push --no-thin
を実行したことがあるとしたら、時には--no-thin
に依存しなくても、かなりの費用がかかります。