时长13:30大小6.19M
记得大概是一年前吧,我与好友老吴喝茶聊天时,讨论到:高效的持续交付体系,必定需要一个合适的代码分支策略。
我告诉老吴:“采用不同的代码分支策略,意味着实施不同的代码集成与上线流程,这会影响整个研发团队每日的协作方式,因此研发团队通常会很认真地选择自己的策略。”
老吴是一名有多年开发经验的资深架构师,当时正好要接手一个框架团队,从个人贡献者向团队管理者转型。他个人对代码管理工具可谓熟之又熟,甚至连“老古董”的 CVS 都可以跟你聊半天。但他在为团队制定代码分支管理策略时,还是慎之又慎,足见其重要性。
最后我们发现,要确定选用哪种代码分支管理策略,需要先假设几个问题,这几个问题有了答案,也就代表你找到了适合的方向。
你需要思考的几个问题如下:
Google 和 Facebook 这两个互联网大咖都在用主干开发(Trunk Based Development,简称 TBD),我们是不是也参照它俩,采用主干开发分支策略?
用 Google 搜索一下,会发现有个排名很靠前的分支策略,叫“A successful Git branching model”(简称 Git Flow),它真的好用吗?团队可以直接套用吗?
GitHub 和 GitLab 这两个当下最流行的代码管理平台,各自推出了 GitHub Flow 和 GitLab Flow,它们有什么区别?适合我使用吗?
像阿里、携程和美团点评这样国内知名的互联网公司,都在用什么样的分支策略?
今天,我想再沿着当时的思考路径,和你一起回顾和总结一下,希望能够带你全面了解代码分支策略,帮助你做出合适的选择。
主干开发是一个源代码控制的分支模型,开发者在一个称为 “trunk” 的分支(Git 称 master) 中对代码进行协作,除了发布分支外没有其他开发分支。
Google 和 Facebook 都是采用“主干开发”的方式,代码一般直接提交到主干的头部,这样可以保证所有用户看到的都是同一份代码的最新版本。
“主干开发”确实避免了合并分支时的麻烦,因此像 Google 这样的公司一般就不采用分支开发,分支只用来发布。
大多数时候,发布分支是主干某个时点的快照。以后的改 Bug 和功能增强,都是提交到主干,必要时 cherry-pick (选择部分变更集合并到其他分支)到发布分支。与主干长期并行的特性分支极为少见。
由于不采用“特性分支开发”,所有提交的代码都被集成到了主干,为了保证主干上线后的有效性,一般会使用特性切换(feature toggle)。特性切换就像一个开关可以在运行期间隐藏、启用或禁用特定功能,项目团队可以借助这种方式加速开发过程。
特性切换在大型项目持续交付中变得越来越重要,因为它有助于将部署从发布中解耦出来。但据吉姆 · 伯德(Jim Bird)介绍,特性切换会导致代码更脆弱、更难测试、更难理解和维护、更难提供技术支持,而且更不安全。
他的主要论据是,将未经测试的代码引入生产环境是一个糟糕的主意,它们引发的问题可能会在无意间暴露出来。另外,越来越多的特性切换会使得逻辑越来越混乱。
特性切换需要健壮的工程过程、可靠的技术设计和成熟的特性切换生命周期管理,如果不具备这三个关键的条件,使用特性切换反而会降低生产力。
根据上面的分析,主干开发的分支策略虽然有利于开展持续交付,但是它对开发团队的能力要求也更高。
主干开发的优缺点如表 1 所示。
和主干开发相对的是 “特性分支开发” 。在这个大类里面,我会给你分析 Git Flow、GitHub Flow 和 GitLab Flow 这三个常用的模型。
第一,Git Flow
我们在 Google 上查关键词“branch model”(也就是“分支模型”),有一篇排名比较靠前的文章“A successful Git branching model”,它介绍了 Git Flow 模型。
Git 刚出来的那些年,可参考的模型不多,所以 Git Flow 模型在 2011 年左右被大家当作了推荐的分支模型,至今也还有项目团队在使用。然而,Git Flow 烦琐的流程也被许多研发团队吐槽,大家普遍认为 hotfix 和 release 分支显得多余,平时都不会去用。
第二,GitHub Flow
GitHub Flow 是 GitHub 所使用的一种简单流程。该流程只使用 master 和特性分支,并借助 GitHub 的 pull request 功能。
在 GitHub Flow 中,master 分支中包含稳定的代码,它已经或即将被部署到生产环境。任何开发人员都不允许把未测试或未审查的代码直接提交到 master 分支。对代码的任何修改,包括 Bug 修复、热修复、新功能开发等都在单独的分支中进行。不管是一行代码的小改动,还是需要几个星期开发的新功能,都采用同样的方式来管理。
当需要修改时,从 master 分支创建一个新的分支,所有相关的代码修改都在新分支中进行。开发人员可以自由地提交代码和提交到远程仓库。
当新分支中的代码全部完成之后,通过 GitHub 提交一个新的 pull request。团队中的其他人员会对代码进行审查,提出相关的修改意见。由持续集成服务器(如 Jenkins)对新分支进行自动化测试。当代码通过自动化测试和代码审查之后,该分支的代码被合并到 master 分支。再从 master 分支部署到生产环境。
GitHub Flow 的好处在于非常简单实用,开发人员需要注意的事项非常少,很容易形成习惯。当需要修改时,只要从 master 分支创建新分支,完成之后通过 pull request 和相关的代码审查,合并回 master 分支就可以了。
第三,GitLab Flow
上面提到的 GitHub Flow,适用于特性分支合入 master 后就能马上部署到线上的这类项目,但并不是所有团队都使用 GitHub 或使用 pull request 功能,而是使用开源平台 GitLab,特别是对于公司级别而言,代码作为资产,不会随意维护在较公开的 GitHub 上(除非采用企业版)。
GitLab Flow 针对不同的发布场景,在 GitHub Flow(特性分支加 master 分支)的基础上做了改良,额外衍生出了三个子类模型,如表 2 所示。
GitLab Flow 的特性分支合入 master 用的是“Merge Request”,功能与 GitHub Flow 的“pull request”相同,这里不再赘述。
通过 Git Flow、GitHub Flow 和 GitLab Flow(3 个衍生类别) 这几个具体模型的介绍,我给你总结一下特性分支开发的优缺点。如表 3 所示。
上面我跟你讲到的分支模型,都是 IT 研发领域比较流行的。虽然有些策略带上了代码平台的标识,如 GitHub Flow,但并不意味着该策略仅限于 GitHub 代码平台使用,你完全可以在自己搭建的代码平台上使用这些策略。
接下来,我就总体归纳一下什么情况下应该选择什么样的分支策略。如表 4 所示。
GitLab 作为最优秀的开源代码平台,被多数互联网大公司(包括阿里、携程和美团点评等)所使用,这些大厂也都采用特性分支开发策略。当然,这些大公司在长期持续交付实践中,会结合各自公司的情况做个性化的定制。
比如,携程公司在 GitHub Flow 的基础上,通过自行研发的集成加速器(Light Merge)和持续交付 Paas 平台,一起完成集成和发布。
再比如,阿里的 AoneFlow,采用的是主干分支、特性分支和发布分支三种分支类型,再加上自行研发的 Aone 协同平台,实现持续交付。
今天,我主要给你介绍了各种代码分支策略的特性。
你应该已经比较清晰地理解了“主干开发”和“特性分支开发”两种策略的各自特性:
“主干开发”集成效率高,冲突少,但对团队个人的开发能力有较高要求;
“特性分支开发”有利于并行开发,需要一定的流程保证,能保证主干代码质量。
相信在没有绝对自信能力的情况下,面对绝大多数的场景,企业还是会选择“特性分支开发”的策略。所以,我给你介绍了几种主流的特性分支方法,并对比了各类策略的优劣,以及它们适用的场景。
接下来,你就可以根据自己所在项目的具体情况,参考今天的内容,裁剪出最适合自己团队的分支策略了。
开源性质的项目,为什么不适合用主干开发的分支策略?
如果你所在的团队只有 5 人,而且迭代周期为 1 周,你会采用什么样的分支策略?
欢迎你给我留言。
作者回复: 讲的好,主干开发除了cherry pick以外,都简单直接
作者回复: 很棒
作者回复: 您提到利用SIT分支来保证合并到master的都是经过测试,我们强烈建议使用 SmartMerge(本专栏第7讲) 来代替SIT,可以更及时地发现代码集成冲突的问题,其次可以更便捷地决策出用于上线的最大集合。
提到的第一个困惑,我们采用SmartMerge后,对应会有SmartMerge的分支,第一个问题演变为“怎么限制开发不要从SmartMerge的分支创建新分支,而只能从master分支创建新分支?”我认为这个演变过来的问题非常好。可以从两个方面着手来规范。
1)如果在gitlab UI上创建分支的话,我们可以很容易地限制只能从master创建新分支。
2)另一方面我们不打算限制git客户端的行为,仅当git客户端向gitlab服务端push新分支的时候做相应的检查。为了判断新分支是否从master拉取且尚未和其他分支做merge,只需查看该分支和master的merge-base的commit之间,是否存在merge的commit即可,如果有,则不允许push。
如果没有采用SmartMerge的方式,其实方法本质是相同的。如果短期内还是只能通过SIT分支的话,做法和SmartMerge的方式是一样的。
口诀为:在服务端直接控制新分支的创建,且拒绝客户端push不规范的新分支。
第二个困惑,假设你们尚未使用SmartMerge的方式,可以通过在你们的开发流程中额外增加一个活动来搞定。用于集成的SIT分支在编译打包前,务必和master做merge,打包测试后如果没问题的话,SIT和master分支做fast forward的检查,如果是fast forward,那么SIT merge到master分支产生的commit,内容和SIT 的HEAD是相同的,也就无需再一次进行测试了。如果你们master分支的变更只能来自SIT分支,那么这个fast forward是很容易得到保证的。
作者回复: 特性分支模式下,都是从production拉取的开发分支,然后合并到master,master做持续集成,为了不影响持续集成,所以有了从master拉出来的环境分支进行部署
要考虑团队人员的能力,如果新人较多,就使用特性分支,反之使用主干开发,github flow的pull request其实也是特性分支
作者回复: gitlab几个flow,除了用于集成的master分支外,额外还配置了其他用于测试或上生产的分支。
拿带生产的分支来说,如果公司约定每周四才能发布到生产,而团队于周二在master上就集成好了V3.6版本,为了不影响后面的集成,master分支需要先merge到PRODUCTION分支,周四上线只需取PRODUCTION分支即可。
当然,gitlab flow也只是提供一个解决方案而已,只要能在发布窗口时正确快捷地找到用于发布的commit,同时又不影响集成即可。
至于提供带环境分支的,就是用分支模型来规约发布流程,保证用于上线的代码经过层层测试。做法就是约好了只能用PRODUCTION分支上线。比如通过PRE-PRODUCTION环境测试后,才能把该环境对应的分支合入到PRODUCTION分支。
作者回复: 主要还是看你能hold住哪个,比如我个人属于rubyist,所以用ruby写的gitlab肯定就是我的首选了,如果你比较偏爱或擅长go,那就gogs了。
相对来说我个人觉得gogs比较轻量化,有好处有坏处,比如你要大量二次开发,gitlab service形式就比较友好,而且最近也开源了HA方案
作者回复: 主干开发也可以有多个分支,除了master,其他是发布分支,master是用来持续集成的,也就是大家只往master push代码
特性切换不是分支,是代码或框架实现的功能,就理解为功能开关好了,也就是说有些代码即使上线了,也能通过开关保证它不工作
作者回复: 持续交付要求至少有一条分支随时能够进行发布,只要遵循这个原则,仅2条分支并无大碍
作者回复: 最新版本应该给了和GitHub类似的解决方案,我们是对gitlab做了分片,每个分片是一个仓库,在集群之前增加了基于nodejs ssh2修改的proxy,仓库做简单的定时备份
作者回复: 还是有一点区别的,比如可以暂时预设多个发布分支,以针对一段时间内的多版本并行需要