该系列的前面两篇文章主要讲了 Git 相关原理和 Git 常见的一些操作,接下来这篇文章给大家介绍一下业界常见的 Git 工作流。
常见业界工作流分析
git flow
说起 Git 工作流就不得不提 Git Flow, 它是社区最早出现的一批 Git 工作流。
Git Flow的核心是五种分支类型和几条基本规则:五种分支类型包括两条长期分支 master 和 develop,以及多条短期分支 feature-xxx,release-xxx, hotfixs。
几条基本规则如下:
- 开始工作前,从 develop 分支拉出 feature-xxx 分支。
- feature-xxx 分支开发完成后,merge 回 develop 分支。
- 准备发布时,从 develop 分支拉出 release-xxx 分支,在该分支上进行测试和修改相关 bug,但禁止在该分支上进行特性开发。修改完 bug 后,合并代码到 master 分支,并打上相关版本的 tag。最后再把 release 分支上的代码合并到 develop 分支,已防止在 release 分支上修改的 bug 下次再被引入。
- 修复线上 bug 时,需先基于 master 分支拉出 hotfixs 分支,在hotfix 修复完bug 之后,把 hotfix 分支的代码合入 master, develop, 以及 release-xxx(如果此时存在)。
Git Flow 的流程非常完善,对每个阶段的每项操作定义都十分明确,几乎能覆盖大部分开发场景。但同样也是因为其完备的流程和复杂的阶段定义,导致使用起来过于繁琐,无法要求所有团队成员都按照这个流程严格执行。此外,流程的繁琐也给 CI/CD 带来了诸多不便,特别是对于那些一天就要发多个版本的公司来说,它们可能根本就不需要 release 和 hotfix。
Github Flow
和 Git Flow 不同,Github flow 采用了极简的分支模式——只有 master 分支和相关的功能分支。它的规则也很简单。
- 开始开发前,从 master 分支拉出相关的功能分支(功能分支的名字要尽可能表示本次开发的目的)。
- 在功能分支上进行开发和提交,并经常性的把本地的功能分支推送到服务端的同名分支上。
- 当功能分支的开发完成之后,发起一个 pull request (同 Gitlab 中的 merge request) 请求合并代码到 master 分支。
- 当其他人 review 完代码,并且给出相关的通过标识之后,就能把该功能分支的代码合并到 master 分支中。
- 一旦完成合并,你就可以立即发布该功能了。(master 分支的代码都是可部署的)
Github Flow 流程非常轻量,几乎没有什么上手成本,同时通过将所有代码通过Pull Request 合并到 master 分支然后进行发布,可以最大程度上减少未发布代码的数量,这也同样符合精益和持续交付的准则。
但于此同时,由于这个工作流过于轻量,合并代码到 master 分支之后立即就进行代码的构建和发布,因此对团队的要求也比较高,需要团队有完备的自动化工作流作为支持(特别是完备的自动化测试覆盖),不然软件的质量不太好保障。
Gitlab Flow
Gitlab Flow 是 Github Flow 的演进版本,它解决了一些 Github Flow 没有解决的问题,比如多环境部署,多版本部署等。
3.1 演进版本01 - 带 Production 分支
该演进版本主要在 Github Flow 的基础上增加了 Production 分支,旨在解决不具备主干开发能力,无法精准的控制发布时间,同时又需要持续进行集成这种情况。
这里的 Production 作为发布分支,当有新版本需要发布时,从 master 分支中拉出相关的版本进行发布。相较于 Git Flow 的 release-xxx 分支,在 master 中打 tag 会方便很多。
3.2 演进版本02 - 带 Environment 分支
这是 Gitlab Flow 的另一个演进版本,在 Github Flow 的基础上新增 Pre-Production 分支和 Production 分支。旨在帮助那些更注重软件质量(同时又没有太多自动化测试覆盖)的公司,使用该分支模型,当只有各个环境的验证都没问题后,才进行最终的软件发布。
这个工作流仅在下游进行提交,这样可以确保在各个环境中的代码都是经过测试的。如果此时需要进行 hotfix,通常的做法是在 feature-xxx 分支上进行开发,然后合并到 master 分支。如果 master 分支的自动化测试通过,则把该feature-xxx 分支的代码合入 Pre-Production,最后合入 Production 分支。
3.3 演进版本03 - 带 Release 分支
这是 Gitlab Flow 的第三个演进版本,在 Github Flow 的基础上新增了多条 release 相关的分支。它适用于团队不具备主干发布能力,但是同时又需要对外维护好几个不同版本的情况。
在从 master 上拉出 release 相关的分支后,仅添加一些严重 bug 的修复代码到该 release 分支上。通常的做法是,先把这些修复 bug 的代码合并到 master 分支,然后把这些修复代码 cherry-pick 到 release 相关的分支中。因为如果直接合到相关的 release 分支的话,可能会忘了 merge 回 master分支,导致之后拉出的 release 分支也会出现相同的问题。
Gitlab Flow 在 Github Flow的基础上,增加了环境分支,release分支和发布分支等特性,基本上能满足大多数开发场景的需求。
Trunk-based Flow
Trunk-based Flow 和 Github Flow 类似,都有一个主干分支接收所有开发者的 commit, 并在此基础上,进行持续的集成和持续的发布。
它和 Github Flow 不同的一个点在于 Trunk-based Flow 部署的时机要晚于前者。Trunk-based Flow在需要进行发布的时候,从主干分支中拉出相关的发布分支进行部署发布(类似于基于 Production 的 Gitlab Flow)。而 Github Flow 通常在 feature-xxx 分支的 Pull Request完成之后就自动部署发布。
对于 Trunk-based的主干分支,你可以采用 Pull Request 的方式进行合并,也可以通过直接提交代码的方式(小团队),提交到主干的代码也不一定要求是可部署的,但是最好是要走 Code Review。在进行代码合并时(到主干分支),采用 Fast-Forward 的形式保证主干的代码是一条线,等到了需要发布的时候,在合适的 commit 点拉出一条 release-xxx 的分支,进行正式上线操作。
一旦发现 release 分支有 hotfix 的需求,则先在主干分支上进行 fix 开发,测试完成后,cherry-pick 到 release-xxx 分支,确保修复代码即在 release-xxx 中上线,又能被下一个 release 周期所包含。
Trunk-Based Flow很好,也被很多大公司广泛采用( Google, FaceBook ), 但是它的缺点也很明显,和上面提到的 Github Flow 类似,它需要完善的自动化测试覆盖,另外它有太多的工作同时在主干上,如果有很多版本的需求同时并行在主干上,在发布的时候可能比较棘手,可以通过 FeatureToggle 来一定程度上解决这个问题,但是这又对开发团队的能力提出了比较高的要求,因此不太适合大多数公司。
One FLow
One Flow 是 Git Flow 的简化版本(如作者说,功能并未简化,Git Flow能实现的,OneFlow 也能实现)。它主张保留一条长期存在的主干分支(一般为 master,也可以命名为任何你想要的名字),然后使用一些短期分支来进行代码之间的流转(短期分支有 feature-xxx,release-xxx,hotfix-xxx)。
(1)开发新功能时,基于主干分支切出一条新的 feature-xxx 分支,完成该功能后,把 feature-xxx 分支的代码合并(使用merge, rebase都行)回主干分支,完成之后删除 feature-xxx分支。
(2) 要进行发布时,从主干分支切出一条新的 release-xxx 分支,完成发布后,打上 tag, 合并 release-xxx 分支的代码到主干分支,完成之后删除 release-xxx 分支。
(3)当要进行 hotfix 时,从最新提交的版本 tag 中切一条新的 hotfix-xxx分支,完成 bug 修复后,打一个新的 tag, 并把 hotfix-xxx 分支的代码合并回主干分支,完成之后删除 hotfix-xxx 分支。
One Flow 还有一套变种,使用双分支的模式 —— 主干分支和发布分支。 主干分支和上面一样,发布分支主要就是用来记录最新发布的提交。这样做的好处就在于能更方便的看到代码库的最新发布版本而不用去翻历史 tag。
下面再简述一下双分支模式的工作流程
(1)开发新功能时,基于主干分支切出一条新的 feature-xxx 分支,完成该功能后,把 feature-xxx 分支的代码合并(使用merge, rebase都行)回主干分支,完成之后删除 feature-xxx分支。
(2) 要进行发布时,从主干分支切出一条新的 release-xxx 分支,完成发布后,打上 tag, 合并 release-xxx 分支的代码到主干分支,完成之后删除 release-xxx 分支,再基于打的 tag,合并回发布分支(新增步骤)。
(3)当要进行 hotfix 时,直接从发布分支切(而不是之前的 tag )中切一条新的 hotfix-xxx分支,完成 bug 修复后,打一个新的 tag, 并把 hotfix-xxx 分支的代码合并回主干分支,完成之后删除 hotfix-xxx 分支,最后基于新的 tag,合并回发布分支。(新增步骤)
One Flow 模式是简化版的 Git Flow,它的功能像 Git Flow 一样完备,但是它的流程却没有 Git Flow 那么复杂。
但是它的身上仍然有 Git Flow 存在的一些问题,比如对持续集成不那么友好等。
Aone Flow
Aone Flow 是阿里巴巴基于 Trunk based Flow 以及 Git Flow 的特点,提出的一个新的 Git 工作流思路。
该模型只使用三种分支类型:主干分支、特性分支、发布分支,以及三条基本规则。
(1)开始工作前,从主干创建特性分支。
(2)通过合并特性分支,形成发布分支。
(3)发布到线上正式环境后,合并相应的发布分支到主干,在主干添加标签,同时删除该发布分支关联的特性分支。
Aone Flow 的 “pick” 模式,适合复杂项目的多版本并行开发,避免了 Trunk-based Flow 会引入未完成 feature 的问题。但是操作起来有点麻烦,特别是对于最后通过“pick feature”进行发版的人。 由于一个发版周期内可以会有多个 feature, 上一个版本周期中可能也会有遗留的一些 feature, 随着时间推移,留存的 feature 数会越来越多,导致发版人最后在风中凌乱。
总结
本文介绍了业界常见工作流,在研究业界工作流的过程中,发现每种工作流都设计的很好,好像都能拿来被实际业务直接使用,但是当结合团队实际情况之后,发现好像并不是这样。git 工作流其实只是整个开发链路中的一环,你需要从团队的实际情况出发,比如产品的发版策略,比如公司当前基建的情况,这些都是团队在制定 git 工作流的同时,不得不去考虑的东西。
所以要单纯论一个技术或流程的优劣是没有意义的,问题是场景,同样的,没有适合所有场景的 git 工作流,根据自己实际场景定制出来的才是最适合你们自己的。
参考资料
- https://tech.meituan.com/2019/01/10/traffic-git-branch-management.html
- https://www.cnblogs.com/pluto4596/p/11464819.html#oneflow
- https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/git-flow/
- https://developer.aliyun.com/article/573549
- https://www.infoq.cn/article/9dotsvlwscznbxjpxfqe
- http://insights.thoughtworks.cn/gitflow-consider-harmful/
- https://gitential.com/git-branching-strategies-for-your-team-how-to-choose-the-best
- https://www.endoflineblog.com/oneflow-a-git-branching-model-and-workflow