使用 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 流在这种情况下的适用性。