为什么 git 会在'git add'命令之间创建新的 blob?



所以我最近发现了这个工具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。

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

任何斑点都无法更改。 这与关于提交的规则相同:任何提交都不能更改。

原因是每个 Git 对象的哈希 ID(blob 和提交是四种类型的内部 Git 对象中的两种)只是存储为该对象的内容的加密校验和。 对于文件("blob"),实际内容是五个ASCII字符blob空格,然后是十进制并存储在ASCII中的blob的大小,然后是ASCII NUL字节,然后是存储的数据。 例如,hello存储为 Python 可能表示为b"blob 5hello"

(您可以使用 SHA1 哈希器或使用git hash-object计算此哈希

$ echo -n hello | git hash-object --stdin
b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0

或:

$ python3
[snip]
>>> import hashlib
>>> hashlib.sha1(b"blob 5hello").hexdigest()
'b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0'

因此,任何具有哈希 IDb6fc4c620b67d95f953a5c1c1230aaab5db5a1b0的 blob 都必须是文件hello,或者(如果不是),则无法在此 Git 存储库中存储包含hello的文件(不带换行符)。 为某个文件(阻止存储其他文件的邪恶双胞胎)找到分身并非易事:请参阅新发现的 SHA-1 冲突如何影响 Git?了解详情。

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

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

(为了完整起见,其他两种 Git 对象类型是带注释的标记。 树对象存储文件名、从名称到 blob 哈希 ID 的映射,以及文件的可执行位。 提交对象通过哈希 ID 引用表示快照的树。带注释的标记对象是一种特殊情况的数据结构,其中包含另一个 Git 对象的哈希 ID 以及数据有效负载;在此数据有效负载中,您可以存储 GPG 签名或其他一些数字签名,以及您喜欢的任何其他内容。 然后,可以将轻量级标记指向带批注的标记对象,以获取带批注的标记。

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

此外,您还谈到编辑或删除 blob,但这与该工具的原则背道而驰,因为快照必须始终是可重现的,并且它引用的所有 blob 都保持不变。在某种程度上,你永远不会修改任何东西,你只是添加更多的东西和关系。

回答你的最后一点,不,你不能"甚至"回到认为不值得拯救的状态。

最新更新