使用 Git 的最佳方式一直存在争议。那是因为 Git 本身只详细说明了基本的分支操作,这使得它的使用模式: 即分支模型——常常成为用户有意见的地方。虽然Git 分支模型能够帮助开发者减少其在更改代码库时带来的冲突。
Git Flow,是一种经常推荐给 Git 用户的分支模型。也许一开始你对 Git Flow 的逻辑很感兴趣,但是直到你在实践中遇到了一些障碍。毕竟,有无数的变量在起作用,没有一个单一的分支模型能在所有情况下都能很好地工作。
但是,值的高兴的是作为经典 Git Flow 模型的一个变体,增强的 Git Flow模型版本简化了 Git Flow 中更常见的操作,并保留了主要优点。
自从我发现 Git Flow 在开发具有显著价值产品时是多么出色以来,我一直是 Git Flow 的强烈倡导者。
一个显著的价值增长需要大量的时间来证明,就像在基于 scrum 的开发中通常使用的两周以上的 sprint。
当产品仍处于初始开发阶段时,即,没有产品,也没有产品的真正用户时,团队可以把所有的东西都放在主分支中。事实上,这是非常好的:这种策略允许最快的开发速度。但是在生产环境中情况会发生变化。
例如,如果在生产中有一个需要立即修复的关键 issue,那么对于开发团队来说,为了部署修复的代码而不得不回滚现有已经完成的代码。在没有进行适当测试的情况下部署代码,不管代码被认为是不成熟的还是开发良好的,显然是不可取的。
这就是分支模型的特点,包括 Git Flow。任何复杂的分支模型都应该回答以下问题:
Git Flow 通过分离“main”分支(生产或“当前版本”分支)和“dev”(开发或“下一个版本”分支)分支,并提供有关使用 feature/release/hotfix
分支的所有规则,来解决这些基本的场景。它有效地解决了 release 的开发工作流中的许多令人头疼的问题。
但是,即使有非常适合经典 Git Flow 模型的项目,我也遇到了它可能带来的典型问题:
main
分支,然后进入devlop
分支。有时你会忘记兼顾两者。您可以使用脚本或 VCS GUI 客户端插件使 Git Flow 分支更容易,但必须首先为给定项目中涉及的每个开发人员的每台机器设置它们。release
分支本身的最新提交,另一个来自合并提交到 main
分支的提交。严格地说,应该使用来自 main
的。但这两个通常是相同的,可能会造成混淆。在产品的第一次公开发布之前,为了开发工作流的速度和简单性,直接将所有更改提交到 main
分支是绝对有意义的。因为还没有产品,所以团队不可能需要尽快修复产品 bug。因此,在这个阶段执行传统 Git Flow 模型所建议的分支管理是多余的。
然后我们接近了最初的 release
版本,在那之后,我们将不再愿意直接提交到 main
分支。我们进展得很快,业务优先级没有给建立一个坚如磐石的开发过程留下太多空间。它具有足够的自动化测试,使我们有信心保持我们的主要分支处于准备发布的状态。
这似乎是经典 Git Flow 模型的一个有效案例。有了独立的 main
分支和 devlop
分支。
但是,后来我做了一些变化。起初在我看来,对 Git Flow 做一些“补丁”有点过于冒进了。我认为这样可能会打破 Git Flow 主要的思想,从而达不到目标。但经过进一步的思考,我意识到这些调整实际上并没有破坏 Git Flow。同时,他们解决了上面提到的所有问题,使之成为一个更好的 Git 分支模型。
下面,我将和大家分享这套方法,帮助开发者克服传统 Git Flow 的缺点。
对于增强的 Git Flow 中的工作隔离,仍然有两个长期存在的分支:main 和 development。
经典的 Git Flow feature
分支没有正式的命名方案。当 feature
准备好时,只需要从 devlop
分支出来并合并回去进行开发。团队可以使用任何他们喜欢的命名约定,或者只是希望开发人员使用比“my-branch”更具有描述性的名称。增强的 Git Flow 也是如此。
我强烈建议在 feature 分支中使用压缩合并,以便在大多数时候保持良好的线性历史记录。如果没有它,当团队同时处理少量的 feature 分支时,git graph(git log -graph
)日志会显得比较草率:
但即使你对这种情况下的视觉效果没有意见。没有压缩,提交的历史视图-其中包括普通的 git 日志(没有-graph)和 一些相当不连贯的 log,即使是最简单的合并场景:
使用压缩合并需要知道的是原有的 feature 分支提交历史会丢失。
让我们来看看发布周期,因为这是你要做的主要事情。当我们想要发布开发中积累的内容时,它严格来说是 main 的超集。在此之后,经典和增强的 Git 流之间的最大区别就开始了。
在 release 方面,使用增强的 Git Flow 的每一步都与经典的 Git Flow 不同:
main
分支的,而不是基于 devlop
分支的。你可以用一些有意义的东西标记 main
分支的当前的 tip。我采用了 ISO 8601 格式中基于当前日期的标签,前缀是“v”-例如: v2020-09-09
。main
分支的参考。git Push origin <the new tag name>
来 push tag。main
分支。git Push -force
来推动这个新结构,因为远程仓库不会这么容易地接受这样一个“剧烈的变化”。同样,在这种情况下,这并不像看起来那么不安全,因为:Hotfixes 的情况有两方面。如果你正在做一个热修复时,例如,团队正在开发分支中准备一个新版本,当它们准备好时,需要部署到生产环境。
作为最后一步,从 main 中选择提交来开发,以确保下一个版本将包含所有修复。如果您需要提交几个热修复程序,您可以通过创建并应用一个补丁而不是多次选择补丁来节省精力—特别是如果您的 IDE 或其他 Git 工具可以提供便利的话。试图在初始版本发布后将合并主分支压缩到开发分支,很可能会与开发分支的独立进程产生冲突,所以我不建议这样做。
在 relase 期间处理修补程序。例如,当您只是强行推入 main 分支并仍在准备新版本时,是增强版 Git Flow 中最薄弱的地方。根据发布周期的长度和需要解决的问题的严重程度,始终以在新发布版本中包含修复为目标——这是最简单的方法,而且根本不会打乱整个工作流。
如果这是一个不可能的,你必须快速引入一个修复,你不能等待新版本的发布-然后准备一个有点复杂的 Git 过程:
cherry pick
或补丁完成。并不是每个项目都需要专用的开发环境。在每台开发人员机器上设置复杂的本地开发环境可能很容易。
在开发分支上运行测试、测量测试覆盖率和计算复杂性度量,通过在错误进入执行阶段之前很好地捕获它们,通常可以降低错误的成本。
我发现一些 CI/CD 模式在与增强的 Git Flow 结合使用时特别有用:
在两个地方都使用端到端测试似乎是多余的,但是请记住,修补程序不会在开发过程中发生。在提交到 main 时触发 E2E,将测试修复程序和每天的更改,但在提交到开发时触发将更早地捕获bug。
这些模式相对简单,但提供了支持日常开发操作的强大机制。
增强的 Git Flow 并不适合所有人。它确实利用了有争议的策略,用武力推动主要分支,所以纯粹主义者可能会怨恨它。然而,从实际的角度来看,这并没有什么问题。
如前所述,修补程序在发布期间更具挑战性,但仍然是可能的。在适当关注 QA、测试覆盖率等方面,这些不应该经常发生,所以在我看来,与传统的 Git 流相比,增强的 Git 流的整体好处是一个有效的权衡。我很想知道增强的 Git 流在更大的团队和更复杂的项目中如何发挥作用,在这些项目中修补程序可能会更频繁地出现。
我对增强的 Git 流模型的积极体验也主要围绕着封闭源代码的商业项目。这对于一个开源项目来说可能是有问题的,因为拉请求通常是基于源代码树的旧版本派生的。解决这个问题没有技术障碍,只是可能需要比预期更多的努力。我非常欢迎那些在开源领域拥有丰富经验的读者的反馈,因为你们了解增强的 Git 流在这种情况下的适用性。