现在的位置: 首页 > 综合 > 正文

Git 分支模型

2013年10月01日 ⁄ 综合 ⁄ 共 5838字 ⁄ 字号 评论关闭

       这篇文章我想介绍一下一年前就提到过的我所有项目(工作/私有)都在使用的开发模式,经过事实验证,确实非常可行。很早就想写了,一直没腾出时间。我不会涉及项目的细节,只是谈谈分支的使用策略和发布管理。

[SCM笔记]一个成功的git分支模型 - Fantity Wei - FootPrints

 

       上图是使用Git这个版本控制工具来管理所有源码的。

为什么使用Git
       如果要看详细的Git与集中式源码管理工具的优势与劣势,可以参见这篇文章,那里有很多口水仗。作为一个开发人员,所有的源码管理工具中,我最喜欢Git。Git从根本上改变了开发人员对分支和合并的使用,传统的CVS/SVN,分支和合并都是高级话题,而且使用起来稍显麻烦,隔一段时间才会用一次。但是有了Git,这些操作就成了家常便饭。
       由于使用简单,方便重复操作,分支和合并不再是让人望而生畏的操作,版本管理工具应该尽可能地对分支/合并提供最好的支持。
       工具谈得差不多了,回到开发上。如果想管理好软件的开发流程的话,我待会要讲到的模型其实是一些每个开发人员都应该遵守的步骤。

分布式但集中化
       我们要使用的仓库是一个"中心库",当然这个中心库只是被认为是这样(因为Git是分布式的,从技术层面上来说是没有中心库的),我们将把这个仓库叫做"origin",因为Git用户都熟悉这个名字。
       每个开发者都可以pull和push到origin,但除了中心化的push-pull关系外,每个开发者还可以从其他开发者那pull changes,来构成小团队。比如说,当开发一个比较大的新特性时,在把正在进行的工作过早地提交到origin之前,安排2个或多个开发者一起协作可能会很有用。上图中有几个小团队:Alice和Bob,Alice和David,Clair和David。
       从技术角度来说,其实就是Alice定义了一个叫Bob的Git remote,指向到Bob的仓库,反之亦然。

[SCM笔记]一个成功的git分支模型 - Fantity Wei - FootPrints

主(main)分支
       中心仓库有两个分支:
       At the core, the development model is greatly inspired by existing models out there. 中心仓库有两个无限期存在的主分支:
*      master
*      develop
       origin上的master分支,Git用户应该很熟悉了,跟master并行的有一个叫作develop的分支。

 

[SCM笔记]一个成功的git分支模型 - Fantity Wei - FootPrints

        我们把origin/master作为HEAD源码总是表示production-ready(可随时部署)状态的主要分支。
       我们把origin/develop作为HEAD源码总是表示下一个发布的最新发展变化状态的主要分支。有时候也叫它为“合并分支”,每日构建也是基于此分支。
       当develop分支上的源代码达到了一个稳定状态并准备发布时,所有的改变都要合并到master分支,并标上版本号。如何实现的下面细说。
       因此,每次改变合并回master时,都会有新的部署发布。我们对这点非常严格要求,因此从理论上讲,这点可以自动化实现。每次master上有提交时,就可以使用Git hook脚本来构建并发布软件到生产服务器上去。

支持(supporting)分支
       我们的开发模型使用了一些支持分支放在master和develop分支的旁边,用来辅助开发小组之间的并行开发,解除特性追踪,准备生产发布和解决生产现场出现的问题。不像主(main)分支,这些分支是有时间限制的,因为他们最终都会被移除。
       我们将使用到的不同分支:
*      Feature branches
*      Release branches
*      Hotfix branches
       每个分支都有特定的作用并严格规定了它们源自那个分支和将来合并到哪个分支去。这些后面再说。
       划分这些分支绝不是从技术层面来的,而是从它们的功能上划分的。它们是十足的旧的git分支。

Feature 分支
*      继承分支: develop
*      合并分支:develop
*      命名规范:除了master,develop,release-*,hotfix-*
       Feature branches是用来为短期或长期的发布开发新特性。当开始开发新特性时,很可能不知道这个特性会出现在哪个目标版本。一旦开发完成就可以合并到develop,当然如果开发失败,就可以抛弃。
       Feature分支一般只存在于develop仓库,不再origin中。

[SCM笔记]一个成功的git分支模型 - Fantity Wei - FootPrints

 

创建一个特性分支
       当开发一个新特性时,从develop分支上进行分支

$ git checkout -b myfeature develop Switched to a new branch "myfeature"

    将完成的特性合并到develop中

$ git checkout develop  Switched to branch 'develop'  $ git merge --no-ff myfeature Updating ea1b82a..05e9557  (Summary of changes)  $ git branch -d myfeature    Deleted branch myfeature (was 05e9557).  $ git push origin develop

       --no-ff(no fast foward)参数,使得每一次的合并都创建一个新的commit记录。即使合并是fast-foward,这样可以避免丢失feature分支的历史存在,和各小组提交特性的信息。比较下面的两个图:

[SCM笔记]一个成功的git分支模型 - Fantity Wei - FootPrints

         在右边的情况下,除非手动的阅读所有的日志信息,你不知道是哪个提交对象应用的新特性。回到整个特性(例如一个组的提交),后一种情况实在让人头疼,所以使用--no-ff参数很容易解决。        可能第一种情况会多创建一些(空的)提交对象,但是收益远大于消费。        不幸的是,我还没有发现一种方法可以让git merge默认带有--no-ff参数,但真的是应该有。

Release 分支
*      继承分支: develop
*      合并分支:develop 和 master
*      命名规范:release-*
       Release branchs是为新的production release作准备的。他们可用来做最后的细节问题(dotting of i's and crossing t's)的考虑,甚至允许小的Bug修复和准备发布的元数据(版本号,构建日期等)。在一个发布分支上做完所有的这些工作之后,develop分支被清除来接受下一个大的发布的新特性。
       从develop分支合并到release分支的关键点是:develop分支达到了release分支所要求的状态。至少所有针对该release的特性要被合并到develop分支中。至于那些将来会有的特性可以先放一放。然后就是为接下来即将要发布的版本分配一个版本号。

创建发布分支
       Release branches是源自develop分支而创建。举个例子,假如1.1.5是当前的production release,然后即将发布一个大的版本发布。develop的状态已经可以发布下一个版本了,经过商榷后,决定它为1.2版本(而不是1.1.6或2.0)。所以我们创建一个release分支,并以新的版本号来命名它。

$ git checkout -b release-1.2 develop Switched to a new branch "release-1.2" $ ./bump-version.sh 1.2 Files modified successfully, version bumped to 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(-)

       这个新分支可能会存在一定的时间,直到release被明确的发布。这段时间内,bug修补可以在这个分支上进行(而不是develop分支)。添加新特性(尤其比较大的)是不允许的。他们最后还是要被合并到develop,然后继续在develop分支上开发,直到下一个版本。

完成一个release分支
       当release branch已经准备就绪并即将发布,还需要做几件事。首先,release分支将被合并到master分支上(切记,每一个提交到master上的提交都是一个新版本)。然后,master上的commit都要添加tag,方便将来查看和回滚。最后,release上所做的修改必须合并到develop分支上,以保证将来的发布版的bug已被修补。
       前两个步骤是:

$ git checkout master Switched to branch 'master' $ git merge --no-ff release-1.2 Merge made by recursive. (Summary of changes) $ git tag -a 1.2

   发布版已经做好了,为它打上标签以供将来参考。
注:你可能会想用-s或-u <key>参数来秘密的打上你的标签。
   为了把release上的改变保存到develop,我们需要将它合并回develop中,所以:

$ git checkout develop Switched to branch 'develop' $ git merge --no-ff release-1.2 Merge made by recursive. (Summary of changes)

   这个步骤可能会导致冲突(因为我们已经改变了版本号),如果这样的话,解决冲突,然后再提交。
   现在一切都完成了,我们不再需要它了,可以把release branch干掉了。
$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).

Hotfix分支
*      继承分支: master
*      合并分支:develop 和 master
*      命名规范:hotfix-*
       Hotfix branch和Release branch有几分相似,都是为了新的production release而准备的。比如运行过程中发现了bug,就必须快速解决,这时就可以创建一个Hotfix branch,解决完后合并到master分支上。好处是开发人员可以继续工作,有专人来负责搞定这个bug。

[SCM笔记]一个成功的git分支模型 - Fantity Wei - FootPrints

 

创建Hotfix branch
       Hotfix是从master分支上创建的。假如当前运行版本是1.2,然后发现有bug,但是develop还在开发中,不太稳定,这时就可以从master分支新开一个Hotfix branch,然后开始解决问题。

$ git checkout -b hotfix-1.2.1 master Switched to a new branch "hotfix-1.2.1" $ ./bump-version.sh 1.2.1 Files modified successfully, version bumped to 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并提交 $ git commit -m "Fixed severe production problem" [hotfix-1.2.1 abbe5d6] Fixed severe production problem 5 files changed, 32 insertions(+), 17 deletions(-)

完成Hotfix分支
       当结束时,补丁(bugfix)要被合并到master中去,同时也要合并到develop分支中,以保证下个版本发布时该bug已被修复。这跟release branch完成时一样。
       首先,更新master并打上发布标签:

$ git checkout master Switched to branch 'master' $ git merge --no-ff hotfix-1.2.1 Merge made by recursive. (Summary of changes) $ git tag -a 1.2.1

       接下来,也将补丁合并到develop分支:

$ git checkout develop Switched to branch 'develop' $ git merge --no-ff hotfix-1.2.1 Merge made by recursive. (Summary of changes)

       有一个例外是,就是当一个release branch存在时,bugfix要被合并到release而不是develop,因为release最终会被合并到develop。(但是,当develop分支急迫的需要这个补丁,不能等待release分支完成,也可以安全的将补丁合并到develop中)。
       最后,移除临时的分支:

$ git branch -d hotfix-1.2.1 Deleted branch hotfix-1.2.1 (was abbe5d6).

 
总结
       这个开发模型其实没有什么新颖的,一开始提到的"大图"确实在我们的项目起到了非常大的作用。这是很优雅的一个模型,很容易实现,也容易在团队成员之间达成一致。
       PS:需要这个模型大图的,可以去原文地址下载:http://github.com/downloads/nvie/gitflow/Git-branching-model.pdf

原文地址:http://nvie.com/posts/a-successful-git-branching-model/
译文地址:http://blog.leezhong.com/translate/2010/10/30/a-successful-git-branch.html

抱歉!评论已关闭.