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

使用外部差异和合并工具

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

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

[Note] 注意

在更大的 Subversion 操作中,何时触发上下文双向或三向 diff 的决定完全由 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 预期的格式。以下部分将介绍这些期望的具体内容。

外部 diff

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

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

示例 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.



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

TortoiseSVN 官方中文版 1.14.7 发布