Yves

GitFlow

参考:

一.首先谈一下Git.

Git是一个分布式版本控制系统.

什么是分布式?
先说集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。

分布式与集中式的最大的区别在于,是指在开发者可以提交(commit)到本地仓库.在本地机器上拷贝一个完整的Git仓库。这样,分布式版本控制系统根本没有“中央服务器”

既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。

但是这样对于团队开发来说,也带来了一些麻烦,比如,可能两个人不在一个局域网内,两台电脑互相访问不了,或者有一台电脑根本没有开机.

所以我们需要人为指定一个仓库为”中央仓库“,也就是一般我们放在github等代码托管平台上的仓库.注意,只是人为地,从git的技术或概念上来讲,这个所谓的”中央仓库”,跟我们的本地仓库的地位是相同的,只不过没有人直接在那个仓库上干活,其作用只是用来方便”交换”大家的修改.

看下面这张图,可以发现,每个人的主机上都有一个仓库的分支图,这个分支图最初都是中央仓库中的分支图的子图,每个人完成自己本地的开发之后,提交(commit)更新自己本地的分支图,然后将本地的分支图同步到中央仓库(这里是origin)

我们设定这么个中央仓库是为了对团队的代码版本有个很好的管理,那么跟我们平时写代码需要有代码规范一样,对git的使用,也需要有一个规范,不然分支关系错综复杂的话,是不利于代码版本的管理的.请看一下下面的分支关系图

有没有跟看见非常鬼畜的代码风格一样的感受?

二.Git Flow 分支模型

现在有一个很成熟的Git 分支模型Git Flow.简单说来,Git Flow 就是给原本普普通通的分支赋予了不同的职责.

  • master——最为稳定功能最为完整的随时可发布的代码;
    这个分支只能从其他分支合并,不能在这个分支直接修改
  • develop——永远是功能最新最全的分支;
    这个分支是我们是我们的主开发分支,包含所有要发布到下一个Release的代码,这个主要合并与其他分支,比如Feature分支
  • hotfix——修复线上代码的 bug;
    当我们在Production发现新的Bug时候,我们需要创建一个Hotfix, 完成Hotfix后,我们合并回Master和Develop分支,所以Hotfix的改动会进入下一个Release
  • feature——某个功能点正在开发阶段;
    这个分支主要是用来开发一个新的功能,一旦开发完成,我们合并回Develop分支进入下一个Release
  • release——发布定期要上线的功能。
    当你需要一个发布一个新Release的时候,我们基于Develop分支创建一个Release分支,完成Release后,我们合并到Master和Develop分支

三.如何在Git Flow工作

3.1主要分支(Main Branches)

注意,不是主分支,是Main Branches.

在中央仓库中,有两个分支(Branch)是拥有无限的生命周期的:

  • master
    中央仓库或者叫原始仓库的主分支(origin/master),这个分支中的代码是随时可以被发布的.团队中的每一位开发者都应该对这个分支非常熟悉.
  • develop
    开发分支(origin/develop)中保持这会着项目的最新版本,这里的代码将会应用到在下一次的发布中.当develop分支中的代码更新到一个稳定的版本之后,就需要将这些更新全部合并(merge)到master分支,然后为master的新的版本设置一个Tag.

    3.2 Supporting Branches

    在主要分支之外,是一些辅助协调团队成员的开发工作的分支.这些分支的生命周期通常是有限的.
    这些分支主要包括:

  • Feature branches

  • Release branches
  • Hotfix branches

再次强调,从技术层面讲,这些分支和前面的主要分支并没有什么不同,但是现在我们根据规范,为这些分支赋予了不同的职责.

3.2.1 Feature branches 功能分支

这个分支一般基于Develop分支,但是必须只能合并到Develop分支.这个分支的命名可以除了master, develop, release-*, hotfix-*之外的任意命名,不过我个人觉得这个分支命名为与实际开发的功能相关的名字会比较好一点.

顾名思义,这分支就是用来进行新功能的开发的.这个功能可能很快就上线,也有可能进行长期的开发,因此对于本分支来说,什么时候合并到Develop分支,合并到哪个版本,都是不确定的.只要新功能还没有开发完成,这个分支就一直存在,但最终总是会合并到Develop分支上,这时候可以选择删除这个分支,当然也可以选择继续保留着.

举个栗子吧:
现在我们接到一个新的需求,需要添加某个功能,那么我们肯定不能直接在原来的分支上干活,而是应该新建一个源自Develop分支的新分支

1
2
**新建并切换到一个新的分支,当然,叫myfeature有点不太合理,与具体的功能相关比较好.**
$ git checkout -b myfeature develop

N小时过后,这个功能已经开发完成了,那么是时候要合并到Develop分支上了

1
2
3
4
5
6
7
8
**切换到Develop分支**
$ git checkout develop
**将myfeature分支合并(merge)到Develop分支.**
git merge --no-ff myfeature
**删除myfeature分支,当然也可以不删除**
git branch -d myfeature
**将更新之后的Develop分支push到远程仓库(中央仓库)**
$ git push origin develop

在合并的时候,可以看到有个参数–no–ff,这个参数的作用是使被合并的分支,即myfeature分支的提交历史不丢失.看下面的图就一目了然了.

3.2.2 Release branches 发布分支

这个分支一般基于Develop分支,必须要合并到Develop和Master分支,命名为release-*,星号代表版本号

在这个分支上主要用作最后的小修改和debug.当这个分支创建之后,意味着新版本的需求都已经满足,至少要求的新功能都已经合并到Develop分支上了.

还是一个栗子:
当前有个版本1.0已经上线了,然后新版本我们已经基本开发完毕了,Develop分支已经合并了所有的即将上线版本所要求新功能,并且我们已经决定下一个版本号为1.2

1
2
3
4
5
6
7
8
**基于创建release分支**
$ git checkout -b release-1.2 develop
**这里执行了虚构的bump-version.sh脚本,将版本更新为1.2,假装完成了版本的更新**
$ ./bump-version.sh 1.2
**提交**
$ git commit -a -m "Bumped version number to 1.2"
[release-1.2 74d9424] Bumped version number to 1.2
1 files changed, 1 insertions(+), 1 deletions(-)

这个分支将存活一段时间以确保新版本的正确性.在这段时间,一些bug的修复也是在这个release分支上进行,而不是在Develop分支上.其次,新增功能在这里是绝对禁止的.功能的更新必须得合并到Develop分支,然后等待下一个版本的更新.

当这个release分支的稳定性已经足以当做一次真正的版本迭代的时候,需要将这个release分支合并到master分支上(因为master分支上记录的是每一个版本的迭代更新),并且为master分支上的新的commit设置一个版本Tag;然后,在release分支上做的修改或更新(比如debug),应当被合并回Develop分支,这样确保将来的release版本不会出现之前出现过的bug,(如果这个bug确实是被解决掉了的话).

1
2
3
4
5
6
**切到master分支**
$ git checkout master
**合并**
$ git merge --no-ff release-1.2
**标记tag**
$ git tag -a 1.2
1
2
3
4
**切到Develop分支**
$ git checkout develop
**合并.注意,这一步很可能导致冲突**
$ git merge --no-ff release-1.2
1
2
**最后删除这个release分支**
$ git branch -d release-1.2

3.2.3 Hotfix branches 修复分支

本分支一般基于master,必须要合并到master和Develop分支,分支命名为hotfix-*

这个分支和前面的release分支有点类似,都是为一个新的版本发布作准备.不同之处在于,release分支创建于产品正式发布之前,而这个Hotfix分支创建于产品发布之后.

从分支的名字也能猜到,当我们的产品上线之后,突然发现了一个很严重的bug,我们不得不用最快的速度去解决,在这个时候,就应当创建Hotfix分支了.

创建这个分支的好处或必要性在于,当一部分人着手于紧急的bug修复工作的时候,团队中其它成员可以继续在基于develop分支上的开发工作.

继续栗子:
刚才我们已经上线了1.2版本了,但是这时候用户反馈了一个非常严重的bug,但是现在Develop分支又已经开始了新的功能的开发工作,其稳定性还远远达不到上线的标准,这时候我们就需要创建Hotfix分支并进行debug工作.

1
2
3
4
5
6
7
**基于master创建Hotfix分支,注意版本号的设置**
$ git checkout -b hotfix-1.2.1 master
**修改版本号**
$ ./bump-version.sh 1.2.1
$ git commit -a -m "Bumped version number to 1.2.1"
[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1
1 files changed, 1 insertions(+), 1 deletions(-)

修复bug并提交

1
2
3
$ git commit -m "Fixed ××× production problem"
[hotfix-1.2.1 abbe5d6] Fixed severe production problem
5 files changed, 32 insertions(+), 17 deletions(-)

修复完成之后,需要合并回master和Develop分支,以确保下一个版本不会出现同样的bug,这一步跟release分支也是一样的.

1
2
3
$ git checkout master
$ git merge --no-ff hotfix-1.2.1
$ git tag -a 1.2.1 # 同样需要一个tag

1
2
$ git checkout develop
$ git merge --no-ff hotfix-1.2.1

还有一种例外的情况,就是需要合并Hotfix分支时已经存在一个release分支了,这就应该将Hotfix分支合并到release分支而不是Develop分支,因为release分支最终也是要合并到Develop分支上的.但这也不是绝对的,假如现在Develop分支上进行的某项开发迫切需要这个bug的修复,但是又等不及release分支的完工,那么这样也可以将Hotfix分支合并到Develop分支上

最后,删除Hotfix分支

1
$ git branch -d hotfix-1.2.1