分支 (branch)

我们首先要讲解一下分支的使用方法和操作。

在开发软件时,可能有多人同时为同一个软件开发功能或修复BUG,可能存在多个Release版本,并且需要对各个版本进行维护。

所幸,Git的分支功能可以支持同时进行多个功能的开发和版本管理。

什么是分支?

分支是为了将修改记录的整体流程分叉保存。分叉后的分支不受其他分支的影响,所以在同一个数据库里可以同时进行多个修改。

capture_stepup1_1_1

分叉的分支可以合并。

下面是使用分支进行作业的图示。

为了不受其他开发人员的影响,您可以在主分支上建立自己专用的分支。完成工作后,将自己分支上的修改合并到主分支。因为每一次提交的历史记录都会被保存,所以当发生问题时,定位和修改造成问题的提交就容易多了。

capture_stepup1_1_2

master分支

在数据库进行最初的提交后, Git会创建一个名为master的分支。因此之后的提交,在切换分支之前都会添加到master分支里。

capture_stepup1_1_3

分支的运用

Git您可以自由地建立分支。但是,要先确定运用规则才可以有效地利用分支。

这里我们会介绍两种分支 (“Merge分支”和 “Topic分支” ) 的运用规则。

Merge分支

Merge分支是为了可以随时发布release而创建的分支,它还能作为Topic分支的源分支使用。保持分支稳定的状态是很重要的。如果要进行更改,通常先创建Topic分支,而针对该分支,可以使用Jenkins之类的CI工具进行自动化编译以及测试。

通常,大家会将master分支当作Merge分支使用。

Topic分支

Topic分支是为了开发新功能或修复Bug等任务而建立的分支。若要同时进行多个的任务,请创建多个的Topic分支。

Topic分支是从稳定的Merge分支创建的。完成作业后,要把Topic分支合并回Merge分支。

capture_stepup1_2_1

分支的切换

若要切换作业的分支,就要进行checkout操作。进行checkout时,git会从工作树还原向目标分支提交的修改内容。checkout之后的提交记录将被追加到目标分支。

capture_stepup1_3_1

HEAD指向的是现在使用中的分支的最后一次更新。通常默认指向master分支的最后一次更新。通过移动HEAD,就可以变更使用的分支。

提交时使用~(tilde)和(caret)就可以指定某个提交的相对位置。最常用的就是相对于HEAD的位置。HEAD后面加上~(tilde)可以指定HEAD之前的提交记录。合并分支会有多个根节点,您可以用(caret) 来指定使用哪个为根节点。

capture_stepup1_3_2

stash

还未提交的修改内容以及新添加的文件,留在索引区域或工作树的情况下切换到其他的分支时,修改内容会从原来的分支移动到目标分支。

但是如果在checkout的目标分支中相同的文件也有修改,checkout会失败的。这时要么先提交修改内容,要么用stash暂时保存修改内容后再checkout

stash是临时保存文件修改内容的区域。stash可以暂时保存工作树和索引里还没提交的修改内容,您可以事后再取出暂存的修改,应用到原先的分支或其他的分支上。

capture_stepup1_3_3

分支的合并

完成作业后的topic分支,最后要合并回merge分支。合并分支有2种方法:使用mergerebase。使用这2种方法,合并后分支的历史记录会有很大的差别。

merge

使用merge可以合并多个历史记录的流程。

如下图所示,bugfix分支是从master分支分叉出来的。

capture_stepup1_4_1

合并 bugfix分支到master分支时,如果master分支的状态没有被更改过,那么这个合并是非常简单的。 bugfix分支的历史记录包含master分支所有的历史记录,所以通过把master分支的位置移动到bugfix的最新分支上,Git 就会合并。这样的合并被称为fast-forward(快进)合并。

capture_stepup1_4_2

但是,master分支的历史记录有可能在bugfix分支分叉出去后有新的更新。这种情况下,要把master分支的修改内容和bugfix分支的修改内容汇合起来。

capture_stepup1_4_3

因此,合并两个修改会生成一个提交。这时,master分支的HEAD会移动到该提交上。

capture_stepup1_4_4

执行合并时,如果设定了non fast-forward选项,即使在能够fast-forward合并的情况下也会生成新的提交并合并。

capture_stepup1_4_5

执行non fast-forward后,分支会维持原状。那么要查明在这个分支里的操作就很容易了。

rebase

merge的例子一样,如下图所示,bugfix分支是从master分支分叉出来的。

capture_stepup1_4_6

如果使用rebase方法进行分支合并,会出现下图所显示的历史记录。现在我们来简单地讲解一下合并的流程吧。

capture_stepup1_4_7

首先,rebase bugfix分支到master分支, bugfix分支的历史记录会添加在master分支的后面。如图所示,历史记录成一条线,相当整洁。

这时移动提交X和Y有可能会发生冲突,所以需要修改各自的提交时发生冲突的部分。

capture_stepup1_4_8

rebase之后,masterHEAD位置不变。因此,要合并master分支和bugfix分支,即是将masterHEAD移动到bugfixHEAD这里。

capture_stepup1_4_9

Mergerebase都是合并历史记录,但是各自的特征不同。

  • merge 保持修改内容的历史记录,但是历史记录会很复杂。
  • rebase 历史记录简单,是在原有提交的基础上将差异内容反映进去。

因此,可能导致原本的提交内容无法正常运行。 您可以根据开发团队的需要分别使用mergerebase。 例如,想简化历史记录,

  • topic分支中更新merge分支的最新代码,请使用rebase
  • merge分支导入topic分支的话,先使用rebase,再使用merge

topic分支和merge分支的运用实例

我们用简单的实例来讲解topic分支和merge分支的操作方法。

例如,在开发功能的topic分支操作途中,需要修改bug。

capture_stepup1_5_1

这时,merge分支还是处于开发功能之前的状态。在这里新建修改错误用的主题分支,就可以从开发功能的作业独立出来,以便开始新的工作。

capture_stepup1_5_2

完成bug修正的工作后,把分支导入到原本的merge分支后就可以公开了。

capture_stepup1_5_3

回到原本的分支继续进行开发功能的操作。

capture_stepup1_5_4

但是,如果要继续进行操作,你会发现需要之前修正bug时提交X的内容。有2种导入提交X的内容的方法:一种是直接merge,另一种是和rebase导入提交X的合并分支。

这里我们使用rebase合并分支的方法。

capture_stepup1_5_5

在导入提交X的内容的状态下继续进行开发功能。 这样,有效地利用分支的话就可以同时进行不同的作业了。