1 基础知识:Git 分支类型
git官方文档中,将分支分为了两大类,后续介绍的分支管理策略都是基于这个分类进行拓展的。
1.1 长期分支
在整个项目开发周期的不同阶段,定期把某些主题分支合并入其他长期分支中。这些长期分支大概分为三种类型:
(1)分支上的代码达到稳定状态,可能是已经发布了的或即将要发布。例如master分支。
(2)还有一些名为develop或next的平行分支,用来做后续开发或测试稳定性,当这些分支达到稳定状态就可以合并入master分支。
(3)还有一些proposed分支,可能包含一些不成熟的内容,当他们具有一定程度的稳定性后,再合并入更高稳定性的分支。
稳定性越高的分支,在代码提交历史中指针越靠后:
1.2 主题分支
主题分支是一种短期分支,用来实现单一特性或其相关工作。可能会被频繁创建、使用、合并、删除。这样有利于快速并且完整地进行上下文切换,将工作分散到不同的流水线中,每个流水线的分支都仅与其目标特性相关,在做代码审查的时候能更加容易地看出你做了哪些改动。
2 git-flow model
git-flow模型是Vincent Driessen在2010年提出的经典模型。他将分支分为了几种不同类型,每种类型都有明确的职责。
2.1 主要分支(The main branches)
有两个常驻分支:master和develop。
master分支中的最新代码永远都是可以发布到生产环境的稳定状态。
develop分支中的最新代码永远反映了最新的变化,为下一次发布做准备。有些人也把这个分支叫做集成分(integration branch),这是构建任何夜间自动构建的地方。
当develop分支中的代码达到稳定状态并准备发布时,需要合并到master,然后打上tag来标记发布的版本号。 因此,每次变化合并进master分支时,也就是新版本发布的时候。
2.2 支持分支(Supporting branches)
支持分支的生命周期较短,并且最终会被删除。主要用来帮助团队成员之间进行并行开发,简化功能跟踪,为产品发布做准备并协助快速解决生产中的实际问题。
通常使用三种类型的支持分支:
- 功能分支(Feature branches)
- 发布分支(Release branches)
- 修补程序分支(Hotfix branches)
这些分支中的每一个都有特定的用途,并受严格的规则约束。 这些分支并没有技术层面上的差别,只是根据我们的使用方式进行了分类。
2.3 Feature branches (topic branches)
- 分支来源:develop
- 合并到分支:develop
- 分支命名约定:除了master, develop, release-*, 或 hotfix-*的任意名字。
feature分支通常用来开发新的feature,最终会合并回到develop分支中或废弃掉。
功能分支通常仅存在于开发人员的本地仓库中,而不存在于origin中。
创建一个feature分支:
$ git checkout -b myfeature develop
feature分支开发完成后需要合并回develop分支:
$ git checkout develop
$ git merge --no-ff myfeature
$ git branch -d myfeature
$ git push origin develop
--no-ff
会创建一个合并的commit,来记录这一次的合并分支信息;如果不加该参数,合并分支的时候不会再有合并的commit。
2.4 Release branches
- 分支来源:develop
- 合并到分支:develop 和 master
- 分支命名约定:release-*
release分支为一次新的版本发布做准备,这个阶段主要进行最后的检查、小的bug修复,并为发布准备元数据(版本号、构建日期等)。
当develop分支近乎达到了新版本发布的期望状态,就可以从中创建一个新的release分支了,此时所有新版本要发布的功能都已经合并进develop。其他未来发布版本的功能必须等到此次release分支创建后,才可以合并进develop分支。
创建release分支时,确定了即将发布的版本号。例如当前版本是1.1.5,假如下一次是比较大的版本发布,所以release分支可以命名为release-1.2。
$ git checkout -b release-1.2 develop
$ ./bump-version.sh 1.2
$ git commit -a -m "Bumped version number to 1.2"
当创建完release分支时,我们升级了版本号。在这里,bump-version.sh是一个虚构的shell脚本,它更改工作副本中的一些文件以反映新版本。(当然,也可以手动更改)然后,提交升级后的版本号。
这个新分支会一直存在,直到最终发布,在此期间只允许bug修复,禁止添加大的新功能。当release分支的状态达到可以发布的程度时,就合并进master分支。然后在master分支上的commit打上tag,来记录版本号,方便将来回滚或追溯。最后,release分支上的改动还要合并回develop分支。在合并回develop分支的时候可能会出现冲突,因为develop分支可能合并了一些新的feature,这时需要解决冲突并提交。
$ git checkout master
$ git merge --no-ff release-1.2
$ git tag -a 1.2
$ git checkout develop
$ git merge --no-ff release-1.2
在打tag的时候还可以使用-s或-u 标志来对tag进行加密签名。
最后可以删除这个release分支了。
$ git branch -d release-1.2
2.5 Hotfix branches
- 分支来源:master
- 合并到分支:develop 和 master
- 分支命名约定:hotfix-*
当生产版本出现了一个严重的bug必须要立刻修复时,可以从当前master分支上创建一个hotfix分支来修复bug。
例如当前版本号为1.2的生产环境中出现了一个严重的bug急需修复,可以创建一个hotfix-1.2.1分支。
$ git checkout -b hotfix-1.2.1 master
$ ./bump-version.sh 1.2.1
$ git commit -a -m "Bumped version number to 1.2.1"
git commit -a -m
合并了 git add
和 git commit
两个步骤。
修复bug并提交:
$ git commit -m "Fixed severe production problem"
修复完,这些bugfix需要合并进master,同时还需要合并回develop。
$ git checkout master
$ git merge --no-ff hotfix-1.2.1
$ git tag -a 1.2.1
$ git checkout develop
$ git merge --no-ff hotfix-1.2.1
如果这时还有一个release分支同时存在,那么hotfix分支就不需要合并回develop分支,而是合并到release分支,因为release分支最终还是回合并回develop分支。但如果这个bug会影响到develop分支上的开发,则可以合并到develop分支。
最后,删除hotfix分支。
$ git branch -d hotfix-1.2.1
2.6 评价
作者在2020年3月5日对该模型又做了一些说明:由于gitflow-model更适合多个版本并存的场景,但现如今的web应用很少会需要多版本并存,更多的是一个版本持续交付,因此推荐使用更简单的模型,例如GitHub flow。
优点:各分支权责分明,清晰可控,是很多分支管理策略的启蒙模型。
缺点:
- 合并冲突:同时长期存在的分支可能会很多:master、develop、release、hotfix、若干并行的feature分支。两两之间都有可能发生冲突。频繁手动解决冲突不仅增加工作量,而且增大了出错的风险。
- 功能分离:功能并行开发时,合并分支前无法测试组合功能,而且合并后可能会出现互相影响。
- 无法持续交付:git-flow更倾向于按计划发布,一个feature要经历很多步骤才能发布到正式环境,难以达到持续交付的要求。
- 无法持续集成:持续集成鼓励更加频繁的代码集成和交互,尽早解决冲突,而git-flow的分支策略隔离了代码,尽可能推迟代码集成。
3 GitHub flow
GitHub flow是一个轻量级的、基于分支的工作流。是 Github 使用的工作流程。 它只保留一个主分支master,开发新功能时基于master创建一个feature分支,开发完成后发起Pull-Request。小组内进行讨论和评审,并及时反馈,开发者在此期间可以不断提交代码。评审通过后再合并回master分支,并进行部署。
3.1 评价
优点:
- 分支简单,只有master主分支,和feature分支,降低了分支管理的复杂度。
- 符合持续集成:尽早反馈、有问题尽早解决,集成周期短。
缺点:master分支发布前,其他代码无法提交,否则master分支就会与刚发布的版本不一致。
4 Gitlab flow
吸取了Git flow 与 Github flow和优点,Gitlab推荐的做法,具体可见阮一峰老师的这篇文章,此处不再赘述。
5 Trunk based Development 主干开发
存在一个分支作为主干分支,即master分支,开发人员直接向主干分支提交代码,或将feature分支只留在本地。原则上,代码仓库就只有一个master分支了。开发团队成员可以一天内多次将代码提交到主干分支,使团队的工作在24小时内就可以被整合,保证了代码版本随时处于可发布的状态。避免长期存在多个分支,有利于持续集成和持续交付。
发布时直接从主干分支上创建release分支,并打上tag,发布完成后删除release分支。换句话说,发布分支是主干分支某个时点的快照。
主干开发要求每次提交都是一个可上线的版本,因此也带来了一些问题,下面给出相应的解决方案:
- 如何避免发布未完成的feature:使用Feature Toggle —— 在代码中增加一个特性开关来控制新特性的打开与关闭。
- 如何修复线上bug:发布时打tag,如果这个版本出现问题,如果master分支没有新的提交,就直接在master分支上修复,然后合并到release分支;如果master分支已经有新提交了则:
- 从tag处创建release分支
- 在master上修复bug
- 将修复bug的提交cherry pick到release分支
- 在release分支上打tag并发布
6 AoneFlow
AoneFlow是阿里内部使用的代码分支管理策略,“它基本上兼顾了 TrunkBased 的‘易于持续集成’和 GitFlow 的‘易于管理需求’特点,同时规避掉 GitFlow 的那些繁文缛节”。
AoneFlow 只使用三种分支类型:主干分支、特性分支、发布分支。一个完整的流程如下:
- 从主干上创建一个特性分支(前缀为feature/),然后在这个分支上进行代码开发。可能会有多个特性分支并行开发。
- 从主干上创建一个发布分支(前缀为release/),将所有要发布的特性分支合并到发布分支上。发布分支可以有多个,分别对应不同的环境,如release/test分支对应部署测试环境,release/prod分支对应线上正式环境等等。也可以一个发布分支对应多个环境。发布分支的特性组成是动态的,例如某个功能或特性因为某些原因无法如期发布,可以删除这个分支,再从主干重建一个同名发布分支,只把需要发布的特性分支合并过来。发布分支之间松耦合,可以将不同的特性组合发布到不同的环境中。
- 发布分支发布到线上后再合并回主干分支,并在主干分支打上标签,最后删除与该发布分支关联的特性分支。