存储在树对象中的 SHA1 哈希值(由
git ls-tree
返回)与文件内容的 SHA1 哈希值(由 sha1sum
返回)不匹配:
$ git cat-file blob 4716ca912495c805b94a88ef6dc3fb4aff46bf3c | sha1sum
de20247992af0f949ae8df4fa9a37e4a03d7063e -
Git 如何计算文件哈希值?它会在计算哈希值之前压缩内容吗?
Git 在对象前面加上“blob”前缀,后跟长度(作为 人类可读的整数),后跟一个 NUL 字符
$ echo -e 'blob 14\0Hello, World!' | shasum
8ab686eafeb1f44702738c8b0f24f2567c36da6d
来源:http://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html
我只是扩展
@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>
因文件而异。
这就是大家!
但是等等!,您是否注意到
<filename>
不是用于哈希计算的参数?如果两个文件的内容相同,无论它们的创建日期和时间以及名称如何,它们都可能具有相同的哈希值。这是 Git 比其他版本控制系统更好地处理移动和重命名的原因之一。
自己动手(分机)
- 步骤 6. 在同一目录中创建另一个具有不同
的空文件filename
- 第 7 步。比较两个文件的哈希值。
注:
该链接没有提及
tree
对象是如何进行哈希处理的。我不确定算法和参数,但是根据我的观察,它可能会根据它包含的所有 blobs
和 trees
(可能是它们的哈希值)计算哈希值
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
,以下是其他内容:
我需要这个来进行 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')
。
基于 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
这是一个用于二进制哈希计算的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)
Git 2.45(2024 年第 2 季度),第 10 批 现在提供了有关此内容的官方文档。
请参阅提交 28636d7(2024 年 3 月 12 日),作者:Dirk Gouders (
dgouders-whs
)。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