Git Internals
Git 中的引用与 HEAD
把分支、标签、远端跟踪引用和 HEAD 放到同一个模型里,理解 Git 如何用名字指向提交。
一句话理解
提交对象是历史节点,而引用是指向这些节点的名字。HEAD 则表示你当前正站在哪个名字或哪个提交上。
1. 为什么“引用”比命令参数更值得先理解
很多 Git 命令本质上都在做两件事:
- 创建新对象
- 移动引用
如果只记命令表面行为,就容易在 reset、rebase、checkout、reflog 这些场景里迷路。
2. 什么是引用
引用可以理解成一个更容易记忆的入口名。常见例子有:
mainfeature/loginv2.0.0origin/main
它们背后最终都落到某个提交对象上。
3. 分支为什么只是“可移动引用”
分支不是独立仓库副本,也不是一条被复制出来的历史线。它只是一个可以随着新提交向前移动的名字。
这解释了三个常见现象:
- 新建分支很快,因为只是在新增一个引用
- 切换分支很快,因为只是让 HEAD 去跟随另一个引用
- 删除分支不代表提交马上消失,因为对象可能还被别的引用或 reflog 接着
4. 标签和分支有什么不同
都属于引用,但习惯上用途不同:
- 分支通常会继续向前移动
- 标签更像把某个位置固定成一个稳定名字
所以分支更偏“协作中的当前位置”,标签更偏“发布或里程碑标记”。
5. HEAD 到底是什么
在日常正常状态下,HEAD 通常是一个符号引用。它不是直接指向提交,而是先指向当前分支,再由分支指向提交。
这就是为什么:
- 切换分支会让 HEAD 跟随新的分支名
- commit 后当前分支向前移动,HEAD 看起来也就跟着前进
- reset 会改变当前分支与 HEAD 共同指向的位置
6. 什么是 detached HEAD
当 HEAD 不再跟随分支名,而是直接指向某个提交时,就进入了 detached HEAD。
它不是仓库损坏,而是说明你暂时站在“一个具体提交”上,而不是“一个可移动的分支名”上。
这时如果继续提交:
- 新提交仍然会被创建
- 但不会自动挂到某个分支名下面
- 如果想保留,就应该尽快新建分支把这段历史接住
7. 远端跟踪引用为什么重要
main 和 origin/main 不是一回事:
main是你的本地分支origin/main是本地记录的远端状态
git fetch 更新的是后者。也正因为这两者分开,Git 才能让你先观察远端变化,再决定是否 merge、rebase 或 reset。
8. reflog 为什么常常能救命
reflog 本质上记录的是引用移动历史。很多恢复操作之所以能成功,不是因为 Git 知道你“后悔了”,而是因为它还记得某个引用之前指向过哪里。
这也是为什么:
- reset 过头经常还能找回来
- rebase 出错时也常能定位旧位置
- 删除分支后如果 reflog 还在,往往依然有恢复机会
常见误区
HEAD 就是当前提交
更准确的说法是:HEAD 常常通过当前分支间接定位到当前提交。只有在 detached HEAD 时,它才直接指向提交。
删除分支等于删除提交
不一定。删除的是“名字”,不是立即销毁对象。只要还有别的引用或 reflog 接着,这些提交通常还在。
一个最值得记住的结论
很多 Git 故障感,实际上都是“名字变了”而不是“对象坏了”。把对象和引用分开理解,你对 branch、HEAD、reflog、reset 的判断会稳定很多。