前言
在介绍TBD Worflow的部署之前,我们先来看一下什么是TBD,以下文章参考
Trunk Based Development: Introduction
简介
TBD准确的说不是一种"分支策略模型",它更像是一种"分支策略模型"的规范。
一般而言我们只有一个分支----trunk分支(master分支),所有的开发和release工作都是在这个分支上进行。我们可以在trunk分支上以tag的形式进行release版本。如果有需要的话我们可以从相应的tag处检出release分支。通过从trunk分支上cherry-pick bug fix patch的形式,对release分支的维护。release分支和master分支之间不存在merge操作。
从Google的角度看Trunck Based Development:
Trunck Based Development使你能够免除merge所带来的的痛苦(尤其是对那些long lived branches进行merge操作)。
branch只是用来release使用,release branch是trunk的一个snapshot(譬如从某个tag处检出)及一些trunk上的commits的cherry-pick的合体。
Release从哪产生
有两种生成release的方式:
- release from trunk
- branch for release
branch for release
commit的规范
- commit应尽量小,及时(频率高):以便合作的其他同仁能够尽快同步进度,避免重复开发和合并冲突。
- commit的功能不能打断构建过程:不影响合作的其它同仁的开发工作。
Branch by Abstraction
我们开发过程中应尽量遵循尽量短的feature分支(或者叫commit),但是肯定会遇到某些feature需要一段时间才能运行合并入trunk的情况。那么此时可以使用Branch by Abstraction的解决方案。
参考Branch by Abstraction Branch By Abstraction?
举例:
- 当前project的func A需要重构优化为func B,但是func A比较复杂,需要一定的时间(1 week or more)才能够开发完成,且不保证功能稳定。
- 因为我们对trunk的基本要求就是稳定、可随时发布。开发过程中我们不能随时将部分完成的feature提交到turnk上,打断它的构建(影响其他同仁的开发)。
- 但是如果从trunk上切换出一个分支来,经过一段较长的时间开发完成,再合并入trunk,很有可能会有冲突。
- 我们可以使用Branch by Abstraction(听起来很拗口,但是思路很简单)
- project中添加进抽象层func C,将调用func A的地方改为调用func C,func C调用func A
- 在project中添加进func B,func C可以通过配置选择调用func A还是func B(默认是func A,这样不影响其他同仁的开发工作;开发这一功能的同仁使用配置为func B)
- 当func B经过充分的检验后,可以移除func A和抽象层func C,只保留func B。
Fix production bugs on Trunk
在"Branch for Release"方案中,当已经release的版本发现bug时。
- 需要在trunk上复现bug
- 然后在trunk上修复bug
- 进行测试验证
- 将bugfix cherry-pick到release branch上
- 在release branch上进行测试验证
- 发布release branch
注意良好的习惯是从trunk上往release branch上cherry-pick,而不是先往release branch上提交,再cherry-pick到trunk上。这么做的原因很简单,防止你忘记将这些commits cherry-pick到trunk上(譬如说某些员工离职,或者下班太晚,第二天忘记cherry-pick到trunk上),以后在trunk上再去浪费时间解决先前已经解决的问题。
Late creation of release branches
作为"Branch for Release"的替代方案,从trunk的某个tag处发布版本,而不是创建release分支,等待发布的版本需要修复bug时从trunk分支的tag处检出release分支。
Release from trunk
这种开发模式适合那些具有非常高发布频率的项目(譬如每天都要发布)。项目版本的命名方式也不是Dewey-decimal的数字命名方式,而是通常以日期、时间来命名。
不适合我们的使用场景
release branch的角色
一般而言是对发布版本进行bug fix(在master上修复,cherry-pick到release,如果master上能复现bug的话),特殊情况我们可以将master上开发的feature cherry-pick过来,譬如:
- sdk_v1.1.3已经发布给客户,现在客户需要feature_A,而sdk_v1.1.x上没有feature_A
- 如果客户坚持继续使用sdk_v1.1.x版本(不想更新版本),我们就得在master上开发feature_A然后cherry-pick到sdk_v1.1.4上(如果master上原本有feature_A,直接cherry-pick)
相比于只cherry-pick bug fix而言,cherry-pick feature的工作量要大很多,而这种情况可以预见我们会碰到,所以请大家在开发feature 的时候,一定不要将一个feature放到多个feature branch中。如果feature很大,需要逐步实现的话,也请将他们关联到同一个jira上,便于我们的追溯
release branch检出的时机
在我们的版本命名规范中x.y.z,只有主次版本需要使用到release branch,譬如:
- sdk_v1.2_release
- sdk_v1.3_release
- sdk_v2.1_release
Release Branch检出的时机,分为计划内的检出(主动检出)和计划外的检出(被动检出):
-
当某个版本的feature开发完毕(主动检出)
譬如master分支上sdk_v1.3的功能已经开发完毕,我们需要从master分支的HEAD上检出一个新的release分支sdk_v1.3_release -
当某个版本A正在开发过程中,此时需要同时开发新版本B(被动检出)
尽管我们不希望这样做,因为这样需要将A需要的剩余feature在B中完成,然后再cherry-pick到A上
譬如master分支上sdk_v1.3正在开发,此时部分Developer需要为sdk_v2.1进行开发。
- 我们需要在master上检出sdk_v1.3_release分支
- sdk_v2.1的开发工作在master上开发(master上下一个发布版本是sdk_v2.1)
- sdk_v1.3的剩余工作需要在master上开发后再cherry-pcik到sdk_v1.3_release分支上
- 从某个release分支上检出其他release分支
尽量少这样干,维护多个release分支的成本比较高。
Trunk Based Development三选一
一共有三种类型的Trunk Based Development:
- Committing Straight to the Trunk
- Short-Lived Feature Branches
- Coupled “Patch Review” System
结合我们目前的Gitlab托管平台,我们选择第二种
Committing Straight to the Trunk
适合团队规模1~1000人,流程如下:
- 所有人直接往trunk/master分支上推送提交
- CI进行build测试,测试不通过需要修改后再提交
- CI通过后Reviewer可以pull/sync此部分代码
- Reviewer评审、测试通过
- Reviewer不通过,开发者需要再修改提交
缺点是,所有人直接往trunk/master上提交,存在以下风险:
- 错误的修改了分支历史(影响非常严重,譬如由于提交前没有同步,删除了在此之前其他人的的一些提交记录)
- 提交中包含错误,影响其他人的开发工作(这种情况出现的概率较大,需要频繁地进行revert)
Short-Lived Feature Branches
适合团队规模2~1000人,流程如下:
- 将一个或多个commit以feature branch的形式进行推送提交
- CI进行build测试,测试不通过需要修改后再提交
- 提交后发起merge-request(pull-request)请求,指定reviewer
- Reviewer收到请求后进行评审、测试
- Reviewer评审、测试通过,合并请求
- Reviewer不通过,关闭当前的merge-request,删除feature_branch,修改后再提交
Coupled “Patch Review” System
适合团队规模2~40000人,流程如下:
同Short-Lived Feature Branches相同,只不过提交、评审的基本单元不再是feature_branch,而是每一个commit。
相较于Short-Lived Feature Branches而言,它存在一下两点优势:
- 提交、评审粒度小,conflict解决成本低
- 不需要检出feautre_branch和合并feature_branch,流程更简洁
目前Gerrit、Rietveld和Phabricator包含"patch review system"。