Git 分支管理策略总结

7,070 阅读9分钟

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 addgit 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。

优点:各分支权责分明,清晰可控,是很多分支管理策略的启蒙模型。

缺点:

  1. 合并冲突:同时长期存在的分支可能会很多:master、develop、release、hotfix、若干并行的feature分支。两两之间都有可能发生冲突。频繁手动解决冲突不仅增加工作量,而且增大了出错的风险。
  2. 功能分离:功能并行开发时,合并分支前无法测试组合功能,而且合并后可能会出现互相影响。
  3. 无法持续交付:git-flow更倾向于按计划发布,一个feature要经历很多步骤才能发布到正式环境,难以达到持续交付的要求。
  4. 无法持续集成:持续集成鼓励更加频繁的代码集成和交互,尽早解决冲突,而git-flow的分支策略隔离了代码,尽可能推迟代码集成。

3 GitHub flow

GitHub flow是一个轻量级的、基于分支的工作流。是 Github 使用的工作流程。 它只保留一个主分支master,开发新功能时基于master创建一个feature分支,开发完成后发起Pull-Request。小组内进行讨论和评审,并及时反馈,开发者在此期间可以不断提交代码。评审通过后再合并回master分支,并进行部署。

3.1 评价

优点:

  1. 分支简单,只有master主分支,和feature分支,降低了分支管理的复杂度。
  2. 符合持续集成:尽早反馈、有问题尽早解决,集成周期短。

缺点:master分支发布前,其他代码无法提交,否则master分支就会与刚发布的版本不一致。

4 Gitlab flow

吸取了Git flow 与 Github flow和优点,Gitlab推荐的做法,具体可见阮一峰老师的这篇文章,此处不再赘述。

5 Trunk based Development 主干开发

存在一个分支作为主干分支,即master分支,开发人员直接向主干分支提交代码,或将feature分支只留在本地。原则上,代码仓库就只有一个master分支了。开发团队成员可以一天内多次将代码提交到主干分支,使团队的工作在24小时内就可以被整合,保证了代码版本随时处于可发布的状态。避免长期存在多个分支,有利于持续集成和持续交付。

发布时直接从主干分支上创建release分支,并打上tag,发布完成后删除release分支。换句话说,发布分支是主干分支某个时点的快照。

主干开发要求每次提交都是一个可上线的版本,因此也带来了一些问题,下面给出相应的解决方案:

  1. 如何避免发布未完成的feature:使用Feature Toggle —— 在代码中增加一个特性开关来控制新特性的打开与关闭。
  2. 如何修复线上bug:发布时打tag,如果这个版本出现问题,如果master分支没有新的提交,就直接在master分支上修复,然后合并到release分支;如果master分支已经有新提交了则:
  • 从tag处创建release分支
  • 在master上修复bug
  • 将修复bug的提交cherry pick到release分支
  • 在release分支上打tag并发布

6 AoneFlow

AoneFlow是阿里内部使用的代码分支管理策略,“它基本上兼顾了 TrunkBased 的‘易于持续集成’和 GitFlow 的‘易于管理需求’特点,同时规避掉 GitFlow 的那些繁文缛节”。

AoneFlow 只使用三种分支类型:主干分支、特性分支、发布分支。一个完整的流程如下:

  1. 从主干上创建一个特性分支(前缀为feature/),然后在这个分支上进行代码开发。可能会有多个特性分支并行开发。

  1. 从主干上创建一个发布分支(前缀为release/),将所有要发布的特性分支合并到发布分支上。发布分支可以有多个,分别对应不同的环境,如release/test分支对应部署测试环境,release/prod分支对应线上正式环境等等。也可以一个发布分支对应多个环境。发布分支的特性组成是动态的,例如某个功能或特性因为某些原因无法如期发布,可以删除这个分支,再从主干重建一个同名发布分支,只把需要发布的特性分支合并过来。发布分支之间松耦合,可以将不同的特性组合发布到不同的环境中。

  1. 发布分支发布到线上后再合并回主干分支,并在主干分支打上标签,最后删除与该发布分支关联的特性分支。

References

  1. git分支开发工作流(Branching Workflows)
  2. Git 工作流程
  3. 分支模型与主干开发
  4. 版本分支管理标准 - Trunk Based Development主干开发模型
  5. Git 分支 - 分支开发工作流
  6. A successful Git branching model
  7. Understanding the GitHub flow
  8. 在阿里,我们如何管理代码分支?
  9. 谷歌的代码管理
  10. cn.trunkbaseddevelopment.com/