Git Internals
Blob 对象与内容寻址教程
解释 blob 如何只按内容存储,以及哈希为什么成为对象身份。
- 想建立稳定 Git 心智模型的学习者
- 经常遇到历史、引用、恢复问题的开发者
- 会看基础命令输出
- 知道提交、分支、HEAD 这些名词
- 只背底层术语却不连接到实际命令
- 把对象、引用、工作区混成一层理解
一句话理解
blob 是 Git 里最“纯粹”的对象类型之一:它只保存文件内容本身,不保存文件名、不保存路径,也不关心这个内容属于哪个分支。
1. 为什么先理解 blob
很多人第一次接触 Git internals,会下意识把"文件"和"Git 对象"当成一回事。
但 Git 其实把这两件事拆开了:
- blob 负责内容
- tree 负责结构
- commit 负责把 tree 固定成历史节点
只要这层拆分建立起来,很多底层行为都会突然变得容易理解。
2. blob 到底保存什么
blob 保存的是文件字节内容本身。
这意味着:
- 同样内容的两个文件,可以对应同一个 blob
- 文件名变了,不代表 blob 一定变了
- 路径变了,也不代表 blob 一定变了
Git 真正关心的是“内容长什么样”,不是“你给这个内容起了什么名字”。
3. 为什么 Git 说自己是内容寻址
Git 写入对象时,不是先随便分配一个编号,而是:
- 先取对象内容
- 给内容加上对象类型和长度头部
- 对这段数据计算哈希
- 用这个哈希作为对象身份
所以对象 ID 不是外部贴上的标签,而是内容本身推导出来的身份。
4. 相同内容为什么会得到相同 blob
因为 Git 对 blob 的身份判断只看内容。
例如:
README.mddocs/readme-copy.md
如果这两个文件内容完全一样,那么它们底层完全可能指向同一个 blob。
这也是 Git 能高效复用内容的原因之一。
5. blob 为什么不保存文件名
这是 Git 对象模型里非常关键的一次分工:
- blob 只管“内容是什么”
- tree 才管“这个内容在目录里叫什么名字、放在哪个位置”
这种拆分的好处是:
- 内容复用更自然
- 重命名和移动路径不必强行改变内容对象
- 对象模型更稳定
6. 从 blob 的角度看常见命令
git add
当工作区里的文件内容变化后,Git 最终会让这些内容朝“新的 blob / 新的暂存快照”靠近。
git commit
commit 不只是“记一条说明”,它会让一组内容最终通过 tree 和 commit 对象固化下来,其中底层文件内容就可能对应若干 blob。
git mv
如果只是改路径而内容不变,那么变化更多发生在 tree 结构层,而不一定是 blob 内容层。
7. blob 和“版本”不是同一个概念
一个 blob 只是某一份文件内容。
它本身不是“完整版本”,也不是“一个提交”。
真正能表达“某个时刻整个项目长什么样”的,不是 blob,而是:
- 一组 tree
- 一个根 tree
- 一个指向这个根 tree 的 commit
8. 为什么这能帮助你理解 diff 和历史
当你知道 Git 会把内容拆成 blob,对很多现象就不会再误会:
- 重命名不等于重新创造内容
- 同内容复用是自然结果
- 文件内容和目录结构是两个层次的问题
常见误区
blob 等于文件
不准确。blob 更准确地说是“文件内容对象”,而不是工作区里的那个文件实体。
同名文件一定对应同一个 blob
不一定。名字相同但内容不同,就是不同 blob。
改文件名一定会改 blob
不一定。只改名字、不改内容,往往主要影响 tree,而不是 blob。
一个最值得记住的结论
如果只记一句话,可以记:
blob 只回答“内容是什么”,从来不回答“它叫什么、放在哪里”。