主题行中问题的正确答案:
Git 对象 SHA-1 是文件内容还是文件名?
可能是“两者都不是”,因为您指的是松散对象文件的内容,而不是原始文件,即使您指的是原始文件,这仍然不太正确。
A 松散物体在 Git 中,是一个普通文件。文件的名称是根据对象的哈希 ID 构造的。对象的哈希 ID 又是通过计算对象内容的哈希来构造的附加前缀标头.
带前缀的标头取决于对象类型。有四种类型:blob
, commit
, tag
, and tree
。标头由一个以零结尾的字节字符串组成,该字符串由 ASCII(或等效的 UTF-8)字节字符串形式的类型名称组成,后跟一个空格,后跟对象大小的十进制表示形式(以字节为单位),最后是通过 ASCII NUL (b'\x00'
在 Python 中,如果您更喜欢现代 Python 表示法,或者'\0'
如果你更喜欢C)。
标头之后是实际的对象内容。因此,对于包含字节字符串的文件b'hello\n'
,要散列的数据包括b'blob 6\0hello\n
:
$ echo 'hello' | git hash-object -t blob --stdin
ce013625030ba8dba906f756967f9e9ca394464a
$ python3
[...]
>>> import hashlib
>>> s = b'blob 6\0hello\n'
>>> hashlib.sha1(s).hexdigest()
'ce013625030ba8dba906f756967f9e9ca394464a'
因此,用于存储该文件的文件名是(派生自)ce013625030ba8dba906f756967f9e9ca394464a
。作为一个松散的物体,它变成.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
.
The contents然而,该文件的 zlib 压缩形式是b'blob 6\0hello\n'
(显然,level=1
— 当前默认值为 6,结果在该级别不匹配;目前尚不清楚 Git 的 zlib deflate 是否与 Python 完全匹配,但使用级别 1 确实可以在这里工作):
$ echo 'hello' | git hash-object -w -t blob --stdin
ce013625030ba8dba906f756967f9e9ca394464a
$ vis .git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
x\^AK\M-J\M-IOR0c\M-HH\M-M\M-I\M-I\M-g\^B\000\^]\M-E\^D\^T$
(注意最后的$
又是 shell 提示符;现在回到Python3)
>>> import zlib
>>> zlib.compress(s, 1)
b'x\x01K\xca\xc9OR0c\xc8H\xcd\xc9\xc9\xe7\x02\x00\x1d\xc5\x04\x14'
>>> import vis
>>> print(vis.vis(zlib.compress(s, 1)))
x\^AK\M-J\M-IOR0c\M-HH\M-M\M-I\M-I\M-g\^B\^@\^]\M-E\^D\^T
where vis.py
is:
def vischr(byte):
"encode characters the way vis(1) does by default"
if byte in b' \t\n':
return chr(byte)
# control chars: \^X; del: \^?
if byte < 32 or byte == 127:
return r'\^' + chr(byte ^ 64)
# printable characters, 32..126
if byte < 128:
return chr(byte)
# meta characters: prefix with \M^ or \M-
byte -= 128
if byte < 32 or byte == 127:
return r'\M^' + chr(byte ^ 64)
return r'\M-' + chr(byte)
def vis(bytestr):
"same as vis(1)"
return ''.join(vischr(c) for c in bytestr)
(vis
生成可逆但可打印的二进制文件编码;这是我 1993 年对以下问题的回答cat -v
).
请注意,文件名存储在 Git 存储库中(在提交下)仅显示为路径名组成部分存储在个人tree
对象。计算树对象的哈希 ID 并不简单;我的 Python 代码在我的公共“脚本”存储库中执行此操作,位于gitash.py https://github.com/chris3torek/scripts/blob/master/githash.py.