编辑,2017 年 12 月下旬:Git version 2.16 is gradually acquiring internal interfaces to allow for different hashes。还有很长的路要走。
简短(但不令人满意)的答案是示例文件对 Git 来说不是问题,但两个其他(仔细计算)文件可能是。
我下载了这两个文件,shattered-1.pdf 和 shattered-2.pdf,并将它们放入一个新的空存储库中:
macbook$ shasum shattered-*
38762cf7f55934b34d179ae6a4c80cadccbb7f0a shattered-1.pdf
38762cf7f55934b34d179ae6a4c80cadccbb7f0a shattered-2.pdf
macbook$ cmp shattered-*
shattered-1.pdf shattered-2.pdf differ: char 193, line 8
macbook$ git init
Initialized empty Git repository in .../tmp/.git/
macbook$ git add shattered-1.pdf
macbook$ git add shattered-2.pdf
macbook$ git status
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: shattered-1.pdf
new file: shattered-2.pdf
即使这两个文件具有相同的 SHA-1 校验和(并且显示大致相同,尽管一个具有红色背景,另一个具有蓝色背景),它们获得不同的 Git 哈希:
macbook$ git ls-files --stage
100644 ba9aaa145ccd24ef760cf31c74d8f7ca1a2e47b0 0 shattered-1.pdf
100644 b621eeccd5c7edac9b7dcba35a8d5afd075e24f2 0 shattered-2.pdf
这些是存储在 Git 中的文件的两个 SHA-1 校验和:一个是 ba9aa...,另一个是 b621e...。 38762c... 也不是。但是——为什么?
答案是,Git 存储文件,不是作为文件本身,而是作为字符串文字blob,一个空格,文件大小十进制,一个 ASCII NUL 字节,然后文件数据。两个文件大小完全相同:
macbook$ ls -l shattered-?.pdf
... 422435 Feb 24 00:55 shattered-1.pdf
... 422435 Feb 24 00:55 shattered-2.pdf
因此两者都以文字文本 blob 422435\0 为前缀(其中 \0 表示单个字节,字符串中的 la C 或 Python 八进制转义)。
也许令人惊讶——如果你知道 SHA-1 是如何计算的——将相同的前缀添加到两个不同的文件中,而这两个文件之前却产生了相同的校验和 ,导致它们现在产生不同的校验和。
这应该不足为奇的原因是,如果最终校验和结果不对每个输入位的位置以及值非常敏感,它通过获取已知的输入文件并仅重新排列其中的一些位,将很容易按需产生冲突。尽管char 193, line 8 的字节不同,这两个输入文件产生了相同的总和,但据研究人员称,这是通过尝试超过 9 个 quintillion (short scale) 输入来实现的。为了得到这个结果,他们将精心挑选的原始数据块放入他们控制的位置,这会影响总和,直到他们找到导致冲突的输入对。
通过添加 blob 标头,Git 移动了位置,在一次或多或少的意外打嗝中破坏了 110 个 GPU 年的计算。
现在,知道 Git 会执行此操作,他们可以重复使用以 blob 422435\0 开头的输入(前提是他们的牺牲块也不会被推来推去)很多;并且实际需要的 GPU 计算年数可能会有所不同,因为这个过程有点 stochastic)。然后他们会想出两个 不同的 文件,可以去掉 blob 标头。这两个文件现在会有不同的 SHA-1 校验和,但是当git add-ed 时,两者都会产生相同的 SHA-1 校验和。
在这种特殊情况下,添加的第一个文件将“赢得”该插槽。 (假设它被命名为shattered-3.pdf。)一个足够好的Git——我完全不确定当前的Git是否这么好;参见Ruben's experiment-based answer 到How would Git handle a SHA-1 collision on a blob?——会注意到git add shattered-4.pdf 在尝试添加第二个文件时与第一个但不同的shattered-3.pdf 发生冲突,并会警告您并使git add 步骤失败。在任何情况下,您都无法将这两个文件都添加到单个存储库中。
但首先,有人必须花费更多的时间和金钱来计算新的哈希冲突。