Git 如何计算文件散列?

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

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

Git 如何计算文件散列? 是否在计算散列之前压缩内容?

49735 次浏览

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

$Echo-e‘ blob 140 Hello,World! | shasum 8ab686eafeb1f44702738c8b0f24f2567c36da6d

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

我只是通过 @Leif Gruenwoldt扩展了答案,并详细说明了 @Leif Gruenwoldt提供的 参考文献中的内容

自己动手。

  • 步骤1。在存储库中创建一个空文本文档(名称不重要)
  • 步骤2. 分阶段提交文档
  • 步骤3。通过执行 git ls-tree HEAD来识别 blob 的散列
  • 步骤4。找到该块的散列为 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
  • 第五步,从惊讶中清醒过来,阅读下面的内容

GIT 如何计算它的提交哈希

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

文本 blob⎵是常量前缀,\0也是常量,是 NULL字符。<size_of_file><contents_of_file>根据文件的不同而不同。

见: Git 提交对象的文件格式是什么?

就这样了,伙计们!

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

自己动手(Ext)

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

注:

链接没有提到如何散列 tree对象。我不确定的算法和参数,但从我的观察,它可能计算一个哈希的基础上所有的 blobstrees(他们的哈希可能)它包含

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

基于 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

我需要在 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')