在这篇文章中,我提出一个开发模型。我已经将这个开发模型引入 到我全部的项目里(不管在工做仍是私人)已经一年有余,而且它被证实是很是成功的。我打算写 这些已经好久了,但我一直找不到时间来作,如今终于有时间了。我不会讲任何项目的具体细节,仅是关于分支策略和释放管理相关内容。php
它主要体现了Git对咱们源代码版本的管理。git
对于Git与其余集中式代码管理工具相比的优缺点的全面讨论,请参见这里。 这样的争论老是喋喋不休。做为一个开发者,与现今的其余开发工具相比较,我更喜欢Git。Git真得改变了开发者对于合并和分支的思考。我曾经使用经典的 CVS/Subversion,然而每次的合并/分支和其余行为总让人担惊受怕(“当心合并里的冲突,简直要命!”)。github
可是对于Git来讲,这些行为很是简单和搞笑,它们被认为是平常工做中的核心部分。例如,在不少CVS/Subversion书里,分支与合并老是在后面的章节中被讨论(对于高级用户使用),然而在每一个Git书中,在第3章就已经彻底涵盖了(做为基础)。shell
简单和重复的特性带来的结果是:分支与合并再也不是什么能够惧怕的东西。分支/合并被认为对于版本管理工具比其余功能更重要。服务器
关于工具,再也不多说,让咱们直接看开发模型吧。这个模型并非以下模型:在管理软件开发进度方面,面对每一个开发过程,每一个队员必须按必定次序开发。分布式
对于这种分支模型,咱们设置了一个版本库,它运转良好,这是一个"事实上" 版本库。不过请注意,这个版本库只是被认为是中心版本库(由于Git是一个分布式版本管理系统,从技术上来说,并无一个中心版本库)。咱们将把这个版本库称为原始库,这个名字对全部的Git用户来讲都很容易理解。svn
每一个开发者都对origin库拉代码和提交代码。可是除了集中 式的存取代码关系,每一个开发者也能够从子团队的其余队友那里得到代码版本变动。例如,对于2个或多个开发者一块儿完成的大版本变动,为了防止过早地向 origin库提交工做内容,这种机制就变得很是有用。在上述途中,有以下子团队:Alice和Bob,Alice和David,Clair和 David。工具
从技术上将,这意味着,Alice建立了一个Git的远程节点,而对于Bob,该节点指向了Bob的版本库,反之亦然。post
在核心部分,研发模型很大程度上靠其余现有模型支撑的。中心库有2个可一直延续的分支:开发工具
每一个Git用户都要熟悉原始的master分支。与master分支并行的另外一个分支,咱们称之为develop分支。
咱们把原始库/master库认做为主分支,HEAD的源代码存在于此版本中,而且随时都是一个预备生产状态。
咱们把origin/develop库认为是主分支,该分支HEAD源码始终体现下个发布版的最新软件变动。有人称这个为“集成分支”,而这是每晚自动构建得来的。
当develop分支的源码到达了一个稳定状态待发布,全部的代码变动须要以某种方式合并到master分支,而后标记一个版本号。如何操做将在稍后详细介绍。
因此,每次变动都合并到了master,这就是新产品的定义。在这一点,咱们倾向于严格执行这一点,从而,理论上,每当对master有一个提交操做,咱们就可使用Git钩子脚原本自动构建而且发布软件到生产服务器。
咱们的开发模型使用了各类辅助性分支,这些分支与关键分支(master和develop)一块儿,用来支持团队成员们并行开发,使得易于追踪功能,协助生产发布环境准备,以及快速修复实时在线问题。与关键分支不一样,这些分支老是有一个有限的生命期,由于他们最终会被移除。
咱们用到的分支类型包括:
每一种分支有一个特定目的,而且受限于严格到规则,好比:能够用哪些分支做为源分支,哪些分支能做为合并目标。咱们立刻将进行演练。
从技术角度来看,这些分支毫不是特殊分支。分支的类型基于咱们使用的方法来进行分类。它们理所固然是普通的Git分支。
多是develop分支的分支版本,最终必须合并到develop分支中。
分支命名规则:除了master、develop、release-*、orhotfix-*以外,其余命名都可。
功能分支(有时被称为topic分支)一般为即将发布或者将来 发布版开发新的功能。当新功能开始研发,包含该功能的发布版本在这个仍是没法肯定发布时间 的。功能版本的实质是只要这个功能处于开发状态它就会存在,可是最终会或合并到develop分支(肯定将新功能添加到不久的发布版中)或取消(譬如一次 使人失望的测试)。
功能分支一般存在于开发者的软件库,而不是在源代码库中。
开始一项功能的开发工做时,基于develop建立分支。
1 $ git checkout -b myfeature develop 2 Switched to a new branch "myfeature"
完成的功能能够合并进develop分支,以明确加入到将来的发布:
1 $ git checkout develop 2 Switched to branch 'develop' 3 $ git merge --no-ff myfeature 4 Updating ea1b82a..05e9557 5 (Summary of changes) 6 $ git branch -d myfeature 7 Deleted branch myfeature (was 05e9557). 8 $ git push origin develop
--no-ff标志致使合并操做建立一个新commit对象,即便该合并操做能够fast-forward。这避免了丢失这个功能分支存在的历史信息,将该功能的全部提交组合在一块儿。 比较:
后一种状况,不可能从Git历史中看到哪些提交一块儿实现了一个功能——你必须手工阅读所有的日志信息。若是对整个功能进行回退 (好比一组提交),后一种方式会是一种真正头痛的问题,而使用--no-ffflag的状况则很容易.
是的,它会建立一个新的(空)提交对象,可是收益远大于开销。
不幸的是,我还没找到一种方法,让--no-ff时做为合并操做的默认选项,但它应该是可行的。
Release分支可能从develop分支分离而来,可是必定要合并到develop和master分支上,它的习惯命名方式为:release-*。
Release分支是为新产品的发布作准备的。它容许咱们在最 后时刻作一些细小的修改。他们容许小bugs的修改和准备发布元数据(版本号,开发时间等等)。当在Release分支完成这些全部工做之后,对于下一次 打的发布,develop分支接收features会更加明确。
从develop分支建立新的Release分支的关键时刻是develop分支达到了发布的理想状态。至少全部此次要发布的features必须在这个点及时合并到develop分支。对于全部将来准备发布的features必须等到Release分支建立之后再合并。
在Release分支建立的时候要为即将发行版本分配一个版本 号,一点都不早。直到那时,develop分支反映的变化都是为了下一个发行版,可是在Release分支建立以前,下一个发行版到底叫0.3仍是1.0 是不明确的。这个决定是在Release分支建立时根据项目在版本号上的规则制定的。
Release分支是从develop分支建立的。例如,当前 产品的发行版本号为1.1.5,同事咱们有一个大的版本即将发行。develop 分支已经为下次发行作好了准备,咱们得决定下一个版本是1.2(而不是1.1.6或者2.0)。因此咱们将Release分支分离出来,给一个可以反映新 版本号的分支名。
1 $ git checkout -b release-1.2 develop 2 Switched to a new branch "release-1.2" 3 $ ./bump-version.sh 1.2 4 Files modified successfully, version bumped to 1.2. 5 $ git commit -a -m "Bumped version number to 1.2" 6 [release-1.2 74d9424] Bumped version number to 1.2 7 1 files changed, 1 insertions(+), 1 deletions(-)
建立新分支之后,切换到该分支,添加版本号。这里,bump-version.sh 是一个虚构的shell脚本,它能够复制一些文件来反映新的版本(这固然能够手动改变--目的就是修改一些文件)。而后版本号被提交。
这个新分支可能会存在一段时间,直到该发行版到达它的预约目标。在此期间,bug的修复可能被提交到该分支上(而不是提交到develop分支上)。在这里严格禁止增长大的新features。他们必须合并到develop分支上,而后等待下一次大的发行版。
当一个release分支准备好成为一个真正的发行版的时候, 有一些工做必须完成。首先,release分支要合并到master上(由于每一次提交到master上的都是一个新定义的发行版,记住)。而后,提交到 master上必须打一个标签,以便之后更加方便的引用这个历史版本。最后,在release分支上的修改必须合并到develop分支上,以便将来发行 版也包含这些bugs的修复。
在Git中的前两步是:
1 $ git checkout master 2 Switched to branch 'master' 3 $ git merge --no-ff release-1.2 4 Merge made by recursive. 5 (Summary of changes) 6 $ git tag -a 1.2
发行版如今已经完成,为之后引用打上标签。
编辑:你可能也想使用the-sor-u <key>flags来标记你的标签。
为了是修改保持在release分支上,咱们须要合并这些到develop分支上去,在Git上:
1 $ git checkout develop 2 Switched to branch 'develop' 3 $ git merge --no-ff release-1.2 4 Merge made by recursive. 5 (Summary of changes)
这个步骤可能会致使合并冲突(可能因为改变版本号更是如此)。若是是这样,修复它而后提交。
如今咱们真正的完成了,这个release分支将被删除,由于咱们再也不须要它了。
1 $ git branch -d release-1.2 2 Deleted branch release-1.2 (was ff452fe).
能够基于master分支,必须合并回develop和master分支。
分支名约定:hotfix-*
热修复分支与发布分支很类似,他们都为新的生成环境发布作准备,尽管这是未经计划的。他们来自生产环境的处于异常状态压力。当生成环境验证缺陷必须立刻修复是,热修复分支能够基于master分支上对应与线上版本的tag建立。
其本质是团队成员(在develop分支上)的工做能够继续,而另外一我的准备生产环境的快速修复。
hotfix branch(修补bug分支)是从Master分支上面分出来的。例如,1.2版本是当前生产环境的版本而且有bug。可是开发分支(develop)变化还不稳定。咱们须要分出来一个修补bug分支(hotfix branch)来解决这种状况。
1 $ git checkout -b hotfix-1.2.1 master 2 Switched to a new branch "hotfix-1.2.1" 3 $ ./bump-version.sh 1.2.1 4 Files modified successfully, version bumped to 1.2.1. 5 $ git commit -a -m "Bumped version number to 1.2.1" 6 [hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1 7 1 files changed, 1 insertions(+), 1 deletions(-)
分支关闭的时侯不要忘了更新版本号(bump the version)
而后,修复bug,一次提交或者屡次分开提交。
1 $ git commit -m "Fixed severe production problem" 2 [hotfix-1.2.1 abbe5d6] Fixed severe production problem 3 5 files changed, 32 insertions(+), 17 deletions(-)
完成一个bugfix以后,须要把butfix合并到master和develop分支去,这样就能够保证修复的这个bug也包含到下一个发行版中。这一点和完成release分支很类似。
首先,更新master并对release打上tag:
1 $ git checkout master 2 Switched to branch 'master' 3 $ git merge --no-ff hotfix-1.2.1 4 Merge made by recursive. 5 (Summary of changes) 6 $ git tag -a 1.2.1
编辑:你可能也会想使用 -sor-u <key>参数来对你的tag进行加密
下一步,把bugfix添加到develop分支中:
1 $ git checkout develop 2 Switched to branch 'develop' 3 $ git merge --no-ff hotfix-1.2.1 4 Merge made by recursive. 5 (Summary of changes)
规则的一个例外是: 若是一个release分支已经存在,那么应该把hotfix合并到这个release分支,而不是合并到develop分支。当 release分支完成后, 将bugfix分支合并回release分支也会使得bugfix被合并到develop分支。(若是在develop分支的工做急需这个bugfix, 等不到release分支的完成,那你也能够把bugfix合并到develop分支)
最后,删除临时分支:
1 $ git branch -d hotfix-1.2.1 2 Deleted branch hotfix-1.2.1 (was abbe5d6).
尽管这个分支模型没有任何震撼的新东西, 文章开头的图表在咱们的项目中表现出惊人的实用性。它造成了一个优雅的思惟模型,易于领悟并使团队成员发展出对分支和发布过程的共同理解。
这里提供一份高质量PDF格式图表。去吧,把它挂载墙上以便能随时快速参考。
更新: 若是有人须要: 这是主图表的gitflow-model.src.key (Apple Keynote格式).