但是为什么每次我对文件添加更改时似乎都会创建一个新的blob,即与编辑现有的blob,或者创建一个新的blob并删除旧的blob相反?
无法更改任何 blob。这与关于提交的规则相同:永远不能更改任何提交。
原因是每个 Git 对象的哈希 ID(blob 和提交是四种内部 Git 对象中的两种)只是作为该对象存储的内容的加密校验和。在文件(“blob”)的情况下,实际内容是五个 ASCII 字符b
, l
, o
, b
, space,然后是十进制并存储在 ASCII 中的 blob 的大小,然后是一个 ASCII NUL 字节,然后是存储的数据。例如hello
存储为 Python 可能表示为b"blob 5\0hello"
.
(您可以使用 SHA1 哈希计算此哈希,或使用git hash-object
:
$ echo -n hello | git hash-object --stdin
b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0
或者:
$ python3
[snip]
>>> import hashlib
>>> hashlib.sha1(b"blob 5\0hello").hexdigest()
'b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0'
因此,任何具有哈希 ID 的 blobb6fc4c620b67d95f953a5c1c1230aaab5db5a1b0
都必然是文件hello
,或者——如果不是——你不能hello
在这个 Git 存储库中存储包含(没有换行符)的文件。为某个文件(一个阻止存储其他文件的邪恶双胞胎)找到一个分身并非易事:请参阅新发现的 SHA-1 冲突如何影响 Git?详情。
因此,当您git add
创建一个文件时,Git 会创建一个新的 blob,或重新使用现有的 blob,具体取决于该文件的数据是否已作为存储库中的 blob 存在。如果您这样做git commit
,Git 会永久保存与新提交对象关联的内容。如果您从未提交该 blob ,并且也没有其他提交或其他实体引用它,Git 最终会通过其垃圾收集过程使该 blob 过期(请参阅 参考资料git gc
)。
(请注意,这些 Git 对象也是 zlib 压缩的,并且是所有四种 Git 对象类型的倒数第二个存储形式。但是,一段时间后,现有对象可能会打包到一个打包文件中,在那里它们与其他对象进行增量压缩在被 zlib-deflate 之前。pack 文件是最终的存储形式。如果需要,可以将打包的对象解包,尽管在正常操作中 Git 只是在扩展 delta 压缩的同时从 pack 文件中即时提取解压缩的对象数据。)
(为了完整起见,其他两种 Git 对象类型是树和带注释的标签。树对象存储文件名,从名称到 blob 哈希 ID 的映射,以及文件的可执行位。提交对象通过哈希 ID 指向表示快照的树。带注释的标记对象是一种特殊情况的数据结构,其中包含另一个 Git 对象的哈希 ID 以及数据负载;在此数据负载中,您可以存储 GPG 签名或其他一些数字签名,以及任何你喜欢的东西。然后你可以将一个轻量级标签指向带注释的标签对象,以获得带注释的标签。)