为什么git散列对象返回不同于openssl sha1的散列?

上下文:我从code.google下载了一个文件(Audirvana 0.7.1.zip)到我的Macbook Pro(Mac OS X 10.6.6)。

我想validation该特定文件的校验和发布为862456662a11e2f386ff0b24fdabcb4f6c1c446a(SHA-1)。 git hash-object给了我不同的散列,但openssl sha1返回了预期的862456662a11e2f386ff0b24fdabcb4f6c1c446a。

下面的实验似乎排除了任何可能的下载损坏或换行符差异,并指出实际上存在两种不同的algorithm:

 $ echo A > foo.txt $ cat foo.txt A $ git hash-object foo.txt f70f10e4db19068f79bc43844b49f3eece45c4e8 $ openssl sha1 foo.txt SHA1(foo.txt)= 7d157d7c000ae27db146575c08ce30df893d3a64 

这是怎么回事?

你看到了一个区别,因为git hash-object不仅仅是对文件中的字节进行散列 – 它在string前面加上了string“blob”,之后是文件大小,在散列之前是NUL。 在堆栈溢出的其他答案有更多的细节:

  • 如何将Git SHA1分配给没有Git的文件?

或者,要说服自己,请尝试如下所示:

 $ echo -n hello | git hash-object --stdin b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0 $ printf 'blob 5\0hello' > test.txt $ openssl sha1 test.txt SHA1(test.txt)= b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0 

SHA1摘要是通过头string和文件数据进行计算的。 标题由对象types,空格和对象长度(以字节为单位)组成。 这是通过空字节从数据中分离出来的。

所以:

 $ git hash-object foo.txt f70f10e4db19068f79bc43844b49f3eece45c4e8 $ ( perl -e '$size = (-s shift); print "blob $size\x00"' foo.txt \ && cat foo.txt ) | openssl sha1 f70f10e4db19068f79bc43844b49f3eece45c4e8 

这样做的一个后果就是“空树”和“空块”具有不同的ID。 那是:

e69de29bb2d1d6434b8b29ae775ad8c2e48c5391总是表示“空文件”4b825dc642cb6eb9a060e54bf8d69288fbee4904总是表示“空目录”

你会发现你实际上可以在没有注册对象的新git仓库中执行git ls-tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 ,因为它被识别为一个特殊情况,并且从未实际存储(使用现代Git版本)。 相比之下,如果你添加一个空的文件到你的仓库,一个blob“e69de29bb2d1d6434b8b29ae775ad8c2e48c5391”将被存储。

答案在这里:

如何将Git SHA1分配给没有Git的文件?

git计算文件元数据+内容,而不仅仅是内容。

现在这是一个足够好的答案,而且git并不是校验和下载的工具。