基于:
Git - Git 是什么?
Git - 分支简介
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 副本。