Git 工作流 — 分支策略


# Git 工作流 — 分支策略

# 前言

设想这样一个场景:某企业的开发团队使用 Git 管理日常开发工作,最初开发团队的代码有一条主干分支(master/main)。后来为了开发新功能,从主干分支拉出一条特性分支,但新功能完成后,该特性分支没有合入主干分支,而是作为下次开发的主干分支,重新拉出一条新的特性分支,导致主干分支一直形同虚设,团队没有一条稳定的代码分支。

这个问题很大程度上源于团队对分支策略的不了解,导致分支策略使用很混乱。因此,我们需要采用 Git 工作流 —— 分支策略来此类问题。

目前业界常见的 Git 分支策略主要有:GitFlow、GitHubFlow 以及 GitLabFlow,本文一一作介绍,方便团队开发时,根据自身情况选择最合适的方案。

# GitFlow 策略

GitFlow 是这三种分支策略中最早出现的。

GitFlow 通常包含五种类型的分支:master 分支、develop 分支、feature 分支、release 分支以及 hotfix 分支。

# 分支定义

master 分支:主干分支,这是一个稳定的分支,又称为保护分支,表示正式发布的历史,所有对外正式版本发布都会合并到这里,并打上版本标签。通常情况下只允许其他分支将代码合入,不允许向 master 分支直接提交代码。

develop 分支:开发分支,用来用来整合功能分支,表示最新的开发状态。包含要发布到下一个 release 的代码。

  • 注意每次合并到 master 都要打上 tag,方便定位。
  • master 分支和 develop 分支都是历史分支,用来记录项目的历史记录。相对于这两个分支,下面的其它分支都属于临时分支,这些分支完成自己的使命后就可以被删除。

feature 分支:功能分支,通常从 develop 分支拉出,每个新功能的开发对应一个功能分支,用于开发人员提交代码并进行自测。自测完成后,会将 feature 分支的代码合并至 develop 分支,进入下一个 release

release 分支:发布分支,发布新版本时,基于 develop 分支创建,从此刻开始新的功能不会加到这个分支,这个分支只应该做 bug 修复、文档生成和其他面向发布的任务。当发布分支足够稳定后,它的生命周期就可以结束了,这时候将 release 分支 合并到 master 分支,然后打上 tag 版本号;接着还需要将 release 分支合并回 develop 分支。

  • release 分支可以删掉,因为在 master 上打了 tag,即使删掉了发布分支,你也可以很方便的重新创建一个:
    # 基于 tag v1.0.0 创建一个分支
    $ git checkout -b release/v1.0.0 v1.0.0
    
    1
    2
  • release 分支是 develop 和 master 之间的缓冲区,对于一个发布,往往会在发布分支中停留一段时间,等待稳定后才合并到 master。
  • release 分支使得团队可以在完善当前发布版本的同时,不阻拦新功能的开发。

hotfix 分支:热修复分支,生产环境发现新 bug 时创建的临时分支,这些 bug 可能比较紧急,而且跨越多个分支,所以可以创建一个 hotfix 分支快速修复 master 上面的 bug。问题验证通过后,合并到 masterdevelop 分支,并打上 tag。

hotfix 分支是唯一一个可以从 master 分叉出来的分支类型。

# 开发流程

通常开发过程中新功能的开发过程如下:

  • develop 分支拉取一条 feature 分支,开发团队在 feature 分支上进行新功能开发。
  • 开发完成后,将 feature 分支合入到 develop 分支,并进行开发环境的验证。
  • 开发环境验证完成,从 develop 分支拉取一条 release 分支,到测试环境进行 SIT/UAT 测试。
  • 测试无问题后,可将 develop 分支合入 master 分支。
  • 待发版时,直接将 master 分支代码部署到生产环境。

可参考下图:

GitFlow

(GitFlow 示意图)

# 优缺点

GitFlow 的优点:

  • 分支各司其职,覆盖大部分开发场景。
  • 预期 master 分支中任何 commit 都是可部署的。
  • 严格按照流程执行,出现重大事故的情形会大大降低。

GitFlow 的缺点:

  • 过于繁琐,无法要求所有团队成员按照这个流程严格执行。
  • 违反 Git 提倡的 short-lived 分支原则。
  • 如果特性分支过多的话很容易造成代码冲突,从而提高了合入的成本。
  • 由于每次提交都涉及多个分支,所以 GitFlow 也太不适合提交频率较高的项目。
  • master 分支历史记录并不干净,只能通过打 tag 标记哪些是 master 真正要部署的。
  • 对持续部署和 Monorepo (opens new window) 仓库不友好。

# GitHubFlow 策略

GitHubFlow 看名字也知道和 GitHub 有关,它来源于 GitHub 团队的工作实践。当代码托管在 GitHub 上时,则需要使用 GitHubFlow。相比 GitFlow 而言,GitHubFlow 没有那么多分支。

GitHubFlow 通常只有一个 master 分支是固定的(现在被更名为 main 了 (opens new window)),而且 GitHubFlow 中的 master 分支通常是受保护的,只有特定权限的人才可以向 master 分支合入代码。

# 适用场景

  • 适合开源项目。开源项目需要接受任何开发者的代码共享,无需给他们正式的代码库的写权限。
  • 适合大型的,自发性的团队。fork 的方式可以提供灵活的方式来安全协作。

# 开发流程

  • 在 GitHubFlow 中,要求每个成员先 fork 项目到自己的目录(fork 的本质就是让 GitHub 将项目克隆到你的个人目录)。
  • 每次有新功能开发或修复 bug 时,需要从 master 分支拉取一个新的 feature 分支,在这个新分支上进行代码提交。
  • 功能开发完成,开发者创建 feature 分支合并到上游(upstream)项目的 Pull Request(简称 PR),通知源仓库开发者进行代码修改 Review。
  • 上游项目的所有者决定是否合并你的代码,如果确认无误,他会将代码合入 master 分支。

可参考下图:

GitHubFlow

(GitHubFlow 示意图)

很多人可能会问,提交代码通常是 commit 或者 push,拉取代码才是 pull,为什么 GitHubFlow 中提交代码是「Pull Request」。因为在 GitHubFlow 中,PR 是通知其他人员到你的代码库去拉取代码至本地,然后由他们进行最终的提交,所以用 pull 而非 push

# 优缺点

GitHubFlow 的优点:

  • 相对于 GitFlow 来说比较简单。

GitHubFlow 的缺点:

  • 因为只有一条 master 分支,万一代码合入后,由于某些因素 master 分支不能立刻发布,就会导致最终发布的版本和计划不同。

# GitLabFlow 策略

GitLabFlow 出现的最晚,GitLabFlow 是开源工具 GitLab 推荐的做法。

GitLabFlow 支持 GitFlow 的分支策略,也支持 GitHubFlow 的「Pull Request」(在 GitLabFlow 中被称为「Merge Request」)。

相比于 GitHubFlow,GitLabFlow 增加了对预生产环境和生产环境的管理,即 master 分支对应为开发环境的分支,预生产和生产环境由其他分支(如 pre-productionproduction)进行管理。在这种情况下,master 分支是 pre-production 分支的上游,pre-productionproduction 分支的上游;GitLabFlow 规定代码必须从上游向下游发展,即新功能或修复 bug 时,特性分支的代码测试无误后,必须先合入 master 分支,然后才能由 master 分支向 pre-production 环境合入,最后由 pre-production 合入到 production

基于环境:

GitLabFlow

(GitLabFlow:master -> production,图来源于网络)

GitLabFlow

(GitLabFlow:master -> pre-production -> production,图来源于网络)

基于发布计划:

GitLabFlow

(GitLabFlow:master -> stable,图来源于网络)

GitLabFlow 中的 Merge Request 是将一个分支合入到另一个分支的请求,通过 Merge Request 可以对比合入分支和被合入分支的差异,也可以做代码的 Review。

GitLabFlow 并不像 GitFlow、GitHubFlow 一样具有明显的规范,它更多是在 GitHubFlow 基础上,综合考虑环境部署、项目管理等问题而得出的一种实践。

# 团队定制的分支策略

比较流行的 Git 分支策略是 GitFlow,但是大部分团队会根据自己的情况制定自己的 Git 工作流规范,例如我们团队的分支规范:

master 分支:表示一个稳定的发布版本。

  • 场景:前端会跟随后端的版本迭代,在 dev 分支测试稳定后,合并到 master 分支,并使用 tag 标记前端版本和对应的后端版本。
  • tag 规范:v{version}@{product_version},例如 v0.1.0@SQY_3.0。
  • 人员:由项目负责人进行审核合并,普通开发者没有权限。

dev 分支:开发者主要工作的分支,最新的特性或 bug 修复都会提交到这个分支。开发者如果在该分支进行了提交,在 push 到远程之前应该先 pull 一下,并尽量使用 rebase 模式,保证分支的简洁。

  • 命名规范:dev
  • tag 规范:在 dev 分支中也可能会经历发布过程,例如 bug 修复版本。这里同样使用 tag 来标记这些发布,例如 v0.1.1。
  • 提交规范:如果是在开发分支上进行开发,在推送到远程之前,应该使用 git rebase 形式更新本地分支。
  • 建议操作:建议时不时地 pull 一下,尤其是每天打开电脑正式工作前,保证本地代码版本与远端版本的一致性(因为很多人都不能优雅地解决代码冲突)。

feature 分支:涉及多人协作或者大功能的开发,应该从 dev 分支 checkout 出独立的 feature 分支,避免干扰 dev 分支。

  • 场景
    • 涉及多人协作:团队多个成员在同一个项目下负责开发不同的功能,这时候每个成员在自己的 feature 分支独立开发。
    • 大功能开发:大功能开发跨越周期比较长,需要多次迭代才会稳定。这时候应该在独立的分支上开发。方便跟踪历史记录,也免于干扰 dev 分支的迭代和发布。
  • 命名规范
    • feature/name:name 是功能名称。
    • feature/product_version:这也是团队常见的模式,当无法使用一个功能名称来描述时,可以使用后端版本号作为「功能」。
  • 合并时机
    • feature 分支迭代稳定,并通过测试后,合并到 dev 分支。合并到 dev 后,feature 分支的生命周期就结束了。后续 bug 修复和功能优化直接在 dev 开发。
    • 当多个 feature 分支需要合并对外发布临时版本时。合并到 preview 分支。(⚠️这种情况不应该合并到 dev 分支,因为 feature 分支可能还不稳定或未完成,比如为了联调某些功能)
  • 合并方式

preview 分支:临时的预览分支,preview 分支用于临时合并 feature 分支,这其中可能会修复某些 bug 或者冲突。可以选择性地将这些提交 cherry-pickfeature 分支。当预览结束后就可以销毁 preview 分支。

release 分支release 分支遵循 GitFlow 流程。

  • GitFlow 风格的 release:前端的稳定版本和 SQY(产品名)版本绑定。release 分支不一定存在。一般情况下,只会在前端版本稳定后,将其合并到 master,并创建 tag 标记。而只有需要为指定的正式版本修复 bug 时才会创建 release 分支。
  • 场景:需要为某个正式版本修复 bug(hotfix)时,从 master 的对应 tag 中 checkout 出一个 release 分支。
  • 命名规范:release/{product_version},外部人员只会关注产品版本。
  • 如何修复:
    • 如果对应 bug 可以在 dev 分支直接被修复,可以先提交到 dev 分支(或者已经修复了),然后再 cherry-pickrelease 分支。
    • 如果 bug 在新版本无法复现,比如新版本升级了依赖,那么在 release 分支直接修复即可。
  • release 分支树:
    masterv0.5.0@3.2v1.0.0@3.3v1.2.0@3.5v1.1.0@3.4release/3.3

    (浏览器可视区域的宽高)

# 总结

Git 提供了丰富的分支策略和工作流方式,通过学习业界 Git 工作流,可以发现每种工作流都设计的非常好,似乎都能运用到团队实践。但在引入 Git 工作流规范开发时要留意:Git 工作流仅仅是整个研发流程中的一环。上游项目管理/缺陷追踪系统虎视眈眈,下游 CD (Continuous Delivery) 嗷嗷待哺,还得考虑团队规模、产品形态、发版方式等等因素。因此,在团队中落地 Git 工作流规范并不是一件能轻松决定的事。

选择的策略不同,研发效率也不同,没有最好的工作流策略,只有最适合团队的工作流策略,Git工作流中常见的三种分支策略及其优缺点在上面已经列出,可以根据团队具体情况,选择合适的分支策略进行开发。

(完)