手册

合并

当使用分支来维护独立的开发路线时,在某个阶段,您可能需要将一个分支上的更改合并回主干,反之亦然。

在开始使用 Subversion 之前,了解 Subversion 中的分支和合并工作原理非常重要,因为这可能会变得相当复杂。强烈建议您阅读 Subversion 手册中的 分支和合并 一章,其中详细描述了分支和合并的用法,并提供了许多示例。

需要注意的下一件事是,合并 始终 在工作副本中进行。如果您想将更改 合并到 分支中,您必须检出一个该分支的工作副本,并使用 TortoiseSVN合并... 从该工作副本中调用合并向导。

通常,将合并执行到未修改的工作副本中是一个好主意。如果您在 WC 中进行了其他更改,请先提交这些更改。如果合并没有按预期进行,您可能需要还原更改,而 还原 命令将丢弃 所有 更改,包括您在合并之前所做的任何更改。

合并有三种常见的用例,它们以略微不同的方式处理,如下所述。合并向导的第一页要求您选择所需的方法。

合并一系列修订版

此方法涵盖了您对分支(或主干)进行了一次或多次修订,并且您希望将这些更改移植到另一个分支的情况。

您要求 Subversion 做的是: 计算从分支 A 的修订版 1 到分支 A 的修订版 7 所需的更改,并将这些更改应用到我的工作副本(主干或分支 B)。

如果您将修订版范围留空,Subversion 将使用合并跟踪功能来计算要使用的正确修订版范围。这被称为重新集成或自动合并。

合并两棵不同的树

这是重新整合方法的更一般情况。您要求 Subversion 做的是: 计算从主干的最新版本到分支的最新版本所需的变化,并将这些变化应用到我的工作副本(主干)。 最终结果是主干现在看起来与分支完全相同。

如果您的服务器/存储库不支持合并跟踪,那么这是将分支合并回主干的唯一方法。另一个用例发生在您使用供应商分支并且您需要将新的供应商发布后的更改合并到您的主干代码中时。有关更多信息,请阅读 Subversion 手册中关于 供应商分支 的章节。

合并修订版本范围

图 4.56。合并向导 - 选择修订版本范围

The Merge Wizard - Select Revision Range


从: 字段中,输入包含您要移植到工作副本的更改的分支或标签的完整文件夹 URL。您也可以单击 ... 浏览存储库并找到所需的分支。如果您之前已从此分支合并,则只需使用下拉列表,其中显示了以前使用的 URL 的历史记录。

如果您要从已重命名或已删除的分支合并,则您需要返回到该分支仍然存在的一个修订版本。在这种情况下,您还需要将该修订版本指定为合并的修订版本范围中的挂钩修订版本(见下文),否则合并将无法找到 HEAD 上的该路径而失败。

要合并的修订版本范围 字段中,输入您要合并的修订版本列表。这可以是一个单独的修订版本,一个由逗号分隔的特定修订版本列表,或者一个由连字符分隔的修订版本范围,或者这些的任何组合。

如果您需要为合并指定挂钩修订版本,请在修订版本末尾添加挂钩修订版本,例如 5-7,10@3。在上面的示例中,将合并修订版本 5、6、7 和 10,其中 3 是挂钩修订版本。

重要

在 TortoiseSVN 中指定修订版本范围的方式与命令行客户端相比存在重要差异。最简单的方法是将其想象成一个带有柱子和围栏板的围栏。

使用命令行客户端,您可以使用两个 围栏柱 修订版本来指定要合并的更改,这两个修订版本指定了 之前之后 的点。

使用 TortoiseSVN,您可以使用 围栏面板 指定要合并的变更集。当您使用日志对话框指定要合并的修订版时,原因就变得很清楚了,因为每个修订版都显示为一个变更集。

如果您要分块合并修订版,Subversion 手册中显示的方法会让您这次合并 100-200,下次合并 200-300。使用 TortoiseSVN,您这次会合并 100-200,下次会合并 201-300。

这种差异在邮件列表中引起了很多热议。我们承认与命令行客户端存在差异,但我们认为,对于大多数 GUI 用户来说,我们实现的方法更容易理解。

选择所需修订版范围的最简单方法是单击 显示日志,因为这将列出最近的更改及其日志注释。如果您要合并单个修订版的更改,只需选择该修订版。如果您要合并多个修订版的更改,请选择该范围(使用通常的 Shift 修饰符)。单击 确定,将为您填写要合并的修订版号列表。

如果您要将更改合并回 工作副本,以撤消已提交的更改,请选择要撤消的修订版,并确保选中 反向合并 框。

如果您已经从该分支合并了一些更改,希望您在提交更改时在日志消息中记下了最后合并的修订版。在这种情况下,您可以使用 显示日志 来跟踪工作副本的日志消息。记住,我们将修订版视为变更集,您应该使用上次合并结束点之后的修订版作为本次合并的起点。例如,如果您上次合并了修订版 37 到 39,则本次合并的起点应该是修订版 40。

如果您使用的是 Subversion 的合并跟踪功能,则无需记住哪些修订版已合并 - Subversion 会为您记录。如果您将修订版范围留空,则将包含所有尚未合并的修订版。阅读 名为“合并跟踪”的部分 以了解更多信息。

使用合并跟踪时,日志对话框将显示以前合并的修订版,以及在公共祖先点之前(即分支复制之前)的修订版,这些修订版将显示为灰色。 隐藏不可合并的修订版 复选框允许您完全过滤掉这些修订版,以便您只看到 可以 合并的修订版。

如果其他人可能正在提交更改,那么在使用 HEAD 修订版时要小心。如果其他人在你上次更新后提交了更改,它可能不指向你认为的修订版。

如果你将修订版范围留空或选中了 所有修订版 单选按钮,那么 Subversion 会合并所有尚未合并的修订版。这被称为重新整合或自动合并。

重新整合合并有一些适用条件。首先,服务器必须支持合并跟踪。工作副本的深度必须为无限(无稀疏检出),并且不能有任何本地修改、切换项或已更新到除 HEAD 之外的其他修订版的项。在分支开发期间对主干的所有更改都必须合并到分支(或标记为已合并)。要合并的修订版范围将自动计算。

单击 下一步 并转到 名为“合并选项”的部分

合并两个不同的树

图 4.57。合并向导 - 树合并

The Merge Wizard - Tree Merge


如果你使用此方法将功能分支合并回主干,则需要从主干的工作副本中启动合并向导。

从: 字段中输入 主干 的完整文件夹 URL。这听起来可能不对,但请记住,主干是你要添加分支更改的起点。你也可以单击 ... 浏览存储库。

到: 字段中输入功能分支的完整文件夹 URL。

从修订版 字段和 到修订版 字段中,都输入两棵树同步的最后一个修订版号。如果你确定没有其他人正在提交更改,则可以在两种情况下都使用 HEAD 修订版。如果有人可能在同步后提交了更改,请使用特定的修订版号以避免丢失最近的提交。

你也可以使用 显示日志 选择修订版。

合并选项

此向导页面允许您在开始合并过程之前指定高级选项。大多数情况下,您只需使用默认设置。

您可以指定用于合并的深度,即合并应深入到工作副本的程度。使用的深度术语在名为“检出深度”的部分中进行了描述。默认深度为工作副本,它使用现有的深度设置,几乎总是您想要的。

大多数情况下,您希望合并考虑文件的历史记录,以便合并相对于共同祖先的更改。有时您可能需要合并可能相关但不在您的存储库中的文件。例如,您可能已将第三方库的版本 1 和 2 导入到两个单独的目录中。虽然它们在逻辑上相关,但 Subversion 对此一无所知,因为它只看到您导入的 tarball。如果您尝试合并这两个树之间的差异,您将看到完全删除,然后是完全添加。要使 Subversion 只使用基于路径的差异而不是基于历史的差异,请选中忽略祖先框。在 Subversion 手册中阅读有关此主题的更多信息, 注意或忽略祖先 .

您可以指定处理行尾和空格更改的方式。这些选项在名为“行尾和空格选项”的部分中进行了描述。默认行为是将所有空格和行尾差异视为要合并的真实更改。

标记为强制合并的复选框用于避免树冲突,其中传入的删除影响本地修改或根本未版本化的文件。如果文件被删除,则无法恢复它,这就是该选项默认情况下未选中。

标记为允许混合修订版(不推荐)的复选框用于允许合并到混合修订版工作副本(对应于命令行选项--allow-mixed-revisions)。自 Subversion 1.7 以来,默认情况下不允许合并到混合修订版工作副本。原因在 Subversion 手册中 保持分支同步 部分的末尾进行了描述。如果您了解合并到混合修订版工作副本的潜在问题,您可以选中此复选框以执行合并。

如果您正在使用合并跟踪,并且想要将一个修订标记为已合并,而无需实际执行合并操作,请选中仅记录合并复选框。您可能想要这样做有两个原因。可能是合并过于复杂,无法使用合并算法,因此您手动编写更改,然后将更改标记为已合并,以便合并跟踪算法能够识别它。或者,您可能希望阻止特定修订被合并。将其标记为已合并将阻止使用合并跟踪感知客户端进行合并。

现在一切都已设置好,您只需点击合并按钮即可。如果您想预览结果,测试合并会模拟合并操作,但不会修改工作副本。它会向您显示将被实际合并更改的文件列表,并指出可能发生冲突的文件。由于合并跟踪使合并过程变得更加复杂,因此无法保证提前知道合并是否会在没有冲突的情况下完成,因此在测试合并中标记为冲突的文件实际上可能在没有问题的情况下合并。

合并进度对话框显示合并的每个阶段,以及涉及的修订范围。这可能表明比您预期的多一个修订。例如,如果您要求合并修订 123,进度对话框将报告合并修订 122 到 123。要理解这一点,您需要记住合并与差异密切相关。合并过程通过生成存储库中两个点之间的差异列表,并将这些差异应用于您的工作副本来完成。进度对话框只是显示了差异的起点和终点。

查看合并结果

合并现在已完成。最好查看一下合并结果,看看是否符合预期。合并通常非常复杂。如果分支与主干相差甚远,则经常会发生冲突。

提示

每当将修订版本合并到工作副本时,TortoiseSVN 会从所有合并的修订版本生成日志消息。然后,这些消息可以在提交对话框中的 最近消息 按钮中获取。

要自定义生成的日志消息,请在工作副本上设置相应的项目属性。请参阅 名为“合并日志消息模板”的部分

对于 1.5 之前的 Subversion 客户端和服务器,不会存储任何合并信息,并且必须手动跟踪合并的修订版本。当您测试完更改并准备提交此修订版本时,您的提交日志消息应 始终 包含在合并中移植的修订版本号。如果您想在以后应用另一个合并,您需要知道您已经合并了什么,因为您不想多次移植更改。有关此方面的更多信息,请参阅 Subversion 手册中的 合并最佳实践

如果您的服务器和所有客户端都运行的是 Subversion 1.5 或更高版本,则合并跟踪功能将记录合并的修订版本,并避免多次合并修订版本。这使您的生活变得更加简单,因为您可以每次简单地合并整个修订版本范围,并知道只有新的修订版本才会被实际合并。

分支管理很重要。如果您想让此分支与主干保持同步,您应该确保经常合并,这样分支和主干不会相差太远。当然,您仍然应该避免重复合并更改,如上所述。

提示

如果您刚刚将功能分支合并回主干,则主干现在包含所有新的功能代码,并且分支已过时。如果需要,您现在可以将其从存储库中删除。

重要

Subversion 无法将文件与文件夹合并,反之亦然 - 只能将文件夹合并到文件夹,将文件合并到文件。如果您单击文件并打开合并对话框,则必须在该对话框中提供文件路径。如果您选择文件夹并打开对话框,则必须指定用于合并的文件夹 URL。

合并跟踪

Subversion 1.5 引入了合并跟踪功能。当您将更改从一个树合并到另一个树时,合并的修订版本号将被存储,并且此信息可用于多种不同的目的。

  • 您可以避免重复合并相同修订版本的风险(重复合并问题)。一旦一个修订版本被标记为已合并,将来包含该修订版本的合并范围将跳过它。

  • 当您将分支合并回主干时,日志对话框可以将分支提交显示为主干日志的一部分,从而更好地跟踪更改。

  • 当您从合并对话框中显示日志对话框时,已合并的修订版本将以灰色显示。

  • 当显示文件的归责信息时,您可以选择显示合并修订版本的原始作者,而不是执行合并的人员。

  • 您可以通过将修订版本包含在合并修订版本列表中(而不实际执行合并)来将它们标记为不合并

合并跟踪信息由客户端在执行合并时存储在svn:mergeinfo属性中。当合并被提交时,服务器将该信息存储在数据库中,当您请求合并、日志或归责信息时,服务器可以做出相应的响应。为了使系统正常工作,您必须确保服务器、存储库和所有客户端都已升级。早期客户端不会存储svn:mergeinfo属性,早期服务器不会提供新客户端请求的信息。

从 Subversion 的 合并跟踪文档 中了解更多关于合并跟踪的信息。

处理合并后的冲突

重要

冲突解决程序对话框中的文本由 SVN 库提供,因此可能尚未翻译成 TortoiseSVN 对话框。对此我们深感抱歉。

合并并不总是顺利进行。有时会出现冲突。TortoiseSVN 通过显示合并冲突对话框来帮助您完成此过程。

图 4.58. 合并冲突对话框

The Merge Conflict Dialog


一些更改可能已顺利合并,而其他本地更改与已提交到存储库的更改冲突。所有可以合并的更改都已合并。合并冲突对话框提供了多种处理冲突行的方法。

对于由于文件内容或其属性更改而发生的正常冲突,对话框将显示按钮,允许您选择保留或拒绝哪个冲突部分。

推迟

现在不要处理冲突。让合并继续,并在合并完成后解决冲突。

接受基础

这将使文件保持原样,既不包含来自合并的更改,也不包含您在工作副本中所做的更改。

接受传入

这将丢弃所有本地更改,并使用来自合并源的文件。

拒绝传入

这将丢弃来自合并源的所有更改,并将文件保留为您的本地编辑。

接受传入冲突

这将丢弃与来自合并源的更改冲突的本地更改。但它会保留所有不冲突的本地更改。

拒绝冲突

这将丢弃与本地更改冲突的来自合并源的更改。但它会保留所有不与本地更改冲突的更改。

标记为已解决

将冲突标记为已解决。此按钮在您使用编辑按钮手动编辑冲突并将这些更改保存回文件之前处于禁用状态。保存更改后,该按钮将启用。

编辑

启动合并编辑器,以便您可以手动解决冲突。不要忘记保存文件,这样标记为已解决按钮就会启用。

如果有树冲突,请首先查看名为“树冲突”的部分,了解各种树冲突类型以及它们如何以及为什么发生。

要解决合并后的树冲突,将显示一个对话框,其中包含有关如何解决冲突的各种选项。

图 4.59。合并树冲突对话框

The Merge Tree Conflict Dialog


由于存在各种可能的树冲突情况,对话框将显示按钮以解决这些冲突,具体取决于特定冲突。按钮文本和标签解释了解决冲突的选项的作用。如果您不确定,请取消对话框或使用推迟按钮稍后解决冲突。

功能分支维护

当您在单独的分支上开发新功能时,最好制定一个在功能完成后重新集成的策略。如果trunk中同时进行其他工作,您可能会发现随着时间的推移,差异变得越来越大,合并回主干将成为噩梦。

如果功能相对简单,开发时间不长,您可以采用一种简单的方法,即在功能完成之前将分支完全分开,然后将分支更改合并回主干。在合并向导中,这将是一个简单的合并一系列修订,修订范围是分支的修订范围。

如果功能开发需要更长时间,并且您需要考虑 trunk 中的变化,那么您需要保持分支同步。这仅仅意味着您需要定期将 trunk 的更改合并到分支中,以便分支包含所有 trunk 的更改 加上 新功能。同步过程使用 合并一系列修订。当功能完成时,您可以使用 重新整合分支合并两个不同的树 将其合并回 trunk

另一种(快速)将所有更改从 trunk 合并到功能分支的方法是使用扩展上下文菜单中的 TortoiseSVN合并所有...(在右键单击文件时按住 Shift 键)。

图 4.60. 合并所有对话框

The Merge-All Dialog


此对话框非常简单。您只需设置合并选项,如 名为“合并选项”的部分 中所述。其余操作由 TortoiseSVN 使用合并跟踪自动完成。

TortoiseSVN 主页