本文件描述了 Subversion 1.6.x 系列。如果您运行的是不同版本的 Subversion,强烈建议您访问 https://svnbook.subversion.org.cn/,并查阅适合您的 Subversion 版本的文档。

使用外部差异和合并工具

Subversion 与外部二路和三路差异工具之间的接口可以追溯到 Subversion 唯一基于上下文的差异功能建立在调用 GNU diffutils 工具链,特别是 diffdiff3 实用程序的时候。为了获得 Subversion 所需的行为,它调用了这些实用程序,并附带了大量选项和参数,其中大部分是针对这些实用程序的。一段时间后,Subversion 发展了自己的内部差异库,并作为一种故障转移机制,在 Subversion 命令行客户端中添加了 --diff-cmd--diff3-cmd 选项,以便用户可以更容易地表明他们希望使用 GNU diff 和 diff3 实用程序而不是新式的内部差异库。如果使用了这些选项,Subversion 将简单地忽略内部差异库,并回退到运行这些外部程序,包括冗长的参数列表。这就是今天的情况。

人们很快就意识到,对于指定 Subversion 应该使用系统特定位置的外部 GNU diff 和 diff3 实用程序,这种简单的配置机制也可以应用于其他差异工具。毕竟,Subversion 并没有真正验证它被告知要运行的东西是否属于 GNU diffutils 工具链。但是使用这些外部工具的唯一可配置方面是它们在系统中的位置,而不是选项集、参数顺序等等。无论该程序是否能理解这些选项,Subversion 都会继续将所有这些 GNU 实用程序选项传递给您的外部差异工具。这就是大多数用户感到不直观的地方。

[Note] 注意

在 Subversion 操作中何时触发基于上下文的二路或三路差异的决定完全由 Subversion 决定,并且受多种因素影响,包括根据文件的 svn:mime-type 属性判断文件是否为人类可读。这意味着,例如,即使您拥有世界上最棒的 Microsoft Word 感知差异或合并工具,只要您版本化的 Word 文档配置的 MIME 类型表示它们不是人类可读的(例如 application/msword),Subversion 永远不会调用它。有关 MIME 类型设置的更多信息,请参见 名为“文件内容类型”一节

后来,Subversion 1.5 引入了交互式冲突解决(在 名为“解决任何冲突”一节中描述)。此功能提供给用户的选项之一是能够交互式地启动第三方合并工具。如果采取了此操作,Subversion 将检查用户是否为此目的指定了此类工具。Subversion 将首先检查 SVN_MERGE 环境变量,以查找外部合并工具的名称。如果未设置该变量,它将在 merge-tool-cmd 运行时配置选项的值中查找相同的信息。找到配置的外部合并工具后,它将调用该工具。

[Note] 注意

虽然三路差异和合并工具的一般用途大体相同(找到一种方法让分离但重叠的文件更改和谐共处),但 Subversion 在不同的时间和出于不同的原因使用这些选项。内部三路差异引擎及其可选的外部替换程序用于 需要用户交互的情况下。实际上,由这种工具引入的重大延迟实际上会导致一些时间敏感的 Subversion 操作失败。外部合并工具旨在交互式地调用。

现在,虽然 Subversion 与外部合并工具之间的接口比 Subversion 与 diff 和 diff3 工具之间的接口简单得多,但找到调用约定完全符合 Subversion 预期的此类工具的可能性仍然很低。在 Subversion 中使用外部差异和合并工具的关键是使用包装器脚本,将来自 Subversion 的输入转换为您的特定差异工具可以理解的格式,然后将您的工具的输出转换回 Subversion 预期的格式。以下部分介绍了这些预期的详细信息。

外部差异

Subversion 使用适合 GNU diff 实用程序的参数调用外部差异程序,并且只期望外部程序根据 GNU diff 的定义以成功的错误代码返回。对于大多数替代差异程序,只有第六和第七个参数(表示差异左右两侧文件的路径)才是重要的。请注意,Subversion 对每个由 Subversion 操作覆盖的修改文件运行一次差异程序,因此如果您的程序以异步方式运行(或 后台运行),您可能会有多个实例同时运行。最后,Subversion 预期您的程序如果检测到差异则返回错误代码 1,如果未检测到差异则返回 0,任何其他错误代码都将被视为致命错误。[56]

示例 7.2 “diffwrap.py”示例 7.3 “diffwrap.bat” 分别是 Python 和 Windows 批处理脚本语言中外部差异工具包装器的模板。

示例 7.2 diffwrap.py

#!/usr/bin/env python
import sys
import os

# Configure your favorite diff program here.
DIFF = "/usr/local/bin/my-diff-tool"

# Subversion provides the paths we need as the last two parameters.
LEFT  = sys.argv[-2]
RIGHT = sys.argv[-1]

# Call the diff command (change the following line to make sense for
# your diff program).
cmd = [DIFF, '--left', LEFT, '--right', RIGHT]
os.execv(cmd[0], cmd)

# Return an errorcode of 0 if no differences were detected, 1 if some were.
# Any other errorcode will be treated as fatal.

示例 7.3 diffwrap.bat

@ECHO OFF

REM Configure your favorite diff program here.
SET DIFF="C:\Program Files\Funky Stuff\My Diff Tool.exe"

REM Subversion provides the paths we need as the last two parameters.
REM These are parameters 6 and 7 (unless you use svn diff -x, in
REM which case, all bets are off).
SET LEFT=%6
SET RIGHT=%7

REM Call the diff command (change the following line to make sense for
REM your diff program).
%DIFF% --left %LEFT% --right %RIGHT%

REM Return an errorcode of 0 if no differences were detected, 1 if some were.
REM Any other errorcode will be treated as fatal.

外部 diff3

Subversion 调用三路差异程序来执行非交互式合并。当配置为使用外部三路差异程序时,它会使用适合 GNU diff3 实用程序的参数执行该程序,期望外部程序将以成功的错误代码返回,并且合并操作完成后的完整文件内容将打印在标准输出流上(以便 Subversion 可以将它们重定向到适当的版本控制文件)。对于大多数替代合并程序,只有第九、第十和第十一个参数(表示 我的旧的你的 输入文件的路径)才是重要的。请注意,由于 Subversion 依赖于您的合并程序的输出,因此您的包装器脚本在将该输出传递给 Subversion 之前不应退出。当它最终退出时,它应该返回错误代码 0 以表示合并成功,或者返回 1 以表示输出中仍然存在未解决的冲突,任何其他错误代码都将被视为致命错误。

示例 7.4 “diff3wrap.py”示例 7.5 “diff3wrap.bat” 分别是 Python 和 Windows 批处理脚本语言中外部三路差异工具包装器的模板。

示例 7.4 diff3wrap.py

#!/usr/bin/env python
import sys
import os

# Configure your favorite three-way diff program here.
DIFF3 = "/usr/local/bin/my-diff3-tool"

# Subversion provides the paths we need as the last three parameters.
MINE  = sys.argv[-3]
OLDER = sys.argv[-2]
YOURS = sys.argv[-1]

# Call the three-way diff command (change the following line to make
# sense for your three-way diff program).
cmd = [DIFF3, '--older', OLDER, '--mine', MINE, '--yours', YOURS]
os.execv(cmd[0], cmd)

# After performing the merge, this script needs to print the contents
# of the merged file to stdout.  Do that in whatever way you see fit.
# Return an errorcode of 0 on successful merge, 1 if unresolved conflicts
# remain in the result.  Any other errorcode will be treated as fatal.

示例 7.5 diff3wrap.bat

@ECHO OFF

REM Configure your favorite three-way diff program here.
SET DIFF3="C:\Program Files\Funky Stuff\My Diff3 Tool.exe"

REM Subversion provides the paths we need as the last three parameters.
REM These are parameters 9, 10, and 11.  But we have access to only
REM nine parameters at a time, so we shift our nine-parameter window
REM twice to let us get to what we need.
SHIFT
SHIFT
SET MINE=%7
SET OLDER=%8
SET YOURS=%9

REM Call the three-way diff command (change the following line to make
REM sense for your three-way diff program).
%DIFF3% --older %OLDER% --mine %MINE% --yours %YOURS%

REM After performing the merge, this script needs to print the contents
REM of the merged file to stdout.  Do that in whatever way you see fit.
REM Return an errorcode of 0 on successful merge, 1 if unresolved conflicts
REM remain in the result.  Any other errorcode will be treated as fatal.

外部合并

Subversion 作为其对交互式冲突解决的支持的一部分,可以选择性地调用外部合并工具。它向合并工具提供以下参数:未修改的基本文件的路径、他们的 文件的路径(包含上游更改)、我的 文件的路径(包含本地修改)、合并工具应将最终解析的内容存储到的文件的路径以及冲突文件的版本控制路径(相对于合并操作的原始目标)。合并工具应返回错误代码 0 以表示成功,或返回 1 以表示失败。

示例 7.6 “mergewrap.py”示例 7.7 “mergewrap.bat” 分别是 Python 和 Windows 批处理脚本语言中外部合并工具包装器的模板。

示例 7.6 mergewrap.py

#!/usr/bin/env python
import sys
import os

# Configure your favorite merge program here.
MERGE = "/usr/local/bin/my-merge-tool"

# Get the paths provided by Subversion.
BASE   = sys.argv[1]
THEIRS = sys.argv[2]
MINE   = sys.argv[3]
MERGED = sys.argv[4]
WCPATH = sys.argv[5]

# Call the merge command (change the following line to make sense for
# your merge program).
cmd = [MERGE, '--base', BASE, '--mine', MINE, '--theirs', THEIRS,
              '--outfile', MERGED]
os.execv(cmd[0], cmd)

# Return an errorcode of 0 if the conflict was resolved; 1 otherwise.
# Any other errorcode will be treated as fatal.

示例 7.7 mergewrap.bat

@ECHO OFF

REM Configure your favorite merge program here.
SET MERGE="C:\Program Files\Funky Stuff\My Merge Tool.exe"

REM Get the paths provided by Subversion.
SET BASE=%1
SET THEIRS=%2
SET MINE=%3
SET MERGED=%4
SET WCPATH=%5

REM Call the merge command (change the following line to make sense for
REM your merge program).
%MERGE% --base %BASE% --mine %MINE% --theirs %THEIRS% --outfile %MERGED%

REM Return an errorcode of 0 if the conflict was resolved; 1 otherwise.
REM Any other errorcode will be treated as fatal.



[56] GNU diff 手册页是这样说的:退出状态 0 表示未发现差异,1 表示发现一些差异,2 表示出现问题。