The interface between Subversion and external two- and three-way
differencing tools harkens back to a time when Subversion's only contextual
differencing capabilities were built around invocations of the GNU diffutils
toolchain, specifically the diff and
diff3 utilities. To get the kind of behavior Subversion
needed, it called these utilities with more than a handful of options and
parameters, most of which were quite specific to the utilities. Some time
later, Subversion grew its own internal differencing library, and as a
failover mechanism, the --diff-cmd and
--diff3-cmd options were added to the Subversion
command-line client so that users could more easily indicate that they
preferred to use the GNU diff and diff3 utilities instead of the newfangled
internal diff library. If those options were used, Subversion would simply
ignore the internal diff library, and fall back to running those external
programs, lengthy argument lists and all. And that's where things remain
today.
It didn't take long for folks to realize that having such easy configuration mechanisms for specifying that Subversion should use the external GNU diff and diff3 utilities located at a particular place on the system could be applied toward the use of other differencing tools, too. After all, Subversion didn't actually verify that the things it was being told to run were members of the GNU diffutils toolchain. But the only configurable aspect of using those external tools is their location on the system—not the option set, parameter order, and so on. Subversion continues to throw all those GNU utility options at your external diff tool regardless of whether that program can understand those options. And that's where things get unintuitive for most users.
The key to using external two- and three-way differencing tools (other than GNU diff and diff3, of course) with Subversion is to use wrapper scripts, which convert the input from Subversion into something that your differencing tool can understand, and then to convert the output of your tool back into a format that Subversion expects—the format that the GNU tools would have used. The following sections cover the specifics of those expectations.
|
注意 |
|---|---|
|
The decision on when to fire off a contextual two- or three-way diff as part
of a larger Subversion operation is made entirely by Subversion and is
affected by, among other things, whether the files being operated on are
human-readable as determined by their |
Subversion 1.5 introduces interactive resolution of conflicts (described in
第 4.5 节 “解决冲突(合并别人的修改)”), and one of the options provided
to users is the ability to launch a third-party merge tool. If this action
is taken, Subversion will consult the merge-tool-cmd
runtime configuration option to find the name of an external merge tool and,
upon finding one, will launch that tool with the appropriate input files.
This differs from the configurable three-way differencing tool in a couple
of ways. First, the differencing tool is always used to handle three-way
differences, whereas the merge tool is employed only when three-way
difference application has detected a conflict. Second, the interface is
much cleaner—your configured merge tool need only accept as
command-line parameters four path specifications: the base file, the
“theirs” file (which contains upstream changes), the
“mine” file (which contains local modifications), and the path
of the file where the final resolved contents should be stored.
Subversion calls external diff programs with parameters suitable for the GNU diff utility, and expects only that the external program will return with a successful error code. For most alternative diff programs, only the sixth and seventh arguments—the paths of the files that represent the left and right sides of the diff, respectively—are of interest. Note that Subversion runs the diff program once per modified file covered by the Subversion operation, so if your program runs in an asynchronous fashion (or is “backgrounded”), you might have several instances of it all running simultaneously. Finally, Subversion expects that your program return an error code of 1 if your program detected differences, or 0 if it did not—any other error code is considered a fatal error. [52]
例 7.2 “diffwrap.py” and 例 7.3 “diffwrap.bat” are templates for external diff tool wrappers in the Python and Windows batch scripting languages, respectively.
例 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.
Subversion calls external merge programs with parameters suitable for the GNU diff3 utility, expecting that the external program will return with a successful error code and that the full file contents that result from the completed merge operation are printed on the standard output stream (so that Subversion can redirect them into the appropriate version-controlled file). For most alternative merge programs, only the ninth, tenth, and eleventh arguments, the paths of the files which represent the “mine,” “older,” and “yours” inputs, respectively, are of interest. Note that because Subversion depends on the output of your merge program, your wrapper script must not exit before that output has been delivered to Subversion. When it finally does exit, it should return an error code of 0 if the merge was successful, or 1 if unresolved conflicts remain in the output—any other error code is considered a fatal error.
例 7.4 “diff3wrap.py” and 例 7.5 “diff3wrap.bat” are templates for external merge tool wrappers in the Python and Windows batch scripting languages, respectively.
例 7.4. diff3wrap.py
#!/usr/bin/env python import sys import os # Configure your favorite diff program here. DIFF3 = "/usr/local/bin/my-merge-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 merge command (change the following line to make sense for # your merge 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 diff3/merge program here. SET DIFF3="C:\Program Files\Funky Stuff\My Merge 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 merge command (change the following line to make sense for REM your merge 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.