在树对象中存储的SHA1哈希值(由git ls-tree
返回)与文件内容的SHA1哈希值(由sha1sum
返回)不匹配:
$ git cat-file blob 4716ca912495c805b94a88ef6dc3fb4aff46bf3c | sha1sum
de20247992af0f949ae8df4fa9a37e4a03d7063e -
Git如何计算文件哈希值?它在计算哈希值之前是否对内容进行压缩?
在树对象中存储的SHA1哈希值(由git ls-tree
返回)与文件内容的SHA1哈希值(由sha1sum
返回)不匹配:
$ git cat-file blob 4716ca912495c805b94a88ef6dc3fb4aff46bf3c | sha1sum
de20247992af0f949ae8df4fa9a37e4a03d7063e -
Git如何计算文件哈希值?它在计算哈希值之前是否对内容进行压缩?
$ echo -e 'blob 14\0Hello, World!' | shasum 8ab686eafeb1f44702738c8b0f24f2567c36da6d
来源:http://alblue.bandlem.com/2011/08/git-tip-of-week-objects.htmlecho 'Hello, World!' | git hash-object --stdin
的输出进行比较。你可以选择指定 --no-filters
确保不进行 crlf 转换,或者指定 --path=somethi.ng
让 Git 使用通过 gitattributes
指定的过滤器(也 @user420667)。而 -w
则是实际将 blob 提交到 .git/objects
(如果你在 git 存储库中)。 - Tobias Kienzlerecho -e 'blob 16\0Hello, \r\nWorld!' | shasum
== echo -e 'Hello, \r\nWorld!' | git hash-object --stdin --no-filters
,并且它也等同于\n
和15。 - Peter Kraussecho
appends a newline to the output, which is also passed into git. That's why its 14 characters.To use echo without a newline, write echo -n 'Hello, World!'
- Bouke Versteegh我只是在 @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>
根据文件而异。
好了,就这些!
但是等等!你是否注意到 <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
,以下是其他类型:
$(printf "\0$s" | wc -c)
。请注意添加的空字符。也就是说,如果字符串是 'abc',在前面加上了一个空字符,则长度将会是 4,而不是 3。这样计算出来的结果将与 sha1sum 的结果匹配 git hash-object 的结果。 - Michael Ekokaprintf
而不是 echo -e
。 - go2null我需要在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 的回答,这里是一个替代 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
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)