持续集成时最佳的分支策略是什么?

105

在进行持续集成时,什么是最好的分支策略呢?

  1. 发布分支: 在主干上开发,为每个发布版本保留一个分支。
  2. 特性分支: 在单独的分支中开发每个特性,在稳定后再合并。

在相同的项目中同时使用这两种策略是否有意义呢?比如说,你可以为每个发布版本和大型特性创建分支。这两者之间哪一种策略更适合持续集成?在使用不稳定的主干时,使用持续集成是否有意义呢?


2
旁注:有些人会认为即使加入了新功能,一切都应该始终保持稳定。另一方面,这可能有点理想化。 - Keith Pinson
12个回答

22

我发现这个话题非常有趣,因为在我的日常工作中我严重依赖分支。

  • 我记得Mark Shuttleworth提出了一个模型,可以在传统CI的基础上保持主分支的纯净性。我在这里发表过相关内容。
  • 由于我熟悉Cruise Control,我也在这里写了一篇关于任务分支和CI的文章。这是一个逐步教程,详细说明如何使用Plastic SCM进行操作。
  • 最后,我还发现Duvall的CI书籍中的一些关于CI的主题(可能涉及分支)也非常有趣。点击这里查看更多。

希望你会找到这些链接很有趣。


我们为Bamboo添加了支持,以便每个任务都可以进行分支 http://codicesoftware.blogspot.com/2012/02/setting-up-bamboo-server-to-test-task.html,并且他们的最新版本将原生支持它,包括dvcs在内的多个版本控制。 - pablo

21
答案取决于团队规模、源代码控制的质量以及正确合并复杂变更集的能力。例如,在完整的分支源代码控制(如CVS或SVN)中,合并可能很困难,您最好选择第一个模型,而如果使用更复杂的系统(如IBM ClearCase)和较大的团队,则可以选择第二个模型或两个模型的组合。
我个人会采用功能分支模型,其中每个主要功能都在单独的分支上开发,并为每个由个人开发人员完成的更改创建任务子分支。当功能稳定时,它们会合并到主干上,您需要保持主干始终稳定并通过所有回归测试。当您接近发布周期的结束,所有功能分支合并时,您需要稳定并分支出一个发布系统分支,只在其上执行稳定性错误修复和必要的后移操作,而主干用于下一个版本的开发,并再次分支出新的功能分支。以此类推。
这样,主干始终包含最新的代码,但您设法保持其相对稳定,并在重大更改和功能合并时创建稳定标签(标记)。功能分支是快节奏的开发,拥有连续集成,个人任务子分支经常从功能分支刷新,以使每个人在相同功能上保持同步,同时不影响其他团队开发的不同功能。
同时,通过历史记录,您可以拥有一组发布分支,您可以为仍停留在产品先前版本或甚至仅发布最新版本的客户提供后移操作、支持和错误修复。与主干一样,您不需要在发布分支上设置连续集成,它们会经过仔细集成,以通过所有回归测试和其他发布质量控制。
如果由于某种原因两个功能相互依存并需要彼此进行更改,则可以考虑在同一功能分支上开发两者,或要求功能定期将稳定的代码部分合并到主干上,然后从主干刷新更改以交换主干分支之间的代码。或者,如果您需要将这两个功能与其他功能隔离开来,则可以创建一个公共分支,从该分支分支出这些功能分支,并可用于在功能之间交换代码。

如果团队规模少于50名开发人员且源代码控制系统没有类似CVS或SVN的稀疏分支和适当的合并能力,则上述模型不太合理。这将使整个模型的设置、管理和集成成为噩梦。


5
我不确定我是否同意你所描述的对于50人以下的团队来说没有意义。我认为即使是更小的团队也可以受益。+1 - Aardvark
2
当然,任何规模的团队都有好处。问题在于,在何种团队规模下,好处超过了与重型流程相关的成本。 - Jiri Klouda
这类似于GitFlow和/或GitHubFlow模型。我认为这些模型不利于持续集成(CI)。在我看来,基于主干的开发是对这些模型的重大改进。 - Yani
您可以看到,这个注释实际上早于git flow的原始版本发布。不太确定您所说的“更好”是什么意思。我曾支持过1人、5人、25人、150人、1,000人和20,000人的团队,他们开发的项目在某种程度上已集成。要求各不相同,“更好”是一个非常相对的术语。您需要回溯代码吗?安全修复程序?如果不需要,那么您的生活就很简单。SaaS是基于主干开发模式施加的限制的直接结果。功能标志与功能分支一样复杂。除非客户在它们的变异破坏时才会发现。 - Jiri Klouda

9
我个人认为,使用稳定的主干并进行功能分支会更加清晰。这样,测试人员等可以保持在单一的“版本”上,并从主干更新以测试任何已完成的功能。
此外,如果多个开发人员正在开发不同的功能,他们都可以有自己的分支,然后在完成后合并到主干,并发送一个功能进行测试,而不需要测试人员切换到多个分支来测试不同的功能。
额外的好处是,自动带来了一定程度的集成测试。

还有,你们现在每次主要发行版都分支和打标签吗?还是只打标签? - KingNestor
1
只要有一定的纪律性,将特性分支合并到主干分支时避免构建失败,它就能够与CI良好地配合工作。我为每个生产发布进行分支和标记,仅用于错误修复。这可以立即合并到稳定的主干分支。 - Adnan
@king 我想这可能取决于你所谓的主要版本发布是什么,但无论哪种情况,你都可以在需要时进行标记和分支(基于标记 :))。 - eglasius

5
发布分支非常有用,甚至是必需的,如果您需要维护应用程序的几个版本。
功能分支也非常方便,尤其是如果一个开发人员需要处理巨大的更改,而其他人仍在发布新版本。
因此,对我来说,同时使用这两种机制是一种非常好的策略。
来自SVN之书的有趣链接。

5

我认为在持续开发中,任何一种策略都可以使用,只要记住其中一个关键原则:每个开发人员每天都要向主干提交代码。

http://martinfowler.com/articles/continuousIntegration.html#EveryoneCommitsToTheMainlineEveryDay

编辑

我正在阅读有关CI的这本书,作者建议使用按发布分支的策略。我同意这种做法。在使用CI时,按功能分支没有意义。

我将尝试解释我为什么这样想。假设三个开发人员各自拿出一个分支来处理一个功能。每个功能需要几天或几周才能完成。为了确保团队持续集成,他们必须每天至少提交一次到主分支。一旦他们开始这样做,他们就失去了创建功能分支的好处。他们的更改不再与其他开发人员的更改分离。既然如此,为什么要先创建功能分支呢?

使用按发布分支的方式需要较少合并分支(总是一件好事),确保所有更改尽快集成,并且(如果正确执行)确保您的代码库随时准备好发布。按发布分支的缺点是您必须对更改更加小心。例如,大型重构必须逐步完成,如果您已经集成了下一个版本不需要的新功能,则必须使用某种功能切换机制隐藏它。
另外编辑
对于这个问题,有不止一种观点。以下是一篇赞成使用CI进行特性分支的博客文章。

http://jamesmckay.net/2011/07/why-does-martin-fowler-not-understand-feature-branches/


有趣,无法再找到这篇帖子了。 - Jirong Hu

4
我最近在使用git时喜欢这个模型。虽然你的问题标记为“svn”,但你仍然可以从中受益。
在这个模型中,“持续集成”在“develop”分支(或者你称之为其他名称的分支)中可以在某种程度上发生,尽管为未来版本建立长期运行的功能分支不会使其变得那么严格,以至于要考虑到代码中的每一次更改。问题在于,你是否真的想要这样做。Martin Fowler想要这样做。

3
持续集成不应成为确定分支策略的任何因素。你的分支方法应基于你的团队、开发系统和可用工具进行选择。
话虽如此...
  • 没有理由不能在你所描述的两种方法中都使用CI
  • 这些方法结合起来效果非常好
  • 两者都没有比另一个更好的表现
  • 在不稳定的主干上使用CI是完全合理的
所有这些都在你从中获取图表的页面的第四个问题中得到了回答: http://blogs.collab.net/subversion/2007/11/branching-strat/

3
只要你理解原则,就可以重新发明最佳实践。如果你不理解原则,最佳实践在遇到某些冲突的外部需求之前只能走那么远。
要了解Mainline Model的最佳介绍,请阅读以下内容:https://web.archive.org/web/20120304070315/http://oreilly.com/catalog/practicalperforce/chapter/ch07.pdf 阅读链接。一旦你掌握了基本知识,请阅读尊敬的Henrik Kniberg的以下文章。它将帮助您将Mainline Model与持续集成联系起来。http://www.infoq.com/articles/agile-version-control

O'Reilly 章节不再可访问。 - Jason S

2
《持续交付》的作者戴夫·法利基干开发(TBD)是持续集成(CI)和持续交付(CD)的基石。他说:
“任何形式的分支都与持续集成背道而驰。”
他还说,
“特性分支从个人开发者的角度来看非常好,但从团队的角度来看并不是最佳选择。我们都希望能够忽略其他人正在做什么,专注于自己的工作。不幸的是,代码并不是这样的。即使在非常良好的代码库中,具有美妙的关注点分离和松散耦合的组件,某些更改也会影响系统的其他部分。”
Trunk Based Development(TBD)是一种将代码更改至少每天一次(最好每天多次)集成到主干(也称为master,mainline)的做法。持续集成(CI)是类似的做法,但它还涉及使用自动化测试验证代码更改。这种情况下最好的分支策略是直接从主干上工作,并通过Pair-Programming进行代码审查。如果由于某些原因你不能配对,或者你真的想要分支,请确保你的分支生命周期很短(少于一天)。
引用: 我在我的GIT repos中使用Trunk,“master”。我在本地提交到主分支并立即推送,当我联网时,CI就会运行。就是这样!
针对较大的功能(即需要超过一天时间的功能),请尝试将它们拆分为小的逻辑块,可以将其集成到主干中而不会破坏软件。您还可以使用诸如特性标志抽象分支等技术,以便在不影响最终用户的情况下部署不完整的工作。

我使用抽象分支、暗中发布和有时特性标志。回报是快速、明确(至少按我的测试质量)的反馈。


Dave Farley和Jez Humble在分支方面的立场上是错误的。原因是它编码了一个重要的假设“您永远不必在功能级别上操作代码,如果需要,则可以将其视为昂贵的操作”,而他们的评估基于另一个假设“合并成本太高,在规模上几乎不可能进行自动合并”。如果这两个假设不成立,如果您生活在合并便宜但需要在功能级别上操作代码以进行后向端口和安全修复的世界中,则他们的陈述会崩溃。虽然这是一种罕见情况。 - Jiri Klouda
有些公司也需要将功能推迟到未来的版本中,因为这些功能在实现过程中遇到了障碍,并且正在拖延发布。有时候可以选择保留代码,比如在SaaS产品中,但如果代码发布给客户,这可能不是一个选项,因为竞争对手可以分析它。如今,很多代码都没有编译,即使编译了,代码中的定义/特性标志与分支的复杂度相同。 - Jiri Klouda

1

当我们开始组建团队时,我们从最初开发系统的供应商那里继承了一个基于发布的策略。这个策略一直有效,直到我们的客户要求在发布中不包括几个已经开发好的功能(FYI ~250k行代码,~2500个文件,Scrum和XP SDLC)。

然后我们开始研究基于功能的分支。这种方法也有效了一段时间——大约两个月,直到我们意识到回归测试过程需要超过两周的时间,再加上不确定会发布哪些内容,这带来了很大的不便。

纯SC策略的最后一根稻草是当我们决定应该有1. 稳定的主干和2. 生产环境应该包含ST、UAT和回归测试过的二进制文件(不仅仅是源代码——想想CC)。

这促使我们设计了一种介于基于功能和基于发布的SC策略之间的混合策略。

所以我们有一个主干(trunk)。每个sprint,我们会从主干分出一个sprint branch(对于非敏捷的人来说,sprint只是一个基于复杂度可变的时间限制开发),然后在这个sprint branch上创建功能分支,开始并行开发。一旦功能完成和系统测试通过,并且我们收到部署的意图,它们就会合并到sprint branch中。有些可能会跨越几个sprint,通常是更复杂的。当sprint接近其结束并且功能已完成时......我们将sprint branch“重命名”为“回归测试”(这使得CruiseControl可以在不进行任何重新配置的情况下获取它),然后在cc-built EAR上开始执行回归测试/集成测试。当所有测试完成后,它就可以投入生产了。
简而言之,基于功能的分支用于开发、系统测试和UAT功能。sprint分支(实际上是发布分支)用于选择性地按需合并功能和集成测试。
现在,这里有一个问题要问社区-由于开发发生在许多分支上并且CruiseControl的重新配置开销,我们显然遇到了连续集成的问题。有人可以建议和提供建议吗?

我不一定同意结论,但感谢您讨论您的过程。没有一种适合所有情况的解决方案。 - RaoulRubin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接