因此,我最近发现了工具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。
答案 0 :(得分:1)
git add
确实创建了blob,因为索引(或暂存区,它有很多名称...)的目的是准备快照,这将构成下一次提交。
此外,您谈论的是编辑或删除Blob,但这与该工具的原理背道而驰,因为快照必须始终可复制,并且引用的所有Blob都保持不变。在某种程度上,您从不修改任何东西,而只是添加更多的东西和关系。
要回答您的最后一点,不,您不能“甚至”回到您认为不值得保存的状态。
答案 1 :(得分:1)
但是为什么每次我对文件添加更改时似乎都创建了一个新的blob,而不是编辑现有的blob或创建一个新的blob并删除旧的blob?
无法修改任何Blob。这与有关提交的规则相同:不能更改任何提交。
原因是,每个Git对象的哈希ID (斑点和提交是内部Git对象的四种类型中的两种)只是作为该对象存储的内容的加密校验和。对于文件(“ blob”),实际内容是五个ASCII字符b
,l
,o
,b
,空间,然后将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签名或其他数字签名以及您喜欢的任何其他内容,然后可以将轻量级标签指向带注释的标签对象,以获取带注释的标签。)