分支的目的是隔离,但多一个分支也意味着维护成本的增加。我们可以分别从开发和发布分支的多寡,做个简单组合,即:
设想两个不同的场景:
一个好的分支模式,可以大大提高软件的开发、集成和发布效率。选择什么样的分支策略,是每一个开发团队开始工作时面临的第一个问题。那么,选择什么样的分支模式才适合我们呢?在回答这个题之前,我们先了解一下几种常见的分支模式。
常见的分支模式有 TBD(即主干开发模式)、Git-Flow 模式、Github-Flow 模式及 Gitlab-Flow 模式。
即所有开发者,仅在一个开发分支(即主干)上进行协作开发的模式,在这种模式下,不允许新建任何长期存在的开发分支,有且仅保留主干分支进行开发协作。
因为没有长期分离的其他开发分支,任何代码变更持续地更新到主干上,在一定程度上避免了 merge 代码带来的困扰。同时,在这种开发模式下,建议采用发布分支的策略,根据软件版本的发布节奏拉出发布分支。在 TBD 模式下,所有的修改都是在主干上,哪怕是缺陷的修改也是,修改完缺陷后,再 cherry pick 到发布分支上。
其特点总结一下就是:
因为团队共享一个开发分支,并且在开发分支上进行集成验证,而每次代码提交都会触发集成验证,这就要求每次代码的变更在主干上都能快速地验证,以确定是否接受下一次代码变更(每次代码变更都应该基于前一个稳定的版本进行),为了保证主干一直处在可工作状态,这就需要:
所以,主干开发模式可以说是持续集成的关键推动者。主干开发模式非常利于持续集成,并且根据稳定和主干基线,做到随时发布,以达到持续交付。但这些是建立在团队成熟的协作能力和相对成熟的工程配套的基础上,快速地对主干的变更提交完成编译、检查及验证;同时,因为采取发布分支的实践方式,在产品版本、分支、部署场景的对应关系需要梳理清楚,避免发布分支混乱,及缺陷修改在各分支上的修复策略。
因为主干开发要求每次变更提交都要小,并且要快速验证完,保证主干是处在可发布状态。对于一些处在开发过程中的特性,如每次变更提交,并非意味着完整特性的完成,为了隔离“特性半成品”对主干的影响,一般会采用特性开关(Feature Toggle)的方式进行隔离。即频繁的代码变更提交,可以先做集成及验证,但是在发布的角度,通过(Feature Toggle)先隐藏相关特性,只有当特性都完成之后,才打开开关,特性完全透出。
但是,特性开关的引入也并不是没有成本,因为特性开关是配置,本质上跟我们常常用到的宏定义(#if #else)没啥区别,从本质上,它也是一种代码的分支。特性开关的使用,在一定程度上让你的代码变得更脆弱。所以,特性开关的使用,是建立在良好的代码设计基础上。
为了弥补诸如特性开关这样针对某个特性开发的需要,而且现在软件开发中,越来越多的团队共同协作在一起完成某一个特性这样的场景,一种针对特性开发的分支模式就应运而生,这就是特性分支开发模式,最有代表性的就是 Git-Flow。
Git-Flow 是为了解决多个不同特性之间并行开发需要的一种工作方式。当开始一个特性的开发工作的时候,从主干上拉出一个特性分支,所有的关于该特性的开发工作都发生在这个特性分支上,当完成该特性的工作之后,再把特性分支合并回代码主路径上,并准备发布。
Git-Flow 有以下几种分支:
每个特性都有属于自己的开发分支,即 feature 分支,当一个开发者需要在两个特性上进行工作的时候,他需要做的是通过 check out 命令在两个分支之间进行切换。这样做的目的是防止开发过程中,两个特性开发工作的相互干扰。
特性开发过程中,需要针对该特性进行单独验证,当该特性并验证通过之后,merge 到一个叫做 develop 分支(大部分时间与 master 分支相近)的集成分支中,对整个软件进行验证。develop 分支永远保存都是最近的未发布版本,当 develop 分支的代码被验证可发布之后,单独从 develop 分支拉出 release 分支进行发布。
当拉出 release 分支进行发布过程中,如果发现缺陷,缺陷的修复发生在 release 分支上,所做的缺陷修改再持续同步到 develop 分支上。当 release 分支被发布完,其代码的最终版本会再次分别同步给 develop 分支和 master 主干上。我们可以发现, master 上永远保存的是可工作版本的基线。develop 分支保证的是开发集成中最新的版本。
Git-Flow 引入了一种叫做 hotfix 的分支,专门用于线上缺陷的修复。当缺陷修复完,再集成到 develop 分支,及同步到 master。其实,我们可以理解 hotfix 是一种特殊的 feature 分支,只是它的变更提交在集成到 develop 分支的同时需要同步到 master。
是不是觉得这个模式很专(fu)业(zha)的样子,那我们通过开发者做一个特性开发,按 happy path 捋捋:
Hotfix 的流程如下:
Git-Flow 的分支模式,提供了相对完备的各种分支,以覆盖软件开发过程中的大部分场景,以致于在相当长的一段时间内,人们认为这就是标准的 Git 的分支模式(因为从它的名字上看,也很容易产生这样的幻觉)。但是,Git-Flow 也存在着明显的一些问题,如:
那么,有没有一种分支模式,既包含开发任务对于主线的隔离,又相对 Git-Flow 轻量一点?我认为,真正的解决方案,应该是本质极简单的。这里,我们就不得不给大家介绍另一种分支模式,叫做 GitHub-Flow。
在 GitHub-Flow 上,第一步就是没有 Git-Flow 中所介绍的 release 分支。对于 GitHub-Flow 来说,发布应该是持续地,当一个版本准备好,它就可以被部署。同样,在 hotfix 上的处理,GitHub-Flow 认为,hotfix 与那些小的特性修改没有任何区别,它的处理方式也应该与之相似。
在 GitHub-Flow 的整体流程是:
GitHub-Flow 相比 Git-Flow 来说,有个显而易见的好处——简单。另一个好处就是持续部署的要求,尽可能快速地发现 master 分支的问题,并能通过 rollback 等机制,快速恢复。将所有内容合到 master 分支中,并经常部署,意味着你可以最小化未发布的代码量,这也是精益开发和持续交付所倡导的最佳实践。部署原本是一件很繁琐的事情,但是因为要频繁做,我们就容易把这样一件事情做简单,以达到持续交付的目的。
虽然 GitHub-Flow 简化了 Git-Flow 的分支模式,但是对于部署、环境、以及发布,该分支模式仍然存在许多未回答的问题,所以,我们希望通过 GitLab-Flow 来为这些问题提供更多的参考。
GitLab-Flow 相比于 GitHub-Flow 来说,在开发侧的区别不大,只是将 pull request 改成了 merge request,而 merge request 的用法与 pull request 类似,都可以做为代码评审、获取反馈意见的一种沟通方式。
最大的区别体现在发布侧,即引入了对应生产环境的 production 分支和对应预发环境的 pre-production 分支(如果有预发环境的话)。这样,master 分支反映的是部署在集成环境上的代码,pre-production 分支反映的是部署在预发环境的代码,production 分支反映的最新部署在生产环境的代码。
当一个特性开发完成,提交 merge request,将特性开发的代码合并到 master,并部署到集成环境进行验证;当验证通过之后,提交 merge reqeust,合并 master 到 pre-production 分支,并部署到预发环境,进行预发环境上验证;当预发环境验证成功之后,再提交 merge request,将 pre-production 分支上的代码合并到 production 分支上。
除了以上这种按环境,将主干发布向下游合并,并依次部署发布的过程。GitLab-Flow 同样支持不同版本的发布分支,即不同的版本会从 master 上拉出发布分支,不同的发布分支再走 pre-production 分支和 production 分支的方式进行发布。
从上面的介绍中,我们发现,GitLab-Flow 更多是在发布侧做了更多的工作。同样 GitLab-Flow 因为跟 GitLab 工具强依赖,所以 GitLab-Flow 与 GitLab 中的 Issue 系统也有很好的集成,在其推荐的工作模式中,每次新建一个新的 feature 分支,都是从一个 issue 上发起的,即建立 issue 与 feature 开发分支之间的映射。
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。