每当我与某人谈论Git和版本控制时,总会出现一个问题:
这个问题没有一个答案。虽然我们已经将公司中的每个人都转移到了一个工程系统中,并在Visual Studio Team Services中托管的Git上实现了标准化,但我们还没有做到的是将每个人都转移到相同的分支和开发模型中。
有些团队 - ,比如Windows - ,保留了一个分支策略,这与他们多年来一直使用的策略类似。这种方法是无可争辩的,他们有很多工具来支持它,而且开发人员对如何在分支机构之间移动有一定的机构知识。把这么大的一个团队搬到吉特是很有挑战性的, - 你只能同时煮这么多大洋。
但是这导致了我们谈论使用Git的方式上的一些混乱:例如,Raymond Chen最近写了一系列有趣的博客文章,解释你不应该在Git中挑剔承诺。虽然这对他的团队的工作流来说是非常合理的建议,但它与我们用来构建Visual Studio Team Services本身的工作流以及VSTS团队的日常工作方式相违背。
那么, - ,我们如何在VSTS团队中进行分支?首先,我们遵循基于主干的开发方法。但与一些基于干线的模型(如GitHub Flow)不同,我们不会持续地将Master部署到生产中。取而代之的是,我们通过为每个版本创建一个分支,在每个Sprint中发布我们的主分支。当我们需要将修补程序投入生产时,我们会从主版本中挑选那些更改到发布分支中。这是一种我们称之为“释放流”的策略。
我们是VSTS团队中基于干线的开发的忠实粉丝。我们喜欢简单的分支结构,其中只有一个主分支,每个人都在其中工作。这比许多年前我们的旧分支结构要简单得多,当时我们的团队与Visual Studio IDE在同一个TFVC存储库中。我们曾经有过这种多层次的分支策略,即 - to be礼貌 - “Complex”。
我与开发人员交谈的越多,我就越能观察到不进行基于主干的开发的团队往往会发生的事情。无论他们认为自己有多有组织,实际上,他们往往会以同样的方式组织自己的分支机构。这是康威定律的一个推论:
我们也不例外:您基本上可以看到您的代码在组织结构图中流动。当您签入您的分支时,您的代码最终将被“向前集成”到离主干更近的下一个分支中,最终落地到主干中。一旦发生这种情况,您会希望将主干“反向集成”回所有功能分支,这样您就可以拥有其他所有人的代码。
这是Merge地狱 - ,是的,这实际上就是我们所说的 - ,我们有一个人全职处理合并,解决冲突,并确保所有这些整合继续建立。不管我们付他多少钱,可能都不够。
当您放弃功能分支,开始考虑基于主干的分支策略时,经常会出现的是GitHub流。(请注意,这是GitHub流,而不是Git流,Git流有两个“干线”,因此根本不是基于干线的。)。
在GitHub工作期间,我对GitHub Flow非常熟悉。总体而言,我真的很喜欢这个系统;它是轻量级的,拥有良好的工具和自动化,您可以非常高效。这个系统在GitHub上运行得很好,但不幸的是,它不能扩展到VSTS团队的需求。这是因为GitHub流有一个经常被忽视的微妙之处。您实际上是在合并拉式请求之前将更改部署到生产中:
一旦您的拉入请求被检查并且分支通过了您的测试,您就可以部署您的更改以在Production…中验证它们。既然您的更改已经在生产中得到验证,现在就可以将您的代码合并到主分支中了。- 了解GitHub流。
这个系统非常聪明:当您准备好签入时,您会立即得到关于拉入请求在生产中的行为的反馈,该反馈会在您完成拉入请求之前发生。因此,如果您的代码更改有问题,您可以简单地放弃部署,并且您的坏代码永远不会合并到master中。这使您可以后退一步,查看监视数据以了解更改存在问题的原因,然后迭代拉取请求并重试。
此开发策略的问题在于,它对大型团队的伸缩性极差,因为当您尝试将其部署到生产环境时会出现争用:
在工作高峰期,多个开发人员经常试图将他们的更改部署到生产环境中。为了避免混淆并给每个人一个公平的机会,我们可以要求Hubot将我们添加到部署队列中。- 将分支机构部署到gihub.com。
(如果您不熟悉Hubot,那么它是GitHub聊天基础设施的核心。GitHub使用Hubot从Slake内部执行其部署。)
当您有几个开发人员时,您将需要一个部署队列来确保一次只能部署一个拉请求。这很好,但是随着您开始发展并雇佣更多的开发人员,排队的人也会更多。随着代码库的增长,构建开始需要更长的时间。随着您变得越来越受欢迎,您的基础设施也会随之增长,部署所需的时间也会随之增加。
Visual Studio team Services有数百名开发人员在从事这项工作。平均而言,我们每天构建、审查和合并200多个拉入请求到我们的主分支。如果我们想在合并它们之前部署它们,那会大大降低我们的速度。
我们试图取得平衡,既要快速编码,又要快速将更改带入Master,即使它们需要更长的时间才能投入生产。因此,我们不是将每个拉请求部署到生产中,而是每三周在每个Sprint - 结束时将MASTER部署到生产中。这意味着一项新功能可能需要这么长时间才能投入生产。(当然,该新特性可能只在测试中启用,而不是对所有用户启用,因为我们在生产中使用特性标志。)。
在冲刺结束时,当我们准备发布时,我们从master创建一个新分支。这将是Sprint剩余部分的发布分支。当新特性的工作和开发在大师手中进行时,生产与该工作保持良好的隔离状态。同样,这使我们的开发速度保持快速;我们不必担心需要多长时间才能将这些更改部署到我们分布在多个Azure区域的由数百台服务器组成的云中。我们只需打开一个Pull请求,进行代码审查,然后将其合并到Master中。
我们根据它们所对应的SPRINT来命名这些分支。在Sprint 129的末尾,我们从master创建一个名为Release/M129的分支并部署它。一旦我们在Sprint 130中完成了开发,我们就可以将这些更改部署到生产环境中了;在这一点上,我们忘记了旧的Release/M129分支。相反,我们从MASTER创建一个名为Release/M130的新分支并部署它。一旦Release/M130部署完成 - ,这将需要一段时间,因为我们使用环形部署策略 - ,所以我们不再关心旧的Release/M129分支。一旦所有服务器都运行Release/M130,并且生产中没有M129,该分支就只有历史意义了。我们甚至可以删除它。
当然,我们不希望生产存在于真空中。如果存在高优先级的bug或可用性问题,我们需要能够快速修复问题并立即部署。这就是挑樱桃的用武之地。
当我们需要对生产进行更改时,我们首先针对主分支进行更改。我们让它像通常的 - 一样进行代码审查,尽管优先级比普通的 - 高一点,并将其合并到MASTER中。然后,我们将该拉请求挑选到当前生产版本分支中,并开始部署它。
我们发现此工作流非常有用,您可以直接从VSTS挑选拉取请求:
这实际上挑选了整个Pull请求,将组成PR的每个提交从一个分支带到另一个分支。
我们总是以这种方式进行生产更改,从MASTER开始;这是因为代码进入生产的方式与最终进入生产的代码一样重要。如果我们直接热修复生产,我们可能会意外地忘记为下一个版本将更改带回MASTER。但是通过先把变化带入师父,我们确保我们的生产永远不会倒退。
这一点非常重要,我们询问您是否已在我们发布分支的拉式请求模板中这样做:
当然,唯一的例外是,将更改引入Master是没有意义的。也许已经进行了一些重构,这意味着MASTER中不再存在此错误。这是拉取请求可以直接进入发布分支的唯一一次,而不需要先通过MASTER。
尽管这个“大师优先”的政策需要额外的几分钟,但它总是值得的。当您感到解决生产事件的时间压力时,当您可能想偷工减料时,情况尤其如此。它确保我们只修复这些错误一次,并且不会因为相同的问题而出现重复的可用性事件。
我希望这能为我们在VSTS团队中使用的分支策略提供一些背景信息,以及为什么它对我们有效。当然,对于您的分支策略,您需要选择一种适合您的团队和您正在构建的产品的方法。随着这些变化,您应该愿意重新评估:当我们从构建每隔几年发货一次的内部部署产品过渡到始终部署云服务时,我们必须改变分支战略以适应需求。我们需要一个能够应对我们今天面临的挑战的结构,而不是过去的战斗。你也是。
如果您有任何问题,请随时发表评论--或者如果您要来参加5月7日的Build 2018大会,那么我很乐意亲自与您交谈。你可以顺道去一下世博会楼层的版本控制区,我会在那里闲逛。