增强的 Git 流程解释
已发表: 2022-03-11使用 Git 无意中造成损坏太容易了。 然而,使用 Git 的最佳方式总是有争议的。
这是因为 Git 本身只详细说明了基本的分支操作,而它的使用模式(即分支模型)则取决于用户的意见。 Git 分支模型承诺通过组织软件开发人员对其代码库进行更改时不可避免地出现的混乱来减轻痛苦。
像许多开发人员一样,您想要一些“可以正常工作”的东西,这样您就可以继续进行实际的软件开发。 所以你选择了Git flow ,这是一个经常推荐给 Git 用户的分支模型。 也许你一开始就接受了 Git 流程的逻辑,直到你在实践中遇到了一些障碍。 或者,Git 流程似乎不太适合您采用它。 毕竟,有无数的变量在起作用,没有一个单一的分支模型能在所有情况下都能很好地工作。
好消息! 作为经典 Git 流模型的一种变体,增强型 Git 流简化了 Git 流工作流中更常见的操作,同时仍保留了主要优势。
经典 Git 流模型的辉煌与痛苦
自从我发现 Git 流在开发以显着价值增量发展的产品(换句话说,发布)时如何表现出色以来,我一直是 Git 流的坚定拥护者。
显着的价值增量需要大量时间才能完成,例如基于 Scrum 的开发中通常使用的两周以上的冲刺。 如果开发团队已经部署到生产环境,如果下一个版本的范围累积在生产代码所在的同一位置(例如,在他们使用的 Git 存储库的主分支中),则可能会出现问题。
虽然产品仍处于初始开发阶段——即,没有生产,也没有产品的真正用户——但团队可以简单地将所有内容保留在主分支中。 事实上,这还不错:这种策略允许以最快的速度发展,而无需太多仪式。 但是在生产环境中情况会发生变化; 然后,真正的人开始依赖产品来稳定。
例如,如果生产中存在需要立即修复的关键错误,那么开发团队不得不回滚迄今为止在主分支中累积的所有工作以部署修复程序,这将是一场重大灾难。 并且在没有经过适当测试的情况下部署代码——无论代码被认为是半生不熟还是开发良好——显然不是一种选择。
这就是分支模型大放异彩的地方,包括 Git 流。 任何复杂的分支模型都应该回答有关如何将下一个版本与人们当前使用的系统版本隔离、如何使用下一个版本更新该版本以及如何将任何关键错误的修补程序引入当前版本的问题。
Git 流程通过分离“main”(生产或“当前版本”分支)和“develop”(开发或“下一个版本”分支)并提供有关使用特性/发布/修补程序分支的所有规则来解决这些基本场景. 它有效地解决了基于发布产品的开发工作流程中的许多令人头疼的问题。
但即使项目非常适合经典的 Git 流模型,我也遇到了它可能带来的典型问题:
- Git 流程很复杂,有两个长期存在的分支,三种类型的临时分支,以及对分支如何相互处理的严格规定。 这种复杂性使错误更有可能发生,并增加了修复它们所需的工作量。
- Release 和 hotfix 分支需要“双重合并”——一次进入 main,然后进入 develop。 有时你可能会忘记两者都做。 您可以使用脚本或 VCS GUI 客户端插件使 Git 流分支更容易,但您必须首先为参与给定项目的每个开发人员的每台机器设置它们。
- 在 CI/CD 工作流程中,您通常会为一个发布最终构建两个最终版本——一个来自发布分支本身的最新提交,另一个来自合并提交到 main。 严格来说,您应该使用主要的一个,但两者通常是相同的,从而产生混淆的可能性。
输入“增强的 Git 流程”
我第一次使用增强型 Git 流是在一个新建的闭源项目上。 我正在与另一位开发人员一起工作,我们一直在通过直接提交到主分支来处理该项目。
注意:在产品第一次公开发布之前,为了开发工作流程的速度和简单性,将所有更改直接提交到主分支绝对是有意义的——即使你是 Git 流程倡导者。 由于还没有生产,因此团队不可能尽快修复生产错误。 因此,在这个阶段执行经典 Git 流程所暗示的所有分支魔法是过度的。
然后我们接近了初始版本,我们同意,超过那个点,我们将不再对直接提交到主分支感到满意。 我们的行动非常迅速,业务优先事项并没有留下太多空间来建立一个坚如磐石的开发流程——即,一个具有足够自动化测试的流程,让我们确信我们的主分支处于发布就绪状态。
这似乎是经典 Git 流模型的有效案例。 有了独立的主分支和开发分支,并且在显着的价值增量之间有足够的时间,人们相信大多数手动 QA 会产生足够好的结果。 当我提倡 Git 流程时,我的同事提出了类似的建议,但有一些关键的区别。
起初,我推后。 在我看来,一些针对经典 Git 流程的提议“补丁”有点太具有革命性了。 我认为他们可能会破坏主要思想,整个方法都会失败。 但经过进一步思考,我意识到这些调整实际上并没有破坏 Git 流程。 同时,他们通过解决上述所有痛点使其成为更好的 Git 分支模型。
在该项目中使用修改后的方法取得成功后,我在另一个封闭源代码项目中使用了它,背后有一个小团队,我是永久代码库所有者和一两个外包开发人员不时提供帮助。 在这个项目中,我们在六个月内投入生产,从那时起,我们已经使用 CI 和 E2E 测试一年多了,每个月左右发布一次。
我对这种新分支方法的总体体验非常积极,因此我想与我的开发人员分享它,以帮助他们克服经典 Git 流程的缺点。
与经典 Git 流程的相似之处:开发隔离
对于增强型 Git 流程中的工作隔离,仍然有两个长期存在的分支,主要和开发。 (用户仍然拥有修补程序和发布功能——强调“功能”,因为这些不再是分支。我们将在差异部分详细介绍。)
经典的 Git 流功能分支没有正式的命名方案。 当功能准备好时,您只需从开发分支出来并重新合并以进行开发。 团队可以使用他们喜欢的任何命名约定,或者只是希望开发人员使用比“my-branch”更具描述性的名称。 增强的 Git 流程也是如此。
在开发分支中累积到某个截止点的所有功能都将塑造新版本。
壁球合并
我强烈建议对特性分支使用 squash 合并,以在大多数情况下保持历史的良好和线性。 没有它,提交图(来自 GUI 工具或git log --graph
)在团队同时处理少数功能分支时开始显得草率:
但是,即使您对这种情况下的视觉效果感到满意,还有另一个理由来挤压。 没有压缩,提交历史视图——其中包括普通的git log
(没有--graph
)和 GitHub——即使是最简单的合并场景也会讲述相当不连贯的故事:
使用 squash 合并的警告是原始特征分支历史会丢失。 但是,如果您使用的是 GitHub,则此警告甚至不适用,例如,它通过压缩合并的拉取请求公开功能分支的完整原始历史记录,即使在功能分支本身被删除之后也是如此。
与经典 Git 流程的区别:版本和修补程序
让我们回顾一下发布周期,因为(希望)这是您将要做的主要事情。 当我们想要发布在开发中积累的内容时,它严格来说是 main 的超集。 在那之后,经典的和增强的 Git 流之间最大的区别就开始了。

增强版 Git 流程中的版本
使用增强的 Git 流程进行发布的每个步骤都不同于经典的 Git 流程:
- 发布是基于主要的,而不是开发的。 用有意义的东西标记主分支的当前尖端。 我采用了基于 ISO 8601 格式的当前日期的标签,前缀为“v”,例如 v2020-09-09 。
- 如果一天内碰巧有多个版本(例如修补程序),则该格式可能会根据需要在其上附加一个序列号或字母。
- 请注意,标签通常与发布日期不对应。 它们只是为了强制 Git 保留对主分支在下一个发布过程开始时的外观的引用。
- 使用
git push origin <the new tag name>
。 - 之后,有点意外:删除你的本地主分支。 别担心,因为我们很快就会恢复它。
- 所有对 main 的提交仍然是安全的——我们通过在上一步标记 main 来保护它们免于垃圾收集。 这些提交中的每一个——甚至是我们将很快介绍的修补程序——也是开发的一部分。
- 只需确保团队中只有一个人在为任何给定版本执行此操作即可; 这就是所谓的“发布经理”角色。 发布经理通常是最有经验和/或最资深的团队成员,但明智的做法是避免任何特定的团队成员永久担任此角色。 在团队中传播知识以增加臭名昭著的总线因素更有意义。
- 在你的开发分支的提示提交处创建一个新的本地主分支。
- 使用
git push --force
推送这个新结构,因为远程仓库不会轻易接受这样的“剧烈变化”。 同样,这并不像在这种情况下看起来那样不安全,因为:- 我们只是将主分支指针从一个提交移动到另一个提交。
- 一次只有一个特定的团队成员进行此更改。
- 日常开发工作都发生在开发分支上,因此您不会通过以这种方式移动 main 来破坏任何人的工作。
- 你有你的新版本! 将其部署到登台环境并进行测试。 (我们将在下面讨论方便的 CI/CD 模式。)任何修复都直接进入主分支,因此它将开始与开发分支分道扬镳。
- 同时,您可以开始在开发分支中开发新版本,这与经典 Git 流程中的优势相同。
- 如果不幸的是,此时需要为当前生产中的内容(而不是即将发布的暂存版本)提供修补程序,请参阅下面的“在活动版本期间处理修补程序......”中有关此方案的更多详细信息。
- 当您的新版本被认为足够稳定时,将最终版本部署到生产环境并进行一次 main 的 squash 合并以开发以获取所有修复。
增强型 Git 流程中的修补程序
修补程序案例是双重的。 如果您在没有活动版本的情况下进行修补程序(即,团队正在开发分支中准备新版本),这很容易:提交到 main,在暂存中部署和测试您的更改,直到它们准备好,然后部署到生产。
作为最后一步,从 main 中挑选您的提交进行开发,以确保下一个版本将包含所有修复。 如果您最终提交了多个修补程序,您可以通过创建和应用补丁而不是多次挑选来节省精力——特别是如果你的 IDE 或其他 Git 工具可以促进它。 在初始版本之后尝试将merge main 压缩到develop 很可能最终与develop 分支中的独立进度发生冲突,因此我不建议这样做。
在活动版本期间处理修补程序(即,当您只是强制推送主版本并且仍在准备新版本时)是增强型 Git 流程中最薄弱的部分。 根据您的发布周期的长度和您必须解决的问题的严重性,始终致力于在新版本本身中包含修复——这是最简单的方法,并且根本不会破坏整个工作流程。
万一这是不行的——你必须快速引入一个修复,并且你不能等待新版本准备好——然后准备一个有点复杂的 Git 过程:
- 创建一个分支——我们称之为“新发布”,但你的团队可以在这里采用任何命名约定——在与 main 的当前提示相同的提交处。 推送新版本。
- 在您之前为当前活动版本创建的标记的提交处删除并重新创建本地主分支。 强制推主。
- 对 main 引入必要的修复,部署到暂存环境并进行测试。 只要准备好了,就部署到生产环境中。
- 通过挑选或补丁将当前主版本的更改传播到新版本。
- 之后,重做发布过程:标记当前主干的尖端并推送标记,删除并在新发布分支的尖端重新创建本地主干,并强制推送主干。
- 您可能不需要以前的标签,因此您可以将其删除。
- new-release 分支现在是多余的,因此您也可以将其删除。
- 您现在应该可以像往常一样使用新版本了。 通过从主要传播紧急修补程序以通过樱桃采摘或补丁进行开发来完成。
有了适当的计划、足够高的代码质量以及健康的开发和 QA 文化,您的团队不太可能不得不使用这种方法。 为了以防万一,为增强 Git 流程开发和测试这样的灾难计划是明智的——但我从来不需要在实践中使用它。
基于增强型 Git 流程的 CI/CD 设置
并非每个项目都需要专用的开发环境。 在每台开发人员机器上设置复杂的本地开发环境可能很容易。
但是,专门的开发环境有助于建立更健康的开发文化。 在开发分支上运行测试、测量测试覆盖率和计算复杂性指标通常可以通过在错误最终进入暂存之前及时发现来降低错误成本。
我发现一些 CI/CD 模式在与增强的 Git 流程结合使用时特别有用:
- 如果您需要开发环境,请设置 CI 以在每次提交到开发分支时对其进行构建、测试和部署。 如果你有它并且它对你的情况有意义的话,也适合这里的 E2E 测试。
- 设置 CI 以在每次提交到主分支时构建、测试和部署到暂存环境。 在这一点上,E2E 测试也非常有益。
- 在这两个地方使用 E2E 测试似乎是多余的,但请记住,在开发中不会发生修补程序。 在提交到 main 时触发 E2E 将在发布之前测试修补程序和日常更改,但也触发提交到开发将更早地捕获错误。
- 以允许您的团队根据手动请求将构建从主环境部署到生产环境的方式配置 CI。
这种模式相对简单,但提供了强大的机制来支持日常开发操作。
增强的 Git 流模型:改进和可能的限制
增强的 Git 流程并不适合所有人。 它确实利用了有争议的武力推动主要分支的策略,因此纯粹主义者可能会对此感到不满。 不过,从实际的角度来看,这并没有什么问题。
如前所述,修补程序在发布期间更具挑战性,但仍有可能。 适当注意不应经常发生的 QA、测试覆盖率等,因此从我的角度来看,与经典 Git 流相比,增强 Git 流的整体优势是一个有效的权衡。 我很想知道在大型团队和更复杂的项目中如何增强 Git 流票价,其中修补程序可能更频繁地出现。
我对增强型 Git 流模型的积极体验也主要围绕闭源商业项目展开。 对于拉取请求通常基于源代码树的旧版本派生的开源项目来说,这可能是有问题的。 没有技术障碍可以解决这个问题——它可能需要比预期更多的努力。 我欢迎在开源领域有丰富经验的读者提供关于增强 Git 流在这种情况下的适用性的反馈。
特别感谢 Toptal 的同事 Antoine Pham 在开发增强 Git 流程背后的想法方面发挥的关键作用。
进一步阅读 Toptal 工程博客:
- 基于主干的开发与 Git 流程
- 专业的 Git 工作流程:一个好的 Git 指南
作为 Microsoft 金牌合作伙伴,Toptal 是您的 Microsoft 专家精英网络。 与您需要的专家一起建立高绩效团队 - 在您需要的任何时间和地点!