git分支指针是存储在.git/refs/heads/下的文本文件,每次commit仅重写该文件中的一行哈希值,不复制文件、不扫描历史;head为符号引用,指向当前分支,决定指针移动方向;合并时分支指针单次跳转至新commit,fast-forward合并仅移动指针而不生成新commit。

Git 分支指针的移动不是“自动刷新”或“后台同步”,而是每次 git commit 后,Git 直接重写对应分支引用文件的内容——仅更新一行哈希值。
分支本质就是 .git/refs/heads/ 下的一个文本文件
比如你在 main 分支上执行一次提交,Git 会做三件事:
- 生成新的 commit 对象,计算其 SHA-1(如
a1b2c3d...) - 把该哈希值写入
.git/refs/heads/main文件(覆盖旧值) - 同时更新
.git/HEAD文件中记录的当前分支位置(如果 HEAD 指向main)
这个过程不复制代码、不扫描历史、不重建索引——就是一次极快的文件写入。你可以手动验证:cat .git/refs/heads/main 看到的就是当前指向的 commit ID。
为什么切换分支后新提交不会影响原分支指针
因为每个分支都有独立的 refs 文件:feature/login 的指针存在 .git/refs/heads/feature/login,main 的存在 .git/refs/heads/main。它们互不干扰。
- 你在
feature/login上提交 → 只改.git/refs/heads/feature/login - 你
git checkout main→.git/HEAD改为指向refs/heads/main,但feature/login文件内容完全不变 - 再在
main提交 → 只改.git/refs/heads/main,feature/login仍稳稳指着它自己的最新 commit
HEAD 指向决定“当前分支”和指针移动方向
HEAD 是一个符号引用(symbolic ref),它本身不存哈希,而是存类似 ref: refs/heads/main 这样的路径。关键点在于:
- 只要
HEAD指向某个分支(如refs/heads/bugfix),后续git commit就只会移动那个分支的指针 - 如果你处于 detached HEAD 状态(比如
git checkout a1b2c3d),HEAD直接指向 commit 哈希,此时git commit会产生一个“游离提交”,新分支指针不会自动创建,也无人指向它——容易丢失
这就是为什么 git switch -c new-branch 比 git checkout --detach 更安全:前者确保 HEAD 正确关联到新分支引用。
合并时指针移动是“单次跳转”,不是“逐个遍历”
执行 git merge feature 时,Git 并不会把 feature 上每个 commit 都“重放”一遍。它只做两件事:
- 计算出一个新 commit(merge commit),其父节点包含
main当前 HEAD 和feature当前 HEAD - 把
main的指针(即.git/refs/heads/main)直接指向这个新 commit 的哈希
所以分支指针永远只做“一步跳”,无论中间隔了多少提交。这也是 fast-forward 合并能省掉 merge commit 的原因:当 main 指针可以直接“滑”到 feature 当前位置时,Git 就只是移动指针,不生成新 commit。
真正容易被忽略的是:分支指针移动只发生在本地仓库,且完全不依赖网络或远程状态。push 或 pull 不会改变本地分支指针的位置,它们只同步 refs 文件的内容到远端或从远端拉取——指针怎么动,永远由你本地的 commit、merge、reset 决定。











