作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
丹尼尔·伊万诺夫的头像

丹尼尔·伊万诺夫

Daniel已经帮助创业公司将产品推向市场十多年了,他使用了最好的HTML/CSS方法, JS, Python, 和c#.

专业知识

以前在

三部曲
分享

无意中对Git造成损害是非常容易的. 然而,最好的方法是使用 Git 总是有争议的.

这是因为Git本身只详细说明了基本的分支操作,这就留下了它的使用模式.e.分支模型——这是用户意见的问题. Git分支模型承诺通过组织软件开发人员对其代码库进行更改时不可避免地出现的混乱来减轻痛苦.

像许多开发者一样, 您想要一些“刚刚工作”的东西,这样您就可以继续进行实际的软件开发. 所以你拿起了 Git流,这是Git用户经常推荐的分支模型. 也许您一开始就熟悉Git流的逻辑, 直到你在实践中遇到了一些障碍——与Git流的多个发布分支搏斗, 计算Git流暂存选项, 等. 或者也许Git流看起来不够好,不适合您采用它. 毕竟, 有无数的变量在起作用, 没有哪一种分支模式能适用于所有情况.

一个好消息! 经典Git流模型的一个变体, 增强的Git流 简化了Git流工作流中更常见的操作,同时仍然保留了其主要优点.

经典Git流的辉煌与悲哀

自从我发现Git流在开发一个演进的产品时有多么出色以来,我一直是Git流的强烈拥护者 显著的价值增量 换句话说, 释放).

一个重要的增值需要大量的时间来完成, 就像两周多的冲刺,通常用在 Scrum的发展. 如果开发团队已经部署到生产环境, 如果下一个版本的范围累积在生产代码所在的位置,可能会有麻烦.g.,在他们使用的Git仓库的主分支中.

虽然该产品仍处于初始开发阶段.e., 没有产品,也没有产品的真正用户——团队可以简单地将所有内容保留在主分支中. 事实上, 这是非常好的:这种策略可以在没有太多仪式的情况下实现最快的开发速度. But things change in a production environment; then, 真正的人开始依赖产品来保持稳定.

例如, 如果在生产中有一个严重的错误需要立即修复, 对于开发团队来说,必须回滚到目前为止在主分支中积累的所有工作,只是为了部署修复,这将是一个主要的灾难. 在没有适当测试的情况下部署代码——无论代码被认为是半生不熟还是开发良好——显然不是一种选择.

这就是分支模型的亮点所在,包括Git流. 任何复杂的分支模型都应该回答有关如何 隔离 下一个发布版本从系统目前使用的人,如何 更新 该版本与下一个发布,以及如何 介绍热补丁 当前版本的任何关键错误.

Git流程通过分离“主”(生产或“当前版本”分支)和“开发”(开发或“下一个版本”分支)来解决这些基本情况,并提供有关使用特性/发布/热修复分支的所有规则. 它有效地解决了基于发布的产品的开发工作流中的许多令人头痛的问题.

但即使是非常适合经典Git流模型的项目, 我遭受了它可能带来的典型问题:

  • Git流程很复杂, 有两根长寿的树枝, 三种类型的临时分支, 对分支机构之间的往来也有严格的规定. 这种复杂性使错误更容易发生,并增加了修复错误所需的工作量.
  • Git流发布分支和hotfix分支类型都需要“双重合并”——一次合并到main中, 然后是发展. 有时你可能会忘记这两件事. 您可以使用脚本或VCS GUI客户端插件使Git流分支更容易, 但是您必须首先为给定项目中涉及的每个开发人员的每台机器设置它们.
  • 在CI/CD工作流中, 一个发布通常会有两个最终的构建——一个来自发布分支本身的最新提交,另一个来自合并提交到main. 严格地说, 你应该用主服务器的那个, 但这两者通常是相同的, 造成潜在的混乱.

输入“增强的Git流”

我第一次使用增强的Git流是在一个新的闭源项目上. 我和另一个开发者一起工作, 我们一直通过直接提交到主分支来处理这个项目.

注意:直到产品的第一次公开发布, 为了开发工作流程的速度和简单性,将所有更改直接提交到主分支绝对是有意义的——即使您是Git流拥护者. 因为还没有生产, 不存在需要团队尽快修复的产品bug. 因此,在这个阶段,执行经典Git流所暗示的所有分支魔法是多余的.

当我们接近最初的发行版本时,我们达成了一致, 超过这个点, 我们不会再对直接提交到主分支感到舒服. 我们走得很快, 而且业务优先级并没有为建立坚如磐石的开发过程留下太多空间.e., 一个有足够的自动化测试来给我们信心,使我们的主要分支处于发布就绪状态.

这似乎是经典Git流模型的一个有效案例. 有独立的主分支和开发分支,在重要的价值增量之间有足够的时间, 有信心大多数手工QA会产生足够好的结果. 当我提倡Git流时, 我的同事也提出了类似的建议, 但有一些关键的区别.

一开始,我拒绝了. 在我看来,一些针对经典Git流的拟议“补丁”似乎有点过于革命性了. 我想他们可能会破坏主要思想,整个方法就会失败. 但经过进一步思考,我意识到这些调整实际上并没有破坏Git流. 与此同时, 它们通过解决上面提到的所有痛点,使它成为一个更好的Git分支模型.

在该项目中使用修改后的方法取得成功之后, 我在另一个闭源项目中使用了它,背后有一个小团队, 我是代码库的永久所有者,一两个外包开发人员不时提供帮助. 在这个项目上, 我们在6个月后投入生产, 从那时起, 我们使用CI和端到端测试已经一年多了, 大约每个月都会发布.

使用增强的Git流时的典型Git提交图. 该图显示了develop和main上的几个提交, 在他们共同承诺之前, 几个基于日期的标签.

我对这种新分支方法的总体体验是如此积极,以至于我想与我的开发人员同事分享它,以帮助他们克服经典Git流的缺点.

与经典Git流的相似之处:开发隔离

对于增强的Git流中的工作隔离,仍然有两个长期存在的分支:main和develop. (用户仍然拥有热修复和发布功能——强调的是“功能”,因为这些不再是树枝了. 我们将在差异部分详细介绍.)

对于经典的Git流特性分支,没有官方的命名方案. 你只需要从开发中分离出来,然后在功能准备好时再合并进来进行开发. 团队可以使用任何他们喜欢的命名约定,或者只是希望开发人员使用比“my-branch”更具描述性的名称.增强的Git流也是如此.

在开发分支中积累到某个截止点的所有特性将形成新版本.

南瓜合并

我强烈建议对功能分支使用压缩合并,以保持历史记录在大多数情况下是线性的. 没有它,提交图形(从GUI工具或 Git日志——图)当一个团队在处理几个功能分支时,就会显得很草率:

比较由压缩合并策略产生的提交图和由合并提交策略产生的提交图. 开始的Git提交图的主分支标记为“develop”,,而之前的提交有“feature-D”和“feature-C”的分支, 以及更早的提交,其中包含“功能b”和“功能a”的分支. 合并提交的结果看起来类似, 但是随着每个特性分支在它绑定到它的地方引起一个新的开发提交. 压缩合并结果发展为简单的直线提交,没有其他分支.

但即使你对这种场景的视觉效果满意,也有另一个原因. 在不压缩的情况下,提交历史视图——其中都是普通的 git日志 (没有 ——图)和github -即使是最简单的合并场景也会讲述相当不连贯的故事:

比较由压缩合并策略和合并提交策略产生的提交历史视图. 原始回购状态以时间轴形式给出, 显示了提交到两个特性分支的时间顺序,这些分支来自于同一个提交“x”, 在分支之间交替提交, 也就是1a阶, 2a, 1b, 和2 b. 合并提交结果以两种形式显示. 在合并第二个分支所产生的提交历史中, 合并第一个分支, 然后删除两个分支, 这个故事是按时间顺序排列的, 但没有凝聚力, 并为第一个分支包含一个额外的合并提交. 在删除分支之前按顺序合并分支所产生的历史记录中, 提交是有序的, "x, 2a, 1a, 2b, 1b,,然后是第二个分支的合并提交, 这甚至不是按时间顺序排列的. 来自squash合并的提交历史仅对每个特性分支进行一次提交, 由提交者讲述的每个分支的故事.

使用压缩合并需要注意的是,原始的特性分支历史会丢失. 但这个警告甚至不适用,如果你使用GitHub,例如 揭露完整的原始历史 通过pull请求合并的特性分支, 即使在特性分支本身被删除之后.

与经典Git流的区别:发布和修复

让我们回顾一下发布周期,因为(希望)这是你将要做的主要事情. 当我们想要发布在开发过程中积累的内容时, 它是main的超集. 在此之后,经典Git流和增强Git流之间的最大区别就开始了.

Git提交图,当在增强的Git流下执行正常发布时,它们会发生变化. 最初的图表主要偏离了提示后面的多次提交和一次提交. 标记后,main和vYYYY-MM-DD相等. 在删除本地main后,在开发、强制推送、部署、测试等的末端创建它., main与develop一起显示,在main原来所在的位置留下vYYYY-MM-DD. 在部署/测试周期之后, 在main上进行阶段性修复(最终合并到开发中), 与此同时, 不相关的变化, 最后的图形已发展,主要分岔, 每个都有几个提交, 从他们在之前的图表中平分的地方开始.

在增强的Git流中发布

使用增强的Git流发布的每一步都不同于经典的Git流流程:

  1. 发布是基于main的,而不是基于develop的. 标记主分支的当前尖端 有意义的东西. 我采用了基于ISO 8601格式的当前日期的标签,前缀为“v”-e.g., v2020-09-09.
    • 如果一天中碰巧有多个版本,例如, hotfixes—该格式可以根据需要添加一个连续的数字或字母.
    • 请注意,标签通常不对应于发布日期. 它们仅仅是为了强制Git在下一次发布过程开始时保留一个主分支的参考.
  2. 推标签 使用 git push origin .
  3. 在那之后,有点惊喜: 删除本地主分支. 别担心,我们很快就会恢复的.
    • 所有提交到main的操作仍然是安全的——我们在前一步中通过标记main来保护它们不被垃圾收集. 这些提交中的每一个—甚至是热修复,我们将很快介绍—也是开发的一部分.
    • Just make sure only one person on a team is doing this for any given release; that’s the so-called “release manager” role. 发布经理通常是最有经验和/或最资深的团队成员, 但是一个团队最好避免任何特定的团队成员永久地承担这个角色. 更有意义的是在团队中传播知识以增加恶名 公共要素.
  4. 在开发分支的提示提交处创建一个新的本地主分支.
  5. 推动这种新的结构使用 Git push -force因为远程回购不会轻易接受如此“剧烈的变化”. 同样,在这种情况下,这并不像看起来那么不安全,因为:
    • 我们只是将主分支指针从一个提交移动到另一个提交.
    • 一次只有一个特定的团队成员在做这个更改.
    • 每天的开发工作都发生在开发分支上, 所以你不会因为走这条路而干扰任何人的工作.
      • (这与“没有开发分支的Git流”方法不同, 在哪个主要分支上直接进行开发.)
  6. 你有你的新版本! 将其部署到暂存环境 然后测试一下. (我们将在下面讨论方便的CI/CD模式.)任何修复都会直接进入主分支, 因此它会从发展分支中分离出来.
    • 同时, 您可以在开发分支中开始处理一个新版本, 与经典Git流相同的优势.
    • 不幸的是,此时需要对当前生产中的产品(而不是即将发布的登台版本)进行热修复, 关于此场景的更多细节,请参阅下面的“在活动发布期间处理热修复…”.
  7. 当你的新版本被认为足够稳定时, 将最终版本部署到生产环境 并做单一的挤压合并的主要开发 收集所有的修复.

修复了增强的Git流

热修复的情况有两种. 如果你在做热修复 当没有主动释放时—i.e., 团队正在开发分支中准备一个新版本——这很简单:提交到主版本, 将您的更改部署并在登台阶段中进行测试,直到它们准备就绪, 然后部署到生产环境.

作为最后一步, 选择从主版本提交到开发版本,以确保下一个版本将包含所有修复. 以防您最终提交了几个热修复, 通过创建和应用一个补丁,而不是多次挑选,您可以节省精力——特别是如果您的IDE或其他Git工具可以简化这一过程的话. 试图在初始版本发布后压制merge main进行开发,很可能会以与开发分支中独立进度的冲突而告终, 所以我不建议这么做.

处理修补程序 在活动释放期间—i.e., 当您刚刚强制推入main并仍在准备新版本时,它是增强的Git流中最弱的部分. 这取决于你的发行周期的长短和你必须解决的问题的严重性, 始终致力于在新版本中包含修复程序——这是最简单的方法, 而且完全不会扰乱整个工作流程.

如果这是不允许的,你必须快速引入修复, 如果你等不及新版本准备好,那就准备好一个有点复杂的Git过程吧:

  1. 创建一个分支——我们称之为“新发布”,但是您的团队可以在这里采用任何命名约定—在与当前提示main相同的提交中. 新书的推.
  2. 在提交之前为当前活动版本创建的标记时,删除并重新创建本地主分支. 力推主.
  3. 将必要的修复引入主环境、部署到登台环境并进行测试. 一旦准备好了,就部署到生产环境.
  4. 将更改从当前的主版本传播到新版本,要么通过筛选,要么通过补丁.
  5. 在那之后, 重做释放程序:标记当前主的尖端并推送标记, 在新发布分支的顶端删除并重新创建本地main, 力推主.
    1. 您可能不需要前一个标记,因此可以删除它.
    2. 新发布的分支现在是冗余的,所以您也可以删除它.
  6. 现在您应该可以像往常一样使用新版本了. 最后,通过筛选或发布补丁,将紧急修复程序从主要系统传播到开发系统.

Git提交图,当在增强的Git流下的活动发布期间执行热修复时,它们会发生变化. 开始的图已经发展成为最长的提交行, main通过一个提交将两个提交分散, 在此之前还有三次提交, 分支因一次提交而分叉, 标记v2020-09-18. 在上面的前两个步骤之后, 图中原来是main,现在是new-release, 而且main甚至没有v2020-09-18. 然后执行其余步骤, 就得到了最终的图形, main在哪里比new-release提前提交, v2020-09-20是在v2020-09-18之前提交的.

只要计划得当, 代码质量足够高, 以及健康的发展和QA文化, 您的团队不太可能使用这种方法. 为增强的Git流开发和测试这样的灾难计划是明智的, 只是以防万一,但我从来没有在实践中使用过.

CI/CD安装在增强的Git流之上

并不是每个项目都需要一个专用的开发环境. 在每台开发人员机器上设置一个复杂的本地开发环境可能很容易.

但是,专门的发展环境可以促进更健康的发展文化. 运行测试, 测量测试覆盖率, 并且在开发分支上计算复杂性度量,通常可以在错误进入阶段之前很好地捕获它们,从而降低错误的成本.

我发现一些CI/CD模式在与增强的Git流结合使用时特别有用:

  • 如果您需要一个开发环境, 设置CI进行构建, 测试, 并在每次提交到开发分支时部署到它. 如果您有并且在您的情况下有意义,那么也可以在这里进行E2E测试.
  • 在每次向主分支提交时,设置CI以构建、测试和部署到暂存环境. 在这一点上,端到端测试也是非常有益的.
    • 在这两个地方使用端到端测试似乎是多余的, 但请记住,热修复不会在开发中发生. 在提交到main时触发E2E将测试热修复 每天都在变化,直到它们消失, 但是触发提交开发也会更早地捕获bug.
  • 以一种允许团队根据手动请求将构建从主环境部署到生产环境的方式配置CI.

The recommended setup of CI/CD with 增强的Git流 when there's a development environment ("dev") in addition to staging ("stage") 和 production ("prod"). 在开发分支上的所有提交都会导致构建部署到开发. 同样,所有提交到main都会导致构建部署到阶段. 一个特定提交的手工请求会导致构建部署到生产环境中.

这种模式相对简单, 然而,提供强大的机制来支持日常的开发操作.

增强的Git流模型:改进和可能的限制

增强的Git流并不适合所有人. 它确实利用了有争议的武力推动主要分支的策略,因此纯粹主义者可能会怨恨它. 从实际的角度来看,这并没有什么错.

如前所述,在发布期间修补程序更具挑战性,但仍然是可能的. 适当关注QA,测试覆盖率等. 这种情况不应该经常发生, 所以在我看来, 与经典Git流相比,增强Git流的整体优势是一个有效的权衡. 我非常有兴趣了解增强的Git流程在大型团队和更复杂的项目中的表现, 在哪些地方可能更频繁地出现热修复程序.

我对增强的Git流模型的积极体验也主要围绕着闭源商业项目. 对于拉取请求通常基于源代码树的旧版本派生的开源项目来说,这可能是有问题的. 解决这个问题没有技术上的障碍——只是可能需要比预期更多的努力. 我欢迎在开源领域有丰富经验的读者就增强的Git流在这种情况下的适用性给出反馈.

特别感谢Toptal同事 安东尼·范教授 因为他在开发增强Git流背后的思想方面发挥了关键作用.


微软金牌合作伙伴徽章.

作为一个 微软金牌合作伙伴, Toptal是您的微软专家精英网络. 与您需要的专家一起建立高绩效团队-随时随地都可以!

了解基本知识

  • 什么是Git流?

    Git流是Git版本控制系统(VCS)的分支模型。. 分支模型建立在基本的Git操作之上, 规定使用模式,以实现健壮的版本管理. Git流是Vincent Driessen在2010年的一篇博客文章中公开宣布的,从那以后一直很受欢迎.

  • 什么是Git流分支策略?

    Git流分支策略在开发以显著的价值增量演进的产品时表现出色. 它通过分离“主”(生产或“当前版本”分支)和“开发”(开发或“下一个版本”分支)并提供有关使用helper分支的所有必要规则来实现这一点.

  • 如何使用Git流?

    按照指定的模式使用Git流. 这可以让您的团队获得关于如何将下一个发行版与当前生产中的系统版本隔离开来的问题的答案, 如何在下一个版本中更新该版本, 以及如何在当前版本中引入任何关键错误的热修复程序.

  • 我应该使用Git流吗?

    是否应该使用Git流取决于您的特定项目. 要回答的主要问题是, “整个开发工作流程是否有足够的自动质量保证来保持主要分支处于发布就绪状态??如果答案是否定的,那么这样的项目可能会受益于Git流.

  • 最好的Git工作流是什么?

    没有最好的Git工作流,因为答案取决于特定的项目环境. 一些项目将受益于Git流或其变体, 而其他人则会更好地使用基于主干的开发方法.

聘请Toptal这方面的专家.
现在雇佣
丹尼尔·伊万诺夫的头像
丹尼尔·伊万诺夫

位于 莫斯科,俄罗斯

成员自 2016年7月6日

作者简介

Daniel已经帮助创业公司将产品推向市场十多年了,他使用了最好的HTML/CSS方法, JS, Python, 和c#.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

专业知识

以前在

三部曲

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.