本文档旨在描述 Apache™ Subversion® 的 1.7.x 系列。如果您运行的是其他版本的 Subversion,强烈建议您访问 https://svnbook.subversion.org.cn/ 并查阅适合您 Subversion 版本的文档。

常见的分支模式

分支和 svn merge 有许多不同的用途,本节将介绍最常见的用途。

版本控制最常用于软件开发,因此这里简要介绍了程序员团队使用的两种最常见的分支/合并模式。如果您没有将 Subversion 用于软件开发,可以跳过本节。如果您是第一次使用版本控制的软件开发人员,请仔细阅读,因为这些模式通常被经验丰富的开发人员视为最佳实践。这些流程并非特定于 Subversion;它们适用于任何版本控制系统。不过,用 Subversion 的术语来描述它们可能会有所帮助。

发布分支

大多数软件都有一个典型的生命周期:编码、测试、发布、重复。这个过程存在两个问题。首先,开发人员需要在质量保证团队花费时间测试据称稳定的软件版本的同时继续编写新功能。在软件测试期间,新工作不能停止。其次,团队几乎总是需要支持旧的已发布的软件版本;如果在最新代码中发现了一个错误,那么它很可能也存在于已发布的版本中,客户将希望获得该错误修复,而不必等待主要的新版本发布。

这就是版本控制可以提供帮助的地方。典型的流程如下所示

  1. 开发人员将所有新工作提交到主干。 日常更改会提交到 /trunk:新功能、错误修复等。

  2. 主干被复制到一个 发布 分支。 当团队认为软件已准备好发布(例如,1.0 版本)时,/trunk 可能会被复制到 /branches/1.0

  3. 团队继续并行工作。 一个团队开始对发布分支进行严格测试,而另一个团队则继续在 /trunk 上进行新工作(例如,针对 2.0 版本)。如果在任一位置发现错误,则根据需要将修复程序移植来回。然而,在某个时刻,即使是那个过程也会停止。在发布前,分支被 冻结 以进行最终测试。

  4. 分支被标记并发布。 当测试完成后,/branches/1.0 被复制到 /tags/1.0.0 作为参考快照。该标签被打包并发布给客户。

  5. 该分支会随着时间推移而维护。 当版本 2.0 的工作仍在 /trunk 上进行时,错误修复会继续从 /trunk 移植到 /branches/1.0。 当积累了足够的错误修复时,管理层可能会决定发布 1.0.1 版本:将 /branches/1.0 复制到 /tags/1.0.1,并对标签进行打包和发布。

随着软件的成熟,整个过程会重复:当 2.0 工作完成时,会创建一个新的 2.0 发布分支,进行测试、标记,并最终发布。 几年后,仓库中会留下一些处于“维护”模式的发布分支,以及一些代表最终发布版本的标签。

功能分支

一个功能分支是本章中一直作为主要示例的分支类型(你在 Sally 继续在 /trunk 上工作时一直在使用的分支)。 它是一个临时分支,用于进行复杂更改,而不会影响 /trunk 的稳定性。 与发布分支(可能需要永久维护)不同,功能分支会创建、使用一段时间、合并回主干,最终被删除。 它们的使用寿命是有限的。

同样,项目策略在何时创建功能分支方面差异很大。 一些项目根本不使用功能分支:对 /trunk 的提交是自由的。 这种系统的优点是简单——没有人需要学习分支或合并。 缺点是主干代码通常不稳定或不可用。 其他项目则极度使用分支:永远不会直接提交更改到主干。 即使是最微不足道的更改也会在短暂的分支上创建,经过仔细审查,然后合并到主干。 然后删除分支。 这种系统保证了主干始终保持异常稳定和可用,但代价是巨大的流程开销。

大多数项目采取折中的方法。 他们通常坚持 /trunk 始终能够编译并通过回归测试。 只有当更改需要大量不稳定的提交时,才需要功能分支。 一个好的经验法则是问自己这个问题:如果开发人员孤立地工作了几天,然后一次性提交了大型更改(这样 /trunk 就不会变得不稳定),那么这是否是一个太大而无法审查的更改? 如果答案是“”,则应在功能分支上开发更改。 当开发人员将增量更改提交到分支时,同行可以轻松地对其进行审查。

最后,还有一个问题是如何在工作进行时最好地将功能分支与主干保持“同步”。正如我们之前提到的,在一个分支上工作数周或数月存在很大的风险;主干的更改可能会不断涌入,以至于两条开发线之间的差异如此之大,以至于将分支合并回主干可能会变成一场噩梦。

这种情况最好通过定期将主干更改合并到分支来避免。制定一个策略:每周一次,将过去一周的主干更改合并到分支。

当您最终准备好将“同步”的功能分支合并回主干时,首先将最新的主干更改合并到分支。完成此操作后,分支和主干的最新版本将完全相同,除了您的分支更改。然后,您使用--reintegrate选项合并回来。

$ cd trunk-working-copy

$ svn update
Updating '.':
At revision 1910.

$ svn merge --reintegrate ^/calc/branches/mybranch
--- Merging differences between repository URLs into '.':
U    real.c
U    integer.c
A    newdirectory
A    newdirectory/newfile
 U   .
…

另一种思考这种模式的方法是,您每周将主干同步到分支类似于在工作副本中运行svn update,而最终的合并步骤类似于从工作副本中运行svn commit。毕竟,工作副本除了是一个非常浅层的私有分支之外,还能是什么呢?它是一个分支,一次只能存储一个更改。