Docs Library
Git 原理专题
从对象数据库、tree 与 commit、引用、HEAD、packfiles 到常见命令的底层影响,系统建立 Git 的内部心智模型。
一句话理解
Git 的核心不是“文件版本列表”,而是一个以内容寻址对象数据库为基础、再通过引用把这些对象组织成可读历史的系统。
为什么学习 Git 原理值得
很多 Git 命令看起来像技巧题,真正难的是不知道命令在底层改了什么。一旦把内部模型建立起来,很多原本零散的现象就会连起来:
- 为什么一次 commit 会生成新的对象
- 为什么 branch 创建几乎是瞬时的
- 为什么 rebase 会产生新的提交 ID
- 为什么 reset 看起来像“回退”,但对象常常还在
- 为什么 reflog 能在误操作后帮你定位旧位置
1. Git 是内容寻址对象数据库
Pro Git 对 Git 对象模型的第一层解释非常重要:Git 底层是一个 key-value 数据存储。你把内容交给 Git,它会基于内容计算出对象 ID,并把对象存进 .git/objects。
这意味着两件事:
- Git 更关心“内容是什么”,而不是“这个文件叫啥”
- 只要对象内容不变,它的对象 ID 就稳定
2. 四类最常见对象
blob
blob 只保存文件内容本身,不保存文件名。
tree
tree 用来组织目录结构,记录“这个目录下有哪些条目,它们分别指向哪些 blob 或 tree”。
commit
commit 会把一次项目状态固定下来。它通常会指向:
- 一个 tree
- 一个父提交或多个父提交
- 作者、时间、提交说明
tag
tag 可以为某个对象附加更稳定、可读的名字,常见于发布版本。
3. 一次 commit 到底发生了什么
从内部模型看,一次 commit 并不是“只记了一行日志”,而是大致发生了这些事情:
- Git 根据暂存区内容写出 tree
- Git 创建一个新的 commit 对象
- commit 指向这次快照对应的 tree
- 当前分支引用移动到这个新提交
- HEAD 因为指向当前分支,也就等于看到了新位置
所以 commit 的本质是“新增节点并移动分支引用”,而不是在旧历史上直接修改。
4. 分支其实只是一个可移动引用
这是理解 Git 的关键转折点。分支不是仓库副本,也不是一条被单独复制出来的历史线;它只是一个指向某个提交的名字。
这解释了很多现象:
- 新建分支很快,因为只是新增一个引用
- 切换分支快,是因为 HEAD 改为跟随另一个引用
- 删除分支不一定等于提交马上消失,因为对象可能仍被别的引用或 reflog 持有
5. HEAD 是你当前观察历史的位置
在正常情况下,HEAD 是一个符号引用,它指向当前分支;当前分支再指向某个提交。
当你进入 detached HEAD 时,HEAD 不再跟随分支名,而是直接指向一个具体提交。这也是为什么你在这种状态下继续提交时,会生成一段暂时没有分支名接住的新历史。
6. 历史本质上是图,不是单纯列表
很多人把 Git 日志想成线性时间线,但 Git 真正维护的是提交图。
普通提交
普通提交通常只有一个父节点,所以看起来像一条线。
merge 提交
merge commit 往往有两个父节点,用来表达两条历史线在这里汇合。
rebase 之后
rebase 不是“改旧提交”,而是把旧改动重新生成到新基底上,所以图结构会被重新表达。
7. 为什么 rebase 会改写历史
因为 commit 对象里包含父提交信息。只要父提交变了,新的 commit 对象 ID 也会变。于是你虽然看起来“内容差不多”,但底层其实是一组新的提交对象。
这也是为什么:
- rebase 适合整理未共享历史
- rebase 不适合随便改写已共享历史
8. reset 到底改了什么
从内部角度看,reset 最重要的动作是移动引用,外加根据模式决定是否同步索引和工作区。
--soft:主要移动 HEAD / 当前分支--mixed:再把索引一起调整--hard:再把工作区也覆盖
对象数据库里的旧提交通常不会在这一刻立刻消失,它们只是可能失去容易访问的名字。
9. fetch、push 和远端跟踪引用
理解远端协作时,最好把这些名字分开:
main:你的本地分支origin/main:本地记录的远端跟踪引用
fetch 更新的是后者,而不是直接改写前者。这样 Git 才能让你先“知道远端现在在哪里”,再自己决定是否 merge、rebase 或 reset。
10. Packfiles 为什么重要
官方书还解释了 packfiles。简单理解,它们是 Git 为了更高效存储和传输对象而做的打包压缩形式。
这件事提醒我们:Git 的仓库不是简单地把每个版本完整复制一份,而是通过对象、压缩和复用来管理历史。
11. 一个理解 Git 的实用心智模型
可以把 Git 分成三层:
内容层
blob、tree、commit、tag 这些对象本身
指针层
branch、tag、HEAD、remote-tracking refs 这些名字
工作层
工作区、暂存区、命令操作体验
很多常见困惑,都是把这三层混在一起了。
12. 用这个模型重新理解常见命令
git add
把工作区中的内容准备进索引,朝下一次 tree/commit 靠近。
git commit
生成新对象并推动当前分支向前。
git merge
新增一个把两条历史线汇合起来的提交节点。
git rebase
基于新的父提交重新生成一组提交对象。
git checkout / git switch
改变 HEAD 所跟随的目标,并同步工作区视图。
git restore
把路径内容恢复到某个对象状态,不一定涉及分支移动。
13. 为什么原理能直接帮助实战
理解原理不是为了炫技,而是为了让判断更稳。比如:
- 当你看到 merge commit,就知道它是图中的汇合点
- 当你看到 detached HEAD,就知道是“指针状态变化”而不是仓库损坏
- 当你准备 reset 或 rebase,就更容易先考虑引用和恢复路径
一个最值得记住的结论
很多 Git 命令真正做的事,并不是“修改文件”这么简单,而是:
- 创建或复用对象
- 移动引用
- 同步工作区与索引的可见状态
只要你把这三件事分开理解,Git 就会清楚很多。
继续学习建议
建议顺着这条线继续读:
- Git 历史说明
- refs and HEAD
- git rebase
- reflog recovery