git 如何计算文件哈希?

Posted

技术标签:

【中文标题】git 如何计算文件哈希?【英文标题】:How does Git compute file hashes? 【发布时间】:2011-11-05 17:18:24 【问题描述】:

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

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

它会在计算哈希之前压缩内容吗?

【问题讨论】:

见assigning Git SHA1's without Git 更多详情,另见progit.org/book/ch9-2.html netvope 的链接现在似乎已失效。我认为这是新位置:git-scm.com/book/en/Git-Internals-Git-Objects 这是来自git-scm.com/book 的§9.2 相关:What is the file format of a git commit object? 【参考方案1】:

Git 以“blob”作为对象的前缀,后跟长度(作为 人类可读的整数),后跟一个 NUL 字符

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

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

【讨论】:

另外值得一提的是,它将“\r\n”替换为“\n”,但留下孤立的“\r”。 ^对上述评论的更正:有时 git 会根据一个人的 eol/autocrlf 设置进行上述替换。 您也可以将其与echo 'Hello, World!' | git hash-object --stdin 的输出进行比较。或者,您可以指定 --no-filters 以确保不发生 crlf 转换,或指定 --path=somethi.ng 以让 git 使用通过 gitattributes 指定的过滤器(也是 @user420667)。和 -w 实际将 blob 提交给 .git/objects(如果您 在 git repo 中)。 表示等价,有意义:echo -e 'blob 16\0Hello, \r\nWorld!' | shasum == echo -e 'Hello, \r\nWorld!' | git hash-object --stdin --no-filters 也将与\n 和 15 等价。 echo 在输出中附加一个换行符,该换行符也传递给 git。这就是为什么它有 14 个字符。要使用不带换行符的 echo,请编写 echo -n 'Hello, World!'【参考方案2】:

我只是扩展@Leif Gruenwoldt 的答案并详细说明@Leif Gruenwoldt 提供的reference 中的内容

自己动手..

步骤 1. 在您的存储库中创建一个空文本文档(名称无关紧要) 第 2 步。暂存并提交文档 步骤 3. 通过执行 git ls-tree HEAD 识别 blob 的哈希值 步骤 4. 找到 blob 的哈希为 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 第 5 步。摆脱惊喜,阅读下文

GIT 如何计算其提交哈希

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

文本blob⎵ 是一个常量前缀,\0 也是常量并且是NULL 字符。 &lt;size_of_file&gt;&lt;contents_of_file&gt; 因文件而异。

见:What is the file format of a git commit object?

这就是所有人!

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

自己动手(分机)

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

注意:

该链接没有提到tree 对象是如何被散列的。我不确定算法和参数,但是根据我的观察,它可能会根据它包含的所有 blobstrees (可能是它们的哈希值)计算一个哈希值

【讨论】:

SHA1("blob" + &lt;size_of_file&gt; - blob 和大小之间是否有额外的空格字符?大小是十进制吗?它是零前缀的吗? @osgx 有。参考和我的测试证实了这一点。我已经更正了答案。大小似乎是没有前缀的整数字节数。【参考方案3】:

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,下面是其他的:

提交:What is the file format of a git commit object? 树:What is the internal format of a git tree object? 标签:How is a Git Tag Object SHA1 Created?

【讨论】:

如上一个答案中所述,长度应该计算为$(printf "\0$s" | wc -c)。注意添加的空字符。也就是说,如果字符串是 'abc' 并且前面添加了空字符,则长度将产生 4,而不是 3。那么 sha1sum 的结果与 git hash-object 匹配。 你说得对,他们确实匹配。似乎在这里使用 printf 而不是 echo -e 会产生一些有害的副作用。当您将 git hash-object 应用于包含字符串“abc”的文件时,您会得到 8baef1b...f903,这是使用 echo -e 而不是 printf 时得到的。假设 echo -e 在字符串末尾添加了一个换行符,似乎为了匹配 printf 的行为,您可以执行相同的操作(即 s="$s\n")。【参考方案4】:

基于Leif Gruenwoldt 的答案,这里是git hash-object 的shell 函数替代:

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

【讨论】:

【参考方案5】:

我在 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 在计算此哈希之前也可能是changing your line endings,因此您可能还需要一个.replace('\r\n', '\n')

【讨论】:

以上是关于git 如何计算文件哈希?的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有 Git 的情况下将 Git SHA1 分配给文件?

如何使用git(git config --global)?

GIT 在特定提交之前获取提交哈希

如何在不造成损害的情况下计算 sqlite 数据库文件的哈希

如何将提交哈希添加到可执行文件的详细信息

『技术群里聊些啥』.NET 如何计算文件 MD5 哈希