git分支管理

学习 git 的分支相关的知识和相关指令,了解 git 分支背后的原理,真正的理解 git 分支,现在很多的版本控制系统都有他的分支,但是使用的方法通常是将整个源代码的目录复制一遍作为副本,这种对于大项目来说的话效率太低了,git的优点就是使用了一种新的方式来管理分支,使得效率大大提高,所以 git 就提倡在工作流程中频繁的使用分支和和合并,理解了分支的这一特性,会让我们更好的使用 git 的分支管理,从而让我们的开发方式变得更加便捷。

分支简介

为了更好的理解 GIt处理分支的方法,需要先理解一下 Git 如何存取数据

Git 数据保存

现在很多的版本控制系统存储和对待数据的方式都是以文件变更列表的方式存储信息,他们将保存信息看作是一组文件和每个文件随时间逐步累积的差异

Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照

Git 把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个快照流

快照(snapshot):

In computer systems, a snapshot is the state of a system at a particular point in time.

这个术语的大概意思就是,快照是计算机系统某一时刻系统的状态的记录,假如在 A 时刻,git 工作区有 file1 和 file2 两个文件,在 B 时刻,你对 file B 进行了更改,然后,在你准备 commit 之前,git 就准备好了快照了,这个快照记录了当前工作空间中指向未修改文件file2的指针和已经修改的file2数据(即当前时刻工作空间的文件数据状态)。因此,commit的时候,就等同于保存了一次快照。

快照如何运作的:

git会读取当前工作空间的所有数据,进行数据预存,再重新调整。它会和上一次的快照版本的内容进行比较,对于没有改变的文件数据,git会把当前预存中冗余文件的数据去除掉,改为保留指向上一个版本中该文件数据的指针,对于有差异的文件数据就会保留下来,最终再把数据完整保存下来,这才算是执行了一次快照。

Git 分支

在进行提交操作时,Git 会保存一个提交对象(commit object)。知道了 Git 保存数据的方式,我们可以很自然的想到——该提交对象会包含一个指向暂存内容快照的指针。 但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象

为了更加形象地说明,我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。 暂存操作 git add 会为每一个文件计算校验和(使用SHA-1 哈希算法),然后会把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交:

当用 git commit 进行提交时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和,然后在 Git 仓库中将这些校验和保存为树对象。 随后,Git 便会创建一个提交对象,它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。如此一来,Git 就可以在需要的时候重现此次保存的快照。

通过了上面的 git addgit commit 操作,Git 已经在 Git 仓库中得到了 5 个对象,3 个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息),三者的关系如下图所示:

当我们对文件作出修改时,同样,git add 操作会为每一个文件进行计算校验和(使用SHA-1 哈希算法),将版本文件快照加入到 Git 仓库中(文件没有更改就不会重复将文件加入到仓库中),将校验和加入到暂存区等待期交:

与初次提交不同的是,这次的提交会在提交对象中加入一个上次提交对象(父对象)的指针。同时,将 Git 仓库中的对象保存成树对象时,指向当前最新的 blob 对象索引(也就是更改过后的文件,没更改的文件不变)。如下图所示为提交对象和父对象之间的关系:

Git 分支本质

Git 的分支,其实本质上仅仅是指向提交对象的可变指针。 Git 的默认分支名字是 master。 在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支。 它会在每次的提交操作中自动向前移动。

注意:Git 的 master 分支并不是什么特殊的分支,他和其他的分支没有任何区别,之所以每个仓库都一个,就是由于仓库在创建的时候默认会创建一个 master 分支,大部分人都没有改动它而已。

下图为 master 分支在一个项目中的位置(其中 snapshot 为快照):

Git 分支相关指令

既然知道了 Git 的数据存储方式还有分支的本质,所以在 Git 中创建一个分支就是一个很简单的事情了,就是创建一个可以移动的新指针,在 git 中是使用 git branch命令:

$ git branch testing

这就会在当前的提交对象上创建一个指针,当分支有两个以上的时候,如何去判断当前在那个分支上呢?在 Git 里面有 一个 HEAD 的特殊指针,用来指向当前所在的本地分支,使用 git branch 创建分支之后 HEAD 指针还是指向 master 上,要想切换分支需要用 git checkout 命令:

$ git checkout testing

当然还可以通过 git log 指令来各个分支当前所指的对象,只要加上 --decorate 参数就可以了。

$ git log -- decorate

这样 HEAD 就指向 testig 分支了,如下图所示:

参考 Git 官方教程:分支简介