The presence of --diff-cmd
and
--diff3-cmd
options, and similarly named
runtime configuration parameters (see the section called “Config”), can lead to a
false notion of how easy it is to use external differencing (or
“diff”) and merge tools with Subversion. While
Subversion can use most of popular such tools available, the
effort invested in setting this up often turns out to be
non-trivial.
The interface between Subversion and external diff and merge
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,
[51]
the --diff-cmd
and --diff3-cmd
options were added to the Subversion command-line client so
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 diff and merge 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, etc. Subversion continues throwing all those GNU utility options at your external diff tool regardless of whether or not that program can understand those options. And that's where things get unintuitive for most users.
The key to using external diff and merge 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 which 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 diff or
merge as part of a larger Subversion operation is made
entirely by Subversion, and is affected by, among other
things, whether or not the files being operated on are
human-readable as determined by their
svn:mime-type
property. This means, for
example, that even if you had the niftiest Microsoft
Word-aware differencing or merging tool in the Universe, it
would never be invoked by Subversion so long as your versioned
Word documents had a configured MIME type that denoted that
they were not human-readable (such as
application/msword
). For more about MIME
type settings, see the section called “File Content Type”
Subversion calls external diff programs with parameters suitable for the GNU diff utility, and expects only that the external program return with a successful error code. For most alternative diff programs, only the sixth and seventh arguments—the paths of the files which 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 “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]
Example 7.2, “diffwrap.sh” and Example 7.3, “diffwrap.bat” are templates for external diff tool wrappers in the Bourne shell and Windows batch scripting languages, respectively.
Example 7.2. diffwrap.sh
#!/bin/sh # Configure your favorite diff program here. DIFF="/usr/local/bin/my-diff-tool" # Subversion provides the paths we need as the sixth and seventh # parameters. LEFT=${6} RIGHT=${7} # Call the diff command (change the following line to make sense for # your merge program). $DIFF --left $LEFT --right $RIGHT # Return an errorcode of 0 if no differences were detected, 1 if some were. # Any other errorcode will be treated as fatal.
Example 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 sixth and seventh REM parameters. SET LEFT=%6 SET RIGHT=%7 REM Call the diff command (change the following line to make sense for REM your merge 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 return with a successful error code and that the full file contents which 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, you 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.
Example 7.4, “diff3wrap.sh” and Example 7.5, “diff3wrap.bat” are templates for external merge tool wrappers in the Bourne shell and Windows batch scripting languages, respectively.
Example 7.4. diff3wrap.sh
#!/bin/sh # Configure your favorite diff3/merge program here. DIFF3="/usr/local/bin/my-merge-tool" # Subversion provides the paths we need as the ninth, tenth, and eleventh # parameters. MINE=${9} OLDER=${10} YOURS=${11} # Call the merge command (change the following line to make sense for # your merge program). $DIFF3 --older $OLDER --mine $MINE --yours $YOURS # 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.
Example 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 ninth, tenth, and eleventh REM parameters. But we only have access to nine parameters at a time, so we REM shift our nine-parameter window 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.