Git 分支本质及与 commit、HEAD、tag 的关系
基于:
本文适合对 Git 有一定使用经验的读者,包括但不限于对分支、分支合并、提交、标签等的了解。
快照
在介绍 Git 分支前,需要知道什么是 commit 对象,介绍 commit 对象前,需要先了解 Git 保存数据的方式。Git 直接记录快照,而非差异比较。
从概念上来说,其它大部分版本控制系统(包括 Subversion 和近似工具)以文件变更列表的方式存储信息,这类系统将它们存储的信息看作是一组基本文件和每个文件随时间逐步累积的差异(它们通常称作基于差异的版本控制)。
![]() |
而在 Git 中,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照。为了效率,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。Git 对待数据更像是一个快照流。
![]() |
这是 Git 与几乎所有其它版本控制系统的重要区别。
提交对象
在进行提交操作时,Git 会保存一个提交对象(commit object)。提交对象会包含一个指向快照的指针。该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象。
分支本质
Git 的分支,本质上是指向提交对象的可变指针。Git 的默认分支名字是master
。在多次提交操作之后,你其实已经有一个指向最后那个提交对象的master
分支。master
分支会在每次提交时自动向前移动。
另:Git 的
master
分支并不是一个特殊分支。它就跟其它分支完全没有区别。之所以几乎每一个仓库都有 master 分支,是因为git init
命令默认创建它,并且大多数人都懒得去改动它。
![]() |
分支创建
Git 创建新分支其实只是为你创建了一个可以移动的新的指针。比如,创建一个 testing 分支,你需要使用git branch
命令:
$ git branch testing
这会在当前所在的提交对象上创建一个指针。
![]() |
HEAD
Git 是怎么知道当前在哪一个分支上呢?它有一个名为HEAD
的特殊指针,指向当前所在的本地分支(可以将HEAD
想象为当前分支的别名)。前面创建了testing
分支,但当前仍然在master
分支上。因为git branch
命令仅仅创建一个新分支,并不会自动切换到新分支中去。
![]() |
分支切换
要切换到一个已存在的分支,你需要使用git checkout
命令。我们现在切换到新创建的testing
分支去:
$ git checkout testing
这样HEAD
就指向testing
分支了。
![]() |
多分支开发
现在提交一次:
$ vim test.rb $ git commit -a -m 'made a change'
![]() |
如图所示,你的testing
分支向前移动了,但是master
分支却没有,它仍然指向运行git checkout
时所指的对象。现在我们切换回master
分支:
$ git checkout master
![]() |
这条命令做了两件事。一是使 HEAD 指回master
分支,二是将工作目录恢复成master
分支所指向的快照内容。也就是说,你现在做修改的话,项目将始于一个较旧的版本。本质上来讲,这就是忽略testing
分支所做的修改,以便于向另一个方向进行开发。
我们不妨再稍微做些修改并提交:
$ vim test.rb $ git commit -a -m 'made other changes'
现在,这个项目的提交历史已经产生了分叉。上述两次改动针对的是不同分支:你可以在不同分支间不断地来回切换和工作,并在时机成熟时将它们合并起来。
![]() |
标签
在上面介绍分支本质的图中,包含了一个v1.0
的标签,看上去和master
差不多。
Git 支持两种标签:轻量标签(lightweight)与附注标签(annotated)。
-
轻量标签很像一个不会改变的分支——它只是某个特定提交的引用。
-
而附注标签是存储在 Git 数据库中的一个完整对象,其中包含打标签者的名字、电子邮件地址、日期时间,此外还有一个标签信息,所以,它像是一个存档起来的 commit 副本。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构