【问题标题】:Git objects SHA-1 are file contents or file names?Git 对象 SHA-1 是文件内容还是文件名?
【发布时间】:2017-06-10 17:02:01
【问题描述】:

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

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

当我在文本编辑器中打开文件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 代表。

【问题讨论】:

  • 这就是我现在正在经历的,但我仍然不清楚文件内容部分,因此我不得不在这里问..
  • 您能否将您的问题编辑为有关该链接“对象存储”标题下描述的某些特定方面的难以理解的问题?
  • 使用命令git cat-file -p [sha1] 探索您的存储库对象,您会更好地理解...

标签: git git-hash


【解决方案1】:

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

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

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

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

前缀标题取决于对象类型。有四种类型:blobcommittagtree。标头由一个以 0 结尾的字节字符串组成,该字符串由作为 ASCII(或等效的 UTF-8)字节字符串的类型名称组成,后跟一个空格,然后是对象大小的十进制表示(以字节为单位),然后是通过 ASCII NUL(如果您更喜欢现代 Python 表示法,则在 Python 中为b'\x00',如果您更喜欢 C,则为'\0')。

在标题之后是实际的对象内容。因此,对于包含字节字符串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

然而,该文件的 内容b'blob 6\0hello\n' 的 zlib 压缩形式(显然,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

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)

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

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

【讨论】:

  • 至少这一次,它更“简洁”且切中要害。 +1
  • @torek,根据你的回答,我试过了。 $ git cat-file -p 16ed 输出:100644 blob a7036e7253f5e7099e8b68c2fe99ecf5f8b013d3 pom.xml 。然后,cat pom.xml | git hash-object -t blob 输出:af86ccbdb1f4f10685ffe85cf68372109694e49a。 pom.xml 只有一个修订版(不确定如何用 git 术语表达),所以我在使用 git-hash-object 时不可能指代 pom.xml 的不同版本。 为什么 SHA-1 不匹配。
  • @samshers:您是否打开了过滤器(清洁和涂抹过滤器)和/或 crlf hacking?如果是这样,请使用&lt;filter&gt; pom.xlm | git hash-object -t blob,因为树和索引哈希来自 filtered 内容,而不是 work-tree 内容。 (插入适当的命令,不管它是什么,作为过滤器——例如,如果工作树副本有 CRLF 结尾,您可以使用 tr -d '\015' &lt; pom.xml 作为过滤器。)
  • @torek - 无可挑剔。 $ cat pom.xml | tr -d '\015' | git hash-object -t blob --stdin 输出 a7036e7253f5e7099e8b68c2fe99ecf5f8b013d3 。但我想澄清的另一件事 - 所以 SHA-1 是根据未压缩的内容计算的。但文件中(在对象的数据库中)的实际内容主要是压缩内容。对。
  • @samshers:是的,散列在未压缩的内容上(包括blob &lt;size&gt;\0 字节)。实际的 in-Git 数据要么是松散对象(zlib 压缩),要么是打包对象(可能是 deltified)。 (哎呀,我忘了这个答案中有 Python 示例!)
【解决方案2】:

Git Magic 提及:

顺便说一句,.git/objects 中的文件是用 zlib 压缩的,所以你不应该直接盯着它们看。通过zpipe -d 过滤它们,或输入(使用git cat-file):

$ git cat-file -p .git/objects/0c/15af113a95643d7c244332b0e0b287184cd049

zpipe:

$ ./zpipe -d < .git/objects/0c/15af113a95643d7c244332b0e0b287184cd049

注意:对于zpipe,我必须先编译zpipe.c

sudo apt-get install zlib1g-dev
cd /usr/share/doc/zlib1g-dev/examples
sudo gunzip zpipe.c.gz
sudo gcc -o zpipe zpipe.c -lz

然后:

$ /usr/share/doc/zlib1g-dev/examples/zpipe -d < /usr/share/doc/zlib1g-dev/examples/zpipe -d <

你会得到如下结果:

vonc@VONCAVN7:/mnt/d/git/seec$ /usr/share/doc/zlib1g-dev/examples/zpipe -d < .git/objects/0d/b6225927ef60e21138a9762c41ea0db714ca0d
blob 2142 <full content there...>

您会看到由类型和内容大小组成的标题,然后是实际内容。

请参阅 Jeff Kunkle 的“Understanding Git Internals”幻灯片 8,了解 blob 实际内容的说明:

【讨论】:

  • 所以 SHA-1 命名文件中的内容是“header+content”的压缩形式(使用 zlib)。而 SHA-1 仅使用“标题+内容”计算。对吗???
  • @samshers 正如 torek 所说:压缩+增量仅用于存储对象。 SHA1 用于引用(未压缩的)内容(+ 标头)。很快这将使用 SHA-256:stackoverflow.com/a/47838703/6309
猜你喜欢
  • 1970-01-01
  • 2011-01-21
  • 1970-01-01
  • 1970-01-01
  • 2014-07-20
  • 2016-05-09
  • 2019-03-02
  • 1970-01-01
  • 2010-12-29
相关资源
最近更新 更多