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