Git 引用 (Refs)
在上一章中,我们了解到 Git 通过哈希值(40位的 SHA-1 字符串)来标识提交。但是,要让用户去记忆 a042389... 这样的哈希值简直是噩梦。
这就是 引用 (References,简称 Refs) 存在的意义。
引用本质上就是给提交哈希起的一个好记的名字,比如 master、main 或 v1.0。
引用的存储位置
Section titled “引用的存储位置”在 Git 的默认 files 后端中,松散引用 (loose refs) 通常存储为 .git/refs/ 目录下的文本文件。但为了节省 inode 和提高效率,Git 还会把大量引用合并到 .git/packed-refs 文件中(称为 packed refs)。此外,HEAD 这个特殊引用位于 .git/HEAD,并不在 .git/refs 目录内。
我们可以通过文件系统直接查看这些引用。
ls -F .git/refs/heads/ (存放本地分支)tags/ (存放标签)remotes/ (存放远程分支)1. 分支 (Heads)
Section titled “1. 分支 (Heads)”当我们执行 git branch feature 时,Git 实际上只是在 .git/refs/heads/ 下创建了一个名为 feature 的文件,并将当前 Commit 的哈希值写入其中。
验证一下:
# 查看 master 分支指向的提交cat .git/refs/heads/master1a2b3c4d5e... (一个哈希值)# 验证它是否与 git log 显示的一致git log -1 --format=%H所以,Git 的分支非常轻量。创建一个分支仅仅是写入一个很小的引用记录(通常就是一行对象 ID),无论你的项目有几百兆大,创建分支都是瞬间完成的。
2. 标签 (Tags)
Section titled “2. 标签 (Tags)”标签存储在 .git/refs/tags/ 目录下。
- 轻量标签 (Lightweight Tag):就像分支一样,文件内容直接就是 Commit 的哈希值。
- 附注标签 (Annotated Tag):文件内容指向的是一个 Tag 对象(我们在上一章提到的第四种对象),而不是直接指向 Commit。
3. 远程引用 (Remotes)
Section titled “3. 远程引用 (Remotes)”当你运行 git push 或 git fetch 时,Git 会更新 .git/refs/remotes/ 下的文件。
例如,origin/main 分支的状态就保存在 .git/refs/remotes/origin/main 文件中。
特殊引用:HEAD
Section titled “特殊引用:HEAD”HEAD 是一个非常特殊的引用,它决定了你的工作目录当前处于哪个版本。
如果你查看 .git/HEAD 文件,你会发现它通常不包含哈希值,而是指向另一个引用。这被称为 符号引用 (Symbolic Ref)。
cat .git/HEADref: refs/heads/master这句话的意思是:“我现在处于 master 分支上”。当我们提交代码时,Git 会先解析 HEAD 找到 refs/heads/master,然后更新 refs/heads/master 指向新的 Commit 哈希。
分离头指针 (Detached HEAD)
Section titled “分离头指针 (Detached HEAD)”当你检出一个具体的 Commit 哈希(而不是分支名)时:
git checkout 1a2b3c....git/HEAD 的内容会直接变成该哈希值:
cat .git/HEAD1a2b3c...这就叫 分离头指针 状态。此时 HEAD 不指向任何分支引用,因此新的提交不会更新任何分支。一旦切走,这些提交可能会被垃圾回收。
不过别太担心——Git 的 reflog 会记录 HEAD 的历史移动轨迹,默认保留 90 天。在这个窗口期内,你可以通过 git reflog 找回这些”丢失”的提交。
graph LR HEAD[HEAD File] --> RefMaster[refs/heads/master] HEAD -.-> RefFeature[refs/heads/feature]
RefMaster --> C1[Commit a1b2c3] RefFeature --> C2[Commit d4e5f6]
subgraph .git/refs RefMaster RefFeature end
subgraph .git directory HEAD end
classDef head fill:#e53e3e,stroke:#c53030,color:#fff classDef branch fill:#3182ce,stroke:#2c5282,color:#fff classDef commit fill:#4a5568,stroke:#2d3748,color:#fff
class HEAD head class RefMaster,RefFeature branch class C1,C2 commit- 分支 只是一个指向特定 Commit 的指针文件。
- HEAD 是一个指向当前所在分支的指针。
- 切换分支 (
git checkout) 实际上就是修改HEAD文件的内容,并更新工作目录的文件。 - 移动分支 (
git reset) 实际上就是修改.git/refs/heads/<branch>文件中的哈希值。
理解了 Refs,你就理解了 Git 分支操作的物理本质。