为什么git hash-object返回与openssl sha1不同的哈希?

twc*_*per 44 git openssl sha1

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

我想验证校验和,该特定文件的校验和发布为862456662a11e2f386ff0b24fdabcb4f6c1c446a(SHA-1). git hash-object给了我一个不同的哈希,但openssl sha1返回了预期的862456662a11e2f386ff0b24fdabcb4f6c1c446a.

以下实验似乎排除了任何可能的下载损坏或换行差异,并表明实际上有两种不同的算法在起作用:

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

这是怎么回事?

Mar*_*air 58

您会看到一个区别,因为git hash-object它不仅仅是对文件中的字节进行哈希处理 - 它在字符串"blob"之后加上文件大小,并在散列之前将NUL添加到文件的内容中.Stack Overflow的另一个答案中有更多细节:

或者,为了说服自己,尝试以下方法:

$ echo -n hello | git hash-object --stdin
b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0

$ printf 'blob 5\0hello' > test.txt
$ openssl sha1 test.txt
SHA1(test.txt)= b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0
Run Code Online (Sandbox Code Playgroud)

  • 此外,文件开头的"blob <filesize>\0"(或类似文件)意味着只需解压缩目标文件的第一个字节就可以非常快速地告诉对象的类型.在[Git Objects上的Pro Git的精彩章节](http://progit.org/book/ch9-2.html#object_storage)的这一部分中,有更多关于压缩以及实际写入磁盘的内容. (5认同)
  • 为什么git作者选择了这种行为? (4认同)
  • @liori:我想这是为了确保你没有一个具有相同对象名称(SHA1sum)的blob作为提交或树等等 - 每个都有(至少)它们的类型在哈希之前预先添加计算. (3认同)
  • @liori:git会以这种方式使用sha-1是有道理的,因为它的目的是对文件树进行版本控制,这不是像sha1sum或md5sum这样的cmd line utils的用途. (2认同)
  • @liori:git 中所有类型的“对象”(blob、提交、标签和树)均由哈希命名。有一个命令 `cat-file -t`,例如 `git cat-file -t a7bb6fb0` 告诉您名称(哈希)以 a7bb6fb0 开头的对象的“类型”...它可以做到这一点,因为实际对象(存储在存储库中,压缩)以“blob”或“tree”或其他名称开头。您可以使用`python -c "import zlib,sys;print repr(zlib.decompress(sys.stdin.read()))" &lt; .git/objects/a7/bb6fb0*`之类的命令查看该对象。无论如何,总结是 git 的名称是 git“对象”的哈希值,而不仅仅是内部的 blob。 (2认同)

ara*_*nid 5

SHA1 摘要是通过后跟文件数据的标头字符串计算得出的。标头由对象类型、空格和以字节为单位的十进制对象长度组成。这通过空字节与数据分开。

所以:

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

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

e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 始终表示“空文件” 4b825dc642cb6eb9a060e54bf8d69288fbee4904 始终表示“空目录”

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