为什么git在'git add'命令之间产生新的斑点?

时间:2019-03-29 19:09:34

标签: git tree blob git-index

因此,我最近发现了工具git cat-file,并且一直在使用它。我知道git使用blob来存储实际内容。但是,为什么每次我git add更改文件时,似乎都创建一个新的blob,即与编辑现有的blob或制作一个新的blob并删除旧的blob相反?

例如

touch hello.txt
// change hello.txt to contains 'hello'
git add hello.txt // creates a blob abc123 containing: 'hello'  

// change hello.txt to 'hello world'
git add hello.txt // creates a blob cba321 containing: 'hello world'  

git commit // creates a commit with tree pointing at blob cba321

因此,包含我的中间阶段性更改的Blob(即包含“ hello”的Blob abc123)的用途并不明显。

在提交方面,hello.txt从“”直接变为“ hello world”,我什至不能不深入git blob就找回我的中间更改abc123。

2 个答案:

答案 0 :(得分:1)

git add确实创建了blob,因为索引(或暂存区,它有很多名称...)的目的是准备快照,这将构成下一次提交。

此外,您谈论的是编辑或删除Blob,但这与该工具的原理背道而驰,因为快照必须始终可复制,并且引用的所有Blob都保持不变。在某种程度上,您从不修改任何东西,而只是添加更多的东西和关系。

要回答您的最后一点,不,您不能“甚至”回到认为不值得保存的状态。

答案 1 :(得分:1)

  

但是为什么每次我对文件添加更改时似乎都创建了一个新的blob,而不是编辑现有的blob或创建一个新的blob并删除旧的blob?

无法修改任何Blob。这与有关提交的规则相同:不能更改任何提交。

原因是,每个Git对象的哈希ID (斑点和提交是内部Git对象的四种类型中的两种)只是作为该对象存储的内容的加密校验和。对于文件(“ blob”),实际内容是五个ASCII字符blob空间,然后将blob的大小十进制化并存储为ASCII,然后是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为b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0的blob都必须是文件hello,或者,如果不是,则不能在其中存储包含hello(没有换行符)的文件Git存储库。查找某个文件(防止存储其他文件的邪恶双胞胎)非常简单:有关详细信息,请参见How does the newly found SHA-1 collision affect Git?

因此,当您git add文件时,Git会创建一个新的Blob或重新使用现有的Blob,具体取决于该文件的 data 在存储库中是否已作为Blob存在。如果然后git commit,Git将与新提交对象相关联的内容永久保存。如果您从不提交该blob,并且没有其他提交或其他实体引用它,那么Git最终将通过其垃圾回收过程使blob失效(请参见git gc)。

(请注意,这些Git对象也是zlib压缩的,并且是所有四种Git对象类型的倒数第二个存储形式。但是,一段时间后,现有对象可能被包装打包文件,在对它们进行zlib压缩之前,将它们与其他对象进行增量压缩。打包文件是最终的存储形式。打包对象可以根据需要进行解压缩,尽管在正常操作中,Git只会提取解压缩的对象压缩文件中的动态数据,同时扩展了增量压缩。

(出于完整性考虑,其他两种Git对象类型是 tree 带注释的标记。树对象存储文件 names ,该文件映射自blob哈希ID的名称,以及文件的可执行位。提交对象通过哈希ID指向代表快照的树。带注释的标记对象是一种特殊情况的数据结构,其中包含另一个Git对象的哈希ID,加上一个数据有效负载;在此数据有效负载中,您可以存储GPG签名或其他数字签名以及您喜欢的任何其他内容,然后可以将轻量级标签指向带注释的标签对象,以获取带注释的标签。)