对比

Git Flow、GitHub Flow 与 trunk-based:该选哪种 Git 工作流

更新于: · Skillaria.Top

Git Flow 是一种以 main 和 develop 两个长期分支为核心,并辅以 feature、release、hotfix 临时分支的分支模型,适合有计划版本号的产品。对于部署频繁的 Web 服务,通常 GitHub Flow(从 main 拉出短期分支并通过 pull request 合并)或带功能开关的 trunk-based development 更简单。

什么是 Git 工作流,为什么选择它很重要

Git 工作流是团队就以下问题达成的约定:存在哪些分支,它们从哪里创建,合并到哪里,以及什么样的代码算是可以发布的。Git 本身并不强制这些规则,因此如果没有约定,每个人都按自己的方式来:一个月后仓库里有几十个分支,没人说得清哪个版本已经部署,而合并会变成耗时数小时的冲突处理。

最有名的模型是 Git Flow、GitHub Flow(Feature Branch Workflow 的一个变体)和 trunk-based development。它们并不是“旧与新”的关系,而是适用于不同条件的工具:你多久发布一次版本,同时维护几个版本,以及自动化测试的成熟程度如何。

Git Flow:带有 develop 与 release 分支的模型

Git Flow 由 Vincent Driessen 在 2010 年提出。该模型建立在两个长期分支之上:

  • main(旧项目中称为 master):只包含已发布的代码,这里的每个提交都对应一个发布版本,并打上标签;
  • develop:集成分支,已完成的功能在这里合并,等待下一个版本。

此外还有三种临时分支:

分支从哪里创建合并到哪里用途
feature/*developdevelop开发新功能
release/*developmain 和 develop稳定版本:修复 bug、设置版本号、补充文档
hotfix/*mainmain 和 develop紧急修复已发布版本中的问题

用原生 Git 实现 Git Flow

一个功能及一次发布的完整流程如下:

# 功能
git switch develop
git switch -c feature/cart-discounts
# ... 提交 ...
git switch develop
git merge --no-ff feature/cart-discounts
git branch -d feature/cart-discounts

# 发布
git switch -c release/1.4.0 develop
# ... 修改版本号并修复 bug ...
git switch main
git merge --no-ff release/1.4.0
git tag -a v1.4.0 -m "Release 1.4.0"
git switch develop
git merge --no-ff release/1.4.0
git branch -d release/1.4.0

参数 --no-ff 在该模型中很关键:即使可以快进,它也会创建一个合并提交,这样在历史中仍能看出哪些提交属于同一个功能或发布。

git-flow 扩展

为了不必手动输入这些命令序列,可以使用 git-flow 工具(现在通常安装其维护中的分支 git-flow-avh,在包管理器中常以这个名字出现)。它会添加一组子命令:

git flow init -d
git flow feature start cart-discounts
git flow feature finish cart-discounts
git flow release start 1.4.0
git flow release finish 1.4.0
git flow hotfix start 1.4.1
git flow hotfix finish 1.4.1

git flow init 会询问分支名和前缀(加上 -d 则使用默认值),而 finish 命令会完成所有必要的合并、打标签并删除临时分支。许多 Git 图形客户端也支持通过按钮操作 Git Flow。

Git Flow 的优缺点

优点:结构清晰,代码状态一目了然(main 是用户正在使用的版本,develop 是下一个版本);发布前有专门的稳定阶段;hotfix 的支持也很方便。它非常适合移动应用、套装软件、库和固件——这些产品的发布需要经过应用商店审核,或者安装在用户设备上。

缺点:分支和合并很多,长期存在的功能分支会与 develop 逐渐产生差异,而 release 和 hotfix 所需的双重合并很容易被遗忘。对于每天部署数次的 Web 服务来说,release 分支会变成繁琐的官僚流程。2020 年,该模型的作者在原文中补充了一条说明:对于持续交付的软件,应当考虑 GitHub Flow 这类更简单的方法。

GitHub Flow:短期分支与 pull request

GitHub Flow 是 Feature Branch Workflow 的轻量版本。规则只有几条:

  1. main 始终处于可工作状态,随时可以部署。
  2. 所有工作都在从 main 拉出的、名称清晰的分支中进行。
  3. 定期推送该分支,并创建 pull request(merge request)以便讨论。
  4. 经过评审并且 CI 通过后,将分支合并进 main。
  5. 合并进 main 的内容立即部署。
git switch main
git pull
git switch -c feature/new-payment
# ... 写清楚的提交说明 ...
git push -u origin feature/new-payment
# 创建 pull request,完成评审和 CI,然后合并到 main

优点:只有一个长期分支,规则最少,从想法到生产环境的路径最短,并且天然包含代码评审。缺点:没有专门的位置来维护多个版本,也没有发布前的稳定阶段;如果自动化测试不够好,“main 始终可用”只能是一种愿望。

与之相似、又增加了环境分支(例如 staging 和 production)的做法,有时被称为 GitLab Flow。当部署到生产环境不是即时完成,而是按计划进行或需要人工验收时,它会更合适。

Trunk-based development

在 trunk-based development 中,所有开发者都会频繁地(至少每天一次)把修改集成到同一个分支,即 trunk,也就是 main。分支即使存在,也只存活几个小时,而不是几周。尚未完成的功能会进入 main,但被功能开关(feature flag)隐藏:

if (featureFlags.isEnabled("new-checkout", user)) {
    renderNewCheckout();
} else {
    renderOldCheckout();
}

主要优势是几乎不会出现严重的冲突:修改很小,合并时所有人都还记得上下文。该模型真正实现了持续集成,非常适合拥有快速 CI 和高测试覆盖率的成熟团队。发布分支也允许存在,但它们是在发布前直接从 trunk 创建的,修复则先提交到 trunk,再通过 cherry-pick 移植过去。

缺点:需要纪律和基础设施——快速的测试、功能开关、监控。新手害怕每天都往 main 提交,而薄弱的 CI 会让 trunk 长期处于损坏状态。功能开关也必须及时清理,否则代码中会堆满无用的分支逻辑。

Git Flow、GitHub Flow 与 trunk-based 对比

标准Git FlowGitHub FlowTrunk-based
长期分支main 和 developmainmain(trunk)
功能分支寿命数天到数周数天数小时,或完全不用分支
发布频率按计划的版本随合并而发布持续发布
维护多个版本支持,通过标签和 hotfix不方便通过 release 分支
对 CI 与测试的要求中等较高非常高
大冲突风险高中等低
上手门槛需要学习规则最低需要文化氛围和功能开关
典型项目移动应用、库、套装软件Web 服务、开源项目、初创公司部署频繁的大型产品

如何为团队选择 Git 工作流

先回答几个问题:

  • 是否需要同时维护多个已发布的版本? 如果客户正在使用 2.x 和 3.x,并且两者都需要修复,那么选择 Git Flow,或者带有 release 分支的 trunk-based。
  • 你多久部署一次? 每季度一次,Git Flow 不会造成负担。每天都部署,则选择 GitHub Flow 或 trunk-based。
  • 你对测试有多大信心? 如果 CI 很慢、覆盖率很低,先从带强制评审的 GitHub Flow 开始,然后逐步缩短分支寿命。
  • 团队规模和经验如何? 对小团队和新手来说,GitHub Flow 最简单:一条规则“main 始终可用”,加上所有改动都通过 pull request 提交。

实用建议:不要因为“大家都这样做”就选择 Git Flow。从解决你当前问题的最简单流程开始,只有当出现具体的痛点时——例如需要稳定阶段或维护旧版本——才逐步增加复杂度。

适用于任何工作流的规则

  • 在服务器上保护 main:禁止直接推送,合并前要求评审并且 CI 通过。
  • 约定分支命名:feature/、fix/、hotfix/ 加上任务编号。
  • 保持分支短小:分支存在的时间越长,合并越痛苦。定期通过 merge 或 rebase 把 main 拉入自己的分支。
  • 选定 pull request 的合并方式(merge commit、squash 或 rebase),并始终统一使用。
  • 发布版本时使用带注释的标签,并遵循语义化版本(Semantic Versioning)。

各模型中的热修复:同一个 bug,三种流程

在生产环境中进行紧急修复时,三种方法的差异最明显。

在 Git Flow 中,从 main 创建 hotfix/1.4.1,修复后合并进 main 并打上 v1.4.1 标签,同时也必须合并进 develop(如果存在当前的 release 分支,则合并进它)。忘记第二次合并是经典错误:下一个版本中,bug 会“复活”。

git switch -c hotfix/1.4.1 main
git commit -am "Fix discount calculation"
git switch main && git merge --no-ff hotfix/1.4.1
git tag -a v1.4.1 -m "Hotfix 1.4.1"
git switch develop && git merge --no-ff hotfix/1.4.1

在 GitHub Flow 中,hotfix 只是一个从 main 拉出的普通短期分支,通过 pull request 提交,并在评审中享有优先权。合并后,它与其他更改一样通过自动部署上线,没有单独的流程。

在 trunk-based 中,修复首先提交到 trunk;如果生产环境运行的是 release 分支,则使用 git cherry-pick -x <哈希> 将提交移植过去。-x 会在说明中添加指向原始提交的引用,使历史中的关联清晰可见。如果问题出在新功能上,往往只需关闭它的功能开关,而无需修改任何代码。

面试中如何回答“你用过哪种 Git 工作流”

面试官关心的不是你是否记得名称,而是你是否理解其中的取舍。好的回答应该说出模型名称,描述其中分支和评审是如何运作的,并解释它为什么适合该项目。例如:“我们用的是 GitHub Flow:从 main 拉出分支,通过 pull request 进行强制评审和 CI 检查,合并后自动部署。我们没有采用 Git Flow,因为我们每周发布好几次,release 分支只会拖慢进度。”加分项是:说明如果流程随着团队扩大,你会做哪些调整。

总结

Git Flow 是一种严格的模型,包含 develop、release 和 hotfix 分支,适合有计划版本号、需要同时支持多个发布版本的产品。GitHub Flow 更简单,适用于大多数 Web 项目;trunk-based development 速度最快,但需要成熟的 CI 和功能开关。应根据发布频率和测试质量来选择 Git 工作流,而不是跟风。这三种方法及其团队实践中的常见错误,在 Git 课程中有实践讲解。

常见问题

Git Flow 过时了吗?

没有,但它的适用范围缩小了。对于持续部署的 Web 服务来说它显得过于复杂,而对于移动应用、库和套装软件,它仍然很方便。

发布频繁时还需要 develop 分支吗?

通常不需要:develop 会复制 main 的内容,并增加一次多余的合并。发布频繁时,只需 main 加上短期的功能分支即可。

Git Flow 中能使用 rebase 吗?

可以把 develop 通过 rebase 拉入自己尚未推送的个人功能分支,但在经典模型中,最终合并进 develop 和 main 时应使用 merge --no-ff,以保留历史结构。

功能开关比长期功能分支好在哪里?

代码立即集成进 main,不会与其他代码库产生分歧;而开启功能成为独立的决定,无需回滚代码就能撤销。

如何从 Git Flow 迁移到 GitHub Flow?

结束当前的发布,将 develop 合并进 main,把 CI 配置为从 main 部署,然后删除 develop。之后新分支一律从 main 创建。

通过视频学习本主题

课程“Git 从零基础到胜任开发者”结合实践详细讲解了这一主题。

前往课程