git如何计算文件哈希?


124

存储在树对象git ls-tree中的SHA1哈希值(由返回sha1sum)与文件内容的SHA1哈希值(由返回)不匹配

$ git cat-file blob 4716ca912495c805b94a88ef6dc3fb4aff46bf3c | sha1sum
de20247992af0f949ae8df4fa9a37e4a03d7063e  -

git如何计算文件哈希?它会在计算哈希值之前压缩内容吗?



1
有关更多详细信息,另请参阅progit.org/book/ch9-2.html
netvope 2011年

5
netvope的链接现在似乎已断开。我认为这是新的位置:git-scm.com/book/en/Git-Internals-Git-Objects这是第9.2节从git-scm.com/book
Rhubbarb

Answers:


122

Git在对象前加上“ blob”,然后是长度(作为人类可读的整数),然后是NUL字符

$ echo -e 'blob 14\0Hello, World!' | shasum 8ab686eafeb1f44702738c8b0f24f2567c36da6d

资料来源:http//alblue.bandlem.com/2011/08/git-tip-of-week-objects.html


2
还值得一提的是,它用“ \ n”代替“ \ r \ n”,但单独留有孤立的“ \ r”。
user420667

8
^对以上评论的更正:有时 git会根据个人的eol / autocrlf设置进行上述替换。
user420667 '16

5
您也可以将此与的输出进行比较echo 'Hello, World!' | git hash-object --stdin。(可选)您可以指定--no-filters以确保不进行crlf转换,或者指定--path=somethi.ng让git使用通过指定的过滤器gitattributes(也@ user420667)。而-w实际上提交的blob .git/objects(如果你在一个混帐回购协议)。
Tobias Kienzler

表达的对等,是有道理的:echo -e 'blob 16\0Hello, \r\nWorld!' | shasum == echo -e 'Hello, \r\nWorld!' | git hash-object --stdin --no-filters ,它会随着也相当于\n与15
彼得·克劳斯

1
echo在输出中添加换行符,并将其也传递到git中。这就是为什么它的14个字符。要使用echo没有换行,写echo -n 'Hello, World!'
Bouke Versteegh

36

我只是扩大对答案@Leif Gruenwoldt和详细说明什么是在参考提供@Leif Gruenwoldt

自己做..

  • 步骤1.在存储库中创建一个空文本文档(名称无关紧要)
  • 步骤2.准备和提交文档
  • 步骤3.通过执行识别Blob的哈希 git ls-tree HEAD
  • 步骤4.找到Blob的哈希 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
  • 步骤5.突如其来,请阅读以下内容

GIT如何计算其提交哈希

    Commit Hash (SHA1) = SHA1("blob " + <size_of_file> + "\0" + <contents_of_file>)

文本blob⎵是常量前缀,\0也是常量,也是NULL字符。在<size_of_file><contents_of_file>取决于该文件。

请参阅:git commit对象的文件格式是什么?

那就是所有人!

可是等等!,您是否注意到<filename>并不是用于哈希计算的参数?如果两个文件的内容与创建日期和时间以及它们的名称相同,则它们可能具有相同的哈希值。这是Git处理移动和重命名比其他版本控制系统更好的原因之一。

自己动手(Ext)

  • 步骤6. filename在同一目录中创建另一个具有不同名称的空文件
  • 步骤7.比较两个文件的哈希值。

注意:

该链接未提及tree对象的哈希方式。我不确定算法和参数,但是从我的观察来看,它可能基于包含的所有blobstrees(可能是其哈希)计算哈希


SHA1("blob" + <size_of_file>-Blob和大小之间是否还有其他空格字符?大小为小数吗?它是零前缀的吗?
osgx

1
@osgx有。参考资料和我的测试证实了这一点。我已经纠正了答案。大小似乎是无前缀的整数形式的字节数。
塞缪尔·哈默

13

git hash-object

这是验证您的测试方法的快速方法:

s='abc'
printf "$s" | git hash-object --stdin
printf "blob $(printf "$s" | wc -c)\0$s" | sha1sum

输出:

f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f
f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f  -

sha1sum在GNU Coreutils中哪里。

然后归结为了解每种对象类型的格式。我们已经介绍了琐碎的内容blob,以下是其他内容:


如上一个答案中所述,长度应计算为$(printf "\0$s" | wc -c)。请注意添加的空字符。也就是说,如果字符串为'abc'并且在前面添加了空字符,则长度为4,而不是3。然后sha1sum的结果与git hash-object相匹配。
Michael Ekoka'4

你说得对,他们确实很匹配。似乎在这里使用printf而不是echo -e会带来一些有害的副作用。当您将git hash-object应用于包含字符串'abc'的文件时,会得到8baef1b ... f903,这是使用echo -e而不是printf时得到的。假设echo -e在字符串的末尾添加了换行符,则似乎可以将行为与printf相匹配(即s =“ $ s \ n”)。
Michael Ekoka'4

3

基于Leif Gruenwoldt答案,这是一个Shell函数替代git hash-object

git-hash-object () { # substitute when the `git` command is not available
    local type=blob
    [ "$1" = "-t" ] && shift && type=$1 && shift
    # depending on eol/autocrlf settings, you may want to substitute CRLFs by LFs
    # by using `perl -pe 's/\r$//g'` instead of `cat` in the next 2 commands
    local size=$(cat $1 | wc -c | sed 's/ .*$//')
    ( echo -en "$type $size\0"; cat "$1" ) | sha1sum | sed 's/ .*$//'
}

测试:

$ echo 'Hello, World!' > test.txt
$ git hash-object test.txt
8ab686eafeb1f44702738c8b0f24f2567c36da6d
$ git-hash-object test.txt
8ab686eafeb1f44702738c8b0f24f2567c36da6d

3

我需要在Python 3中进行一些单元测试,所以我想把它留在这里。

def git_blob_hash(data):
    if isinstance(data, str):
        data = data.encode()
    data = b'blob ' + str(len(data)).encode() + b'\0' + data
    h = hashlib.sha1()
    h.update(data)
    return h.hexdigest()

\n在任何地方都坚持使用行尾,但是在某些情况下,Git可能在计算此哈希值之前会更改行尾,因此您可能也需要.replace('\r\n', '\n')在其中输入。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.