Git 如何计算文件哈希值?

问题描述 投票:0回答:7

存储在树对象中的 SHA1 哈希值(由

git ls-tree
返回)与文件内容的 SHA1 哈希值(由
sha1sum
返回)不匹配:

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

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

git hash sha1 checksum git-hash
7个回答
141
投票

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

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

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


37
投票

我只是扩展

@Leif Gruenwoldt
的答案,并详细说明@Leif Gruenwoldt
提供的
参考

中的内容

自己做..

  • 步骤 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
字符。
<size_of_file>
<contents_of_file>
因文件而异。

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

这就是大家!

但是等等!,您是否注意到

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

自己动手(分机)

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

注:

该链接没有提及

tree
对象是如何进行哈希处理的。我不确定算法和参数,但是根据我的观察,它可能会根据它包含的所有
blobs
trees
(可能是它们的哈希值)计算哈希值


19
投票

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
,以下是其他内容:


4
投票

我需要这个来进行 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')


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

0
投票

这是一个用于二进制哈希计算的python3版本(上面的例子是针对文本的)

为了便于阅读,请将此代码放入您自己的 def 中。 另请注意,代码是一个片段,而不是完整的脚本。为您带来灵感。

    targetSize: int
exists: bool
if os.path.exists(targetFile):
    exists = True
    targetSize = os.path.getsize(targetFile)
else:
    exists = False
    targetSize = 0
openMode: str
if exists:
    openMode = 'br+'
else:
    openMode = 'bw+'
with open(targetFile, openMode) as newfile:
    if targetSize > 0:
        header: str = f"blob {targetSize}\0"
        headerBytes = header.encode('utf-8')
        headBytesLen = len(headerBytes)
        buffer = bytearray(headBytesLen + targetSize)
        buffer[0:0+headBytesLen] = headerBytes
        buffer[headBytesLen:headBytesLen+targetSize] = newfile.read()
        sha1Hash = hashlib.sha1(buffer).hexdigest()
        if not sha == sha1Hash:
            newfile.truncate()
        else:
            continue
    with requests.get(fullFile) as response2:            
        newfile.write(response2.content)

0
投票

Git 2.45(2024 年第 2 季度),第 10 批 现在提供了有关此内容的官方文档。

请参阅提交 28636d7(2024 年 3 月 12 日),作者:Dirk Gouders (

dgouders-whs
)
(由 Junio C Hamano --
gitster
--
合并于 commit 509a047,2024 年 3 月 21 日)

Documentation/user-manual.txt
:生成对象哈希的示例

签署人:Dirk Gouders

添加一个关于如何手动生成对象哈希的简单示例。

此外,由于文档建议查看初始提交,请澄清自那时以来一些细节发生了变化。

user-manual
现在包含在其 手册页中:

对于“文件”(Git 的最早版本的哈希值略有不同 但结论还是一样)。

以下是一个简短的示例,演示了这些哈希值如何 可以手动生成:

我们假设一个包含一些简单内容的小文本文件:

$ echo "Hello world" >hello.txt

我们现在可以手动生成 Git 将用于此文件的哈希值:

  • 我们想要哈希的对象是“blob”类型,其大小是 12 字节。

  • 将对象标头添加到文件内容之前并将其提供给

    sha1sum

$ { printf "blob 12\0"; cat hello.txt; } | sha1sum
802992c4220de19a90767f3000a79a31b98d0df7  -

可以使用

git hash-object
验证手动构建的哈希值 这当然隐藏了标题的添加:

$ git hash-object hello.txt
802992c4220de19a90767f3000a79a31b98d0df7
© www.soinside.com 2019 - 2024. All rights reserved.