Git对象SHA-1是文件内容还是文件名?

app*_*ppu 4 git git-hash

我对文件的实际内容如何存储在.git中感到困惑。

例如,Version 1中的实际文本内容test.txt。当我提交(第一次提交)到仓库时,git为位于的文件返回SHA-1 .git\objects\0c\15af113a95643d7c244332b0e0b287184cd049

当我15af113a95643d7c244332b0e0b287184cd049在文本编辑器中打开文件时,这都是垃圾,就像这样

x+)JMU074f040031QÐKÏ,ÉLÏË/Je¨}ºõw[Éœ„ÇR­ ñ·Î}úyGª*±8#³¨,1%>9?¯$5¯D¯¤¢„áôÏ3%³þú>š~}Ž÷*ë²-¶ç¡êÊòR“KâKòãs+‹sô

但是我不确定这个垃圾代表的是文本的加密形式Version 1还是由SHA-1代表15af113a95643d7c244332b0e0b287184cd049

tor*_*rek 5

主题行中问题的正确答案:

Git对象SHA-1是文件内容还是文件名?

可能是“两者都不是”,因为您指的是松散对象文件的内容,而不是原始文件的内容,即使您引用的是原始文件,这仍然不太正确。

在Git中,一个松散的对象是一个纯文件。文件名是根据对象的哈希ID构造的。反过来,对象的哈希ID是通过计算带有前缀标头的对象内容的哈希来构造的。

前缀标题取决于对象类型。有四种类型:blobcommittag,和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'
Run Code Online (Sandbox Code Playgroud)

因此,将用于存储该文件的文件名是(源自)ce013625030ba8dba906f756967f9e9ca394464a。作为松散的物体,它变成.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a

但是,该文件的内容是zlib压缩的形式b'blob 6\0hello\n'(显然,level=1—当前默认为6,结果在该级别不匹配;尚不清楚Git的zlib deflate是否与Python的完全匹配,而是使用level 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$
Run Code Online (Sandbox Code Playgroud)

(请注意,最后一个$还是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
Run Code Online (Sandbox Code Playgroud)

在哪里vis.py

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)
Run Code Online (Sandbox Code Playgroud)

vis产生二进制文件的可逆但可打印的编码;这是我对1993年问题的解答cat -v)。

请注意,存储在Git存储库中(在提交下)的文件名仅显示为存储在各个对象中的路径名组件tree。计算树对象的哈希ID很简单。我在githash.py下的公共“脚本”存储库中有执行此操作的Python代码。