#!/bin/sh # # This script parses the output of "svn diff" and uses an external # graphical difference tool like mgdiff or xxdiff to show you the # differences one file at a time. # # -- Paul Serice # SVNMGDIFF_VERSION=2.0.11 : ${MGDIFF:=/usr/bin/mgdiff} : ${TMP:=/tmp} # MSYS hijacks "/tmp", "$TMP", and "$TEMP" redirecting all to # "%LOCALAPPDATA%\Temp" which breaks native tools that don't do the # same redirection (which is almost all of them). A further # complication is that emac's (ediff) command has trouble parsing file # names under Windows that start with the drive (like "C:/..."). So, # to work around all of this, just use the current working directory # if MSYS is detected. if [ x"$MSYSTEM" != x ] ; then TMP="." fi # Because this script has to clean up temporary files, an EXIT trap # called clean_up() is established. After cleaning up the files, # clean_up() calls "exit $exit_rv". So do not exit the program via # "exit n"; instead, do "exit_rv=n; exit". This will trigger the EXIT # trap and pass n back to the shell. exit_rv=0 ## # Print usage message. If the output file is specified as "1", the # error message is printed to stdout (the usual file associated with # the "1" file descriptor); otherwise, the error message is printed to # stderr. # # @param[in] fd pseudo file descriptor usage() { u_fd="$1" msg="\ Usage: $progname [options] [-r ] [-r ] [|] Options: -d : Print debugging information including the command used to generate the diff. -g : Graphical difference program to use. -h : Print help message. -n : No gui is used. Only the log is printed to stdout. -r : Specify one or two revisions using simple names for branches and tags. For example, to specify the \"\$url/branches/foo\" branch, use \"-r foo\". To specify two revisions, use the -r flag twice or use one of \"-r :\" or \"-r ..\". The -r flag causes the script to make some guesses. This usually works, but it is recommended that you use the -d option in conjunction so you can review the diff command for accuracy. Alternatively, you can use the -u flag to specify exact URLs. -u : Specify a revision as an exact URL. To specify two revisions, use the -u flag twice. The first -u is equivalent to \"svn diff --old \". The second -u is equivalent to \"svn diff --new \". -v : Print version. -w : Walk the by diffing each version with its predecessor. Purpose: The purpose of this program is to help automate the repetitive task of running recursive differences over a Subversion (svn) source code repository. Examples: # Recursively diff the working tree with the checked out revision. svnmgdiff # Recursively diff the working tree with the latest version of # the \"foo\" branch. Notice that the full svn URL is not # specified on the command line. Instead, just specify the # name of the branch, and the script should find it in # $url/branches/foo. svnmgdiff -r foo # The same holds for tags except the script searches for # $url/tags/foo. svnmgdiff -r foo # It is also possible to specify a specific revision number. # Notice that the script also knows to look for \"trunk\" in # $url/trunk. svnmgdiff -r trunk@67 # Multiple revisions can be specified with two -r options or # as follows: svnmgdiff -r foo@67:foo@68 # To diff two specific revisions of the working tree use the # following: svnmgdiff -r 67:68 # You can take a global view of the repository in order to see # exactly what changed between any two revisions without # having to know anything other than their revision numbers. # Here, it is much easier to use the -u flag (instead of the # -r flag): svnmgdiff -u $url@67 -u $url@68 # To diff a branch against the revision that is checked out do # the following. Note that this is not the same as diffing # against the working tree: svnmgdiff -r foo:HEAD " if [ "$u_fd" -eq 1 ] ; then echo "$msg" else echo "$msg" 1>&2 fi } ## # Verify that a program is in the user's path and is executable. # # @param[in] pname program name verify_exec() { ve_pname="$1" type "$ve_pname" 1>/dev/null 2>&1 if [ $? -ne 0 ] ; then echo "$progname: Error: Unable to find executable" \ "for \"$ve_pname\"." 1>&2 exit_rv=1 exit fi } ## # Trap handler for cleaning up temporary files. clean_up() { rm -f "$tmp_file_1" rm -f "$tmp_file_2" exit $exit_rv } ## # Set "tmp_file_1" and "tmp_file_2 to be the name of two unique # temporary files. getunique() { old_umask=`umask` umask 077 tmp_file_1=`mktemp -q "$TMP/${tmp_base}XXXXXX"` tmp_file_1=`realpath "$tmp_file_1"` if [ $? -ne 0 ] ; then echo "*** $progname:" \ "Error: Unable to allocate a necessary temporary file." 1>&2 exit_rv=1 exit fi tmp_file_2=`mktemp -q "$TMP/${tmp_base}XXXXXX"` tmp_file_2=`realpath "$tmp_file_2"` if [ $? -ne 0 ] ; then echo "*** $progname:" \ "Error: Unable to allocate a necessary temporary file." 1>&2 exit_rv=1 exit fi umask $old_umask } ## # This function returns true if a particular file revision exits in # the repository. To test if the file exists in the checked out # revision, just pass in an empty string for the revision. # # This function can be called with rev=$url/trunk@67 and fname=foo. # It knows how to combine these two to produce $url/trunk/foo@67 which # is not the same as just concatenating the strings because the @67 # has to be moved from the end of rev to the end of fname. # # @param[in] rev revision # @param[in] fname file name # @return whehter a particular file revision exits file_revision_exists() { fre_rev="$1" fre_fname="$2" # Remember that 0 is true for bourne shells. fre_rv=1 # Generate the file name in the repository. This is the same as # "$fre_rev/$fre_fname" except, if $rev has a trailing revision # number attached (like "@67"), it is moved to the end of the file # name where it belongs. fre_revfname=`gen_repo_fname "$fre_rev" "$fre_fname"` # Test if the file revision is in the repository. svn ls "$fre_revfname" 1>/dev/null 2>&1 if [ $? -eq 0 ] ; then # Success. fre_rv=0 fi return $fre_rv } ## # This function will try to cat the correct revison of a file to stdout. # # This function can be called with rev=$url/trunk@67 and fname=foo. # It knows how to combine these two to produce $url/trunk/foo@67 which # is not the same as just concatenating the strings because the @67 # has to be moved from the end of rev to the end of fname. # # @param[in] rev revision of file # @param[in] fname file name # @return revision of file printed to stdout svncat() { sc_rev="$1" sc_fname="$2" sc_revfname=`gen_repo_fname "$sc_rev" "$sc_fname"` svn cat "$sc_revfname" 2>/dev/null } ## # This function takes a combined revision of the form "rev1:rev2" or # "rev1..rev2" and writes "rev1" to stdout. # # @param[in] crev combined revision # @return first revision get_first_revision() { gfr_crev="$1" "$NAWK" -v crev="$gfr_crev" 'BEGIN {\ # Split the string both ways. colon_count = split(crev, colon_parts, /:/) dotdot_count = split(crev, dotdot_parts, /\.\./) # The first separator wins so return the shorter first part. if ((colon_count >= 2) && (dotdot_count >=2)) { if (length(colon_parts[1]) < length(dotdot_parts[1])) { print colon_parts[1] } else { print dotdot_parts[1] } exit } # Only the colon mathched. if (colon_count >= 2) { print colon_parts[1] exit } # Only the dotdot matched. if (dotdot_count >= 2) { print dotdot_parts[1] exit } # No match. print crev }' } ## # This function takes a combined revision of the form "rev1:rev2" or # "rev1..rev2" and writes "rev2" to stdout. # # @param[in] crev combined revision # @return second revision get_second_revision() { gsr_crev="$1" "$NAWK" -v crev="$gsr_crev" 'BEGIN {\ # Split the string both ways. colon_count = split(crev, colon_parts, /:/) dotdot_count = split(crev, dotdot_parts, /\.\./) # The first separator wins so return the complement of the # shorter first part. if ((colon_count >= 2) && (dotdot_count >=2)) { if (length(colon_parts[1]) < length(dotdot_parts[1])) { print substr(crev, length (colon_parts[1]) + 2) } else { print substr(crev, length (dotdot_parts[1]) + 3) } exit } # Only the colon mathched. if (colon_count >= 2) { print substr(crev, length (colon_parts[1]) + 2) exit } # Only the dotdot matched. if (dotdot_count >= 2) { print substr(crev, length (dotdot_parts[1]) + 3) exit } # No match. print "" }' } ## # Exit with an error if the user passed in a URL as one of the # revisions. There is nothing wrong with svn's URL syntax, but this # script is just one in a family of scripts that all expect branches # and tags to have simple names. The script will convert from simple # names to URLs automatically. # # @param[in] revision revision error_if_url_revision() { eiur_rev="$1" # Exit with an error if the user tries to pass in svn-style URLs. # This script just uses normal-style branches and tags and # converts them to URLs. for eiur_url in "file" "http" "https" "svn" "svn+ssh" ; do eiur_tmp=`echo "$eiur_rev" | sed -e 's|${eiur_url}://||'` if [ \( x"$eiur_tmp" != x"$eiur_rev" \) ] ; then echo "***" 1>&2 echo "*** $progname: Error: URL specified. " \ "Use simple revisions (like \"trunk\")" 1>&2 echo "*** with -r or use -u instead." 1>&2 echo "***" 1>&2 exit_rv=1 exit fi done } ## # This function gets the URL that describes the current svn # sub-directory and writes it to stdout. # # @return sub-directory svn URL svn_get_subdir_url() { # Find the URL for this subdirectory. svn info | "$NAWK" '/^URL: /{print substr($0, 6)}' } ## # This function gets the URL that describes the the top-level of the # current svn project and writes it to stdout. It assumes the # canonical directory layout of "/{branches,tags,trunk}" is # being used. # # @return top-level svn URL svn_get_top_level_url() { # Get the URL from the current svn subdirectory. subdir_url=`svn_get_subdir_url` # Try to remove everything in the path that follows "branches", # "tags", or "trunk" including "branches", "tags", or "trunk" # themselves. sgtlu_rv="$subdir_url" sgtlu_rv=`echo "$sgtlu_rv" | sed -e 's|/branches/.*$||'` sgtlu_rv=`echo "$sgtlu_rv" | sed -e 's|/tags/.*$||'` sgtlu_rv=`echo "$sgtlu_rv" | sed -e 's|/trunk/.*$||'` # If nothing was removed, perhaps we are at the top of one of # "branches", "tags", or "trunk". So, try to remove these. if [ x"$sgtlu_rv" = x"$subdir_url" ] ; then sgtlu_rv=`echo "$sgtlu_rv" | sed -e 's|/branches$||'` sgtlu_rv=`echo "$sgtlu_rv" | sed -e 's|/tags$||'` sgtlu_rv=`echo "$sgtlu_rv" | sed -e 's|/trunk$||'` fi # Return what remains. echo "$sgtlu_rv" } ## # Walk all the commits associated with a particular file by # recursively calling this script in order to compare the file before # and after the commit. # # @param[in] fname file name walk_single_file() { wsf_fname="$1" # Make sure the user only passed in a single file. if [ $# -ne 1 ] ; then echo "***" 1>&2 echo "*** $progname: Error: Exactly one file must be specified: $@" 1>&2 echo "***" 1>&2 exit_rv=1 exit fi wsf_first_pass=true svn log -q "$wsf_fname" \ | "$NAWK" '/^r/ {print substr($1,2)}' \ | while read prev ; do # If $curr is set, show the log file entry for that commit. if [ x"$curr" != x ] ; then # Balance the separators. if [ x"$wsf_first_pass" = x"true" ] ; then printf "%s%s\n" \ "------------------------------------" \ "------------------------------------" wsf_first_pass="false" fi # Show the log file entry for this commit. svn log -r "$curr" | "$NAWK" 'NR > 1 {print}' # Diff the single file using the previous and current revision. if [ x"$usegui" = x"true" ] ; then svnmgdiff -r "$prev"${curr:+":$curr"} "$wsf_fname" \ 1>/dev/null 2>&1 fi fi curr="$prev" done } ## # This function converts the simple command-line revisions to formal # svn revisions and writes the result to stdout. This function # assumes the conventional trunk, branches, and tags directory # structure. Examples of how the conversion works follow: # # "" --> "" # "67" --> "$url/trunk@67" # "@67" --> "$url/trunk@67" # "trunk" --> "$url/trunk" # "foo" --> "$url/branches/foo" # "foo@67" --> "$url/branches/foo@67" # "bar" --> "$url/tags/bar" # # @param[in] simple simple revision # @return formal revision simple_to_formal() { stf_simple="$1" stf_sub=`svn_get_subdir_url` stf_top=`svn_get_top_level_url` # If a revision is not specified, use ".". if [ x"$stf_simple" = x ] ; then echo "" return fi # The first two map numeric revisions (like "@67" or "67") to the # current branch. The next two map numeric revisions to the # entire project. The next one maps to trunk, and the last two # map to branch or tag URLs. for url in "${stf_sub}$stf_simple" \ "${stf_sub}@$stf_simple" \ "$stf_top$stf_simple" \ "$stf_top@$stf_simple" \ "$stf_top/$stf_simple" \ "$stf_top/branches/$stf_simple" \ "$stf_top/tags/$stf_simple" do svn ls "$url" 1>/dev/null 2>&1 if [ $? -eq 0 ] ; then echo "$url" return fi done # Print a warning. echo "*** $progname: Warning: Unable to find revision: $stf_simple" 1>&2 # Let the user try the revision verbatim. echo "$stf_simple" } ## # Generate the file name in the repository given the path and the file # name and write the result to stdout. This is the same as # "$path/$fname" except, if $path has a trailing revision number # attached (like "@67"), it is moved to the end of the file name where # it belongs. # # @param[in] path path # @param[in] fname file name # @return file name in repository account for @REV syntax gen_repo_fname() { grf_path="$1" grf_fname="$2" "$NAWK" -v path="$grf_path" \ -v fname="$grf_fname" \ 'BEGIN { # Handle the special case where path is empty. if (length(path) == 0) { print fname } else { count = split(path, parts, /@/) if (count == 2) { print parts[1] "/" fname "@" parts[2] } else { print path "/" fname } } }' } ## # This function is the main part of the script. It runs "svn diff" # and parses the output. It writes a simple log to stdout of the # files that are NEW, OLD, or DIF (i.e. different). For the DIF # files, it invokes the graphical diff program. diff_revisions() { # # The "svn diff" command is finicky not least of which is that if # you specify an old revision without specifying a new revision, # "svn diff" default the new revision to be the same as the old # revision meaning you never get any output in this case. # # Determine which revisions have not been specified by the user. if [ x"${rev0-____UNSET____}" = x"____UNSET____" ] ; then is_rev0_unset="true" else is_rev0_unset="false" fi if [ x"${rev1-____UNSET____}" = x"____UNSET____" ] ; then is_rev1_unset="true" else is_rev1_unset="false" fi # Determine the "svn diff" command to use. if [ \( x"$is_rev0_unset" = x"true" \) \ -a \( x"$is_rev1_unset" = x"true" \) ] then svn_diff_cmd="svn diff ${1+\"\$@\"}" else svn_diff_cmd="svn diff --old \"$rev0\" --new \"$rev1\" ${1+\"\$@\"}" fi if [ x"$debug" != x ] ; then echo "$svn_diff_cmd ${1+ [where \$@ = $@]}" fi # Parse the output of "svn diff" and pass the relevant parts of # the "Index:" and "@@ R1 R2 @@" lines to the subshell. The # "print_ranges" boolean is needed because we only want to print # the first set of ranges for any file. eval "$svn_diff_cmd" \ | "$NAWK" 'BEGIN { print_ranges = 0 } /^Index: / { # For new empty files, "svn diff" seems to only # print the "Index:" line. We detect this problem # here and feed empty ranges to the "while" loop # below. if (print_ranges) { print "" print "" print_ranges = 0 } print substr($0, 8) print_ranges = 1 } /^@@ / { if (print_ranges) { print $2 print $3 print_ranges = 0 } } END { # If we run into a new empty file as the last file # in the diff make sure we print the remaining # values so that the while() loop will run one last # time. if (print_ranges) { print "" print "" print_ranges = 0 } }' \ | while read fname; \ read range_old; \ read range_new; do # Empty ranges or ranges of "-0,0" or "+0,0" mean the file is # either empty or missing. If the file is missing, it means # it is either old or new. if [ \( x"$range_old" = x \) \ -o \( x"$range_new" = x \) \ -o \( x"$range_old" = x"-0,0" \) \ -o \( x"$range_new" = x"+0,0" \) ] then # If the second revision was not specified, the diff is # against the working tree. If the file doesn't exist on # the file system, it is considered to be old because it # has been removed from the project. if [ \( x"$rev1" = x \) -a \( ! -e "$fname" \) ] ; then echo "OLD $fname" continue fi # If the second revision was specified, the diff is # against the repository. If the second revision of the # file doesn't exist in the repository, it is considered # to be old because it has been removed from the project. if [ x"$rev1" != x ] ; then file_revision_exists "$rev1" "$fname" if [ $? -ne 0 ] ; then echo "OLD $fname" continue fi fi # If the first revision was specified and doesn't exist in # the repository, it is considered to be new because the # second revision is known to be either on the file system # or in the repository. if [ x"$rev0" != x ] ; then file_revision_exists "$rev0" "$fname" if [ $? -ne 0 ] ; then echo "NEW $fname" continue fi fi # If the neither revision was specified and the file was # not part of the original check out, it is considered to # be new because the file is known to be on the file # system. if [ x"$rev0" != x ] ; then file_revision_exists "" "$fname" if [ $? -ne 0 ] ; then echo "NEW $fname" continue fi fi fi echo "DIF $fname" # Handle diffing two revisions. if [ \( x"$rev0" != x \) -a \( x"$rev1" != x \) ] ; then # Copy rev0 and rev1 to a temporary files. if [ x"$rev0" != x ] ; then svncat "$rev0" "$fname" > "$tmp_file_1" fi if [ x"$rev1" != x ] ; then svncat "$rev1" "$fname" > "$tmp_file_2" fi # Pop up the graphical diff. if [ x"$usegui" = x"true" ] ; then dr_cmd="\"$MGDIFF\"" if [ x"$QUIET_OPT" != x ] ; then dr_cmd="$dr_cmd \"$QUIET_OPT\"" fi if [ x"$TITLE_1_OPT" != x ] ; then dr_cmd="$dr_cmd \"$TITLE_1_OPT\"" dr_cmd="$dr_cmd \"$fname (rev $rev0_saved)\"" fi if [ x"$TITLE_2_OPT" != x ] ; then dr_cmd="$dr_cmd \"$TITLE_2_OPT\"" dr_cmd="$dr_cmd \"$fname (rev $rev1_saved)\"" fi # Native Windows executables need native Windows paths. # The test for native Windows executable is just if it # ends with ".exe" or ".EXE". if [ \( x"${mgdiff_basename%.exe}" != x"$mgdiff_basename" \) \ -o \( x"${mgdiff_basename%.EXE}" != x"$mgdiff_basename" \) ] then tmp_file_1=$(cygpath -w "$tmp_file_1") tmp_file_2=$(cygpath -w "$tmp_file_2") fi dr_cmd="$dr_cmd \"$tmp_file_1\" \"$tmp_file_2\"" # Evaluate the command. eval "$dr_cmd" fi else # # Here, we force all the GUIs to use a temporary file even # if they claim to be able to handle input on stdin. The # reason for this is that most of the GUIs leave temporary # files behind in /tmp when the exit unexpectedly. By # using the temporary files created by this script, they # are virtually guaranteed to be cleaned up in all cases. # if [ x"$rev0" != x ] ; then title_rev="$rev0_saved" else title_rev=`svn info "$fname" | "$NAWK" '/^Revision:/ {print $2;}'` fi # The convention that "diff" uses is that the old file is # on the left and the new file is on the right. We use # this to display the files. file_first="$tmp_file_1" file_second="$fname" if [ ! -r "$file_second" ] ; then file_second="/dev/null" fi file_second=`realpath "$file_second"` # Copy rev0 to a temporary file. if [ x"$rev0" != x ] ; then svncat "$rev0" "$fname" > "$tmp_file_1" else svncat "" "$fname" > "$tmp_file_1" fi # Pop up the graphical diff. if [ x"$usegui" = x"true" ] ; then # Generate the command. dr_cmd="\"$MGDIFF\"" if [ x"$QUIET_OPT" != x ] ; then dr_cmd="$dr_cmd \"$QUIET_OPT\"" fi if [ x"$TITLE_1_OPT" != x ] ; then dr_cmd="$dr_cmd \"$TITLE_1_OPT\"" dr_cmd="$dr_cmd \"$fname (rev $title_rev)\"" fi if [ x"$TITLE_2_OPT" != x ] ; then dr_cmd="$dr_cmd \"$TITLE_2_OPT\" \"$fname\"" fi # Native Windows executables need native Windows paths. # The test for native Windows executable is just if it # ends with ".exe" or ".EXE". if [ \( x"${mgdiff_basename%.exe}" != x"$mgdiff_basename" \) \ -o \( x"${mgdiff_basename%.EXE}" != x"$mgdiff_basename" \) ] then file_first=$(cygpath -w "$file_first") file_second=$(cygpath -w "$file_second") fi dr_cmd="$dr_cmd \"$file_first\" \"$file_second\"" # Evaluate the command. eval "$dr_cmd" fi fi done } # # Script Starts Here !!! # progname="svnmgdiff.sh" verify_exec "basename" progname=`basename "$0"` # You have to generate the temporary file names before registering the # trap handler. tmp_base="svnmgdiff-$$." getunique # Signal Trap handler to clean up temporary files in "$TMP". trap 'clean_up' HUP INT QUIT TERM EXIT if [ $? -ne 0 ] ; then echo "$progname: Unable to register signal handler." >&2 exit 1 fi # Use GNU awk if possible. for NAWK in gawk nawk awk ; do type "$NAWK" 1>/dev/null 2>&1 if [ $? -eq 0 ] ; then break fi done verify_exec "echo" verify_exec "expr" verify_exec "$MGDIFF" verify_exec "mktemp" verify_exec "$NAWK" verify_exec "realpath" verify_exec "rm" verify_exec "sed" verify_exec "svn" mgdiff_basename=`basename "$MGDIFF"` # Get svn revision(s) to use. usegui="true" while getopts "dg:hnr:u:vw" OPT ; do case "$OPT" in d) debug="true" ;; g) MGDIFF="$OPTARG" export MGDIFF ;; h) usage 1 exit ;; n) usegui="false" ;; # Allow the user to pass in two revisions at once with the # form "-r .." or "-r :". Note that # we have to check immediately if the input is an invalid # svn-style URL because the colon in things like "http://" # actually acts as a separator in the code immediately below. r) error_if_url_revision "$OPTARG" tmp=`get_first_revision "$OPTARG"` if [ x"$tmp" != x ] ; then if [ x"$rev0" = x ] ; then rev0=`simple_to_formal "$tmp"` rev0_saved="$tmp" elif [ x"$rev1" = x ] ; then rev1=`simple_to_formal "$tmp"` rev1_saved="$tmp" else echo "*** $progname: Error: too many revisions" \ "\"$rev0_saved\", \"$rev1_saved\", \"$OPTARG\"" 1>&2 exit_rv=1 exit fi fi tmp=`get_second_revision "$OPTARG"` if [ x"$tmp" != x ] ; then if [ x"$rev0" = x ] ; then rev0=`simple_to_formal "$tmp"` rev0_saved="tmp" elif [ x"$rev1" = x ] ; then rev1=`simple_to_formal "$tmp"` rev1_saved="$tmp" else echo "*** $progname: Error: too many revisions" \ "\"$rev0_saved\", \"$rev1_saved\", \"$OPTARG\"" 1>&2 exit_rv=1 exit fi fi ;; # Allow the user to specify an exact URL as a revision. Only # one revision can be specified per -u flag. u) if [ x"$OPTARG" != x ] ; then if [ x"$rev0" = x ] ; then rev0="$OPTARG" rev0_saved="$OPTARG" elif [ x"$rev1" = x ] ; then rev1="$OPTARG" rev1_saved="$OPTARG" else echo "*** $progname: Error: too many revisions" \ "\"$rev0_saved\", \"$rev1_saved\", \"$OPTARG\"" 1>&2 exit_rv=1 exit fi fi ;; v) echo "$progname: ${SVNMGDIFF_VERSION}" exit ;; # If the user specifies the -w flag and a single file, this # script will walk the commit tree and pop up the gui to show # how the single file changed after each commit. w) walk="true" ;; \?) usage 2 exit_rv=1 exit ;; esac done shift `expr $OPTIND - 1` # # Portability issues. # # QUIET_OPT -- How to prevent the gui from starting if there are no diffs. # TITLE_1_OPT -- How to override the name of file1. # TITLE_2_OPT -- How to override the name of file2. # if [ "$mgdiff_basename" = "mgdiff" ] ; then QUIET_OPT="-quit" elif [ "$mgdiff_basename" = "tkdiff" ] ; then TITLE_1_OPT="-L" TITLE_2_OPT="-L" elif [ "$mgdiff_basename" = "xdiff" ] ; then QUIET_OPT="-D" elif [ "$mgdiff_basename" = "xxdiff" ] ; then QUIET_OPT="-D" TITLE_1_OPT="--title1" TITLE_2_OPT="--title2" elif [ "$mgdiff_basename" = "WinMergeU.exe" ] ; then QUIET_OPT="/x" TITLE_1_OPT="/dl" TITLE_2_OPT="/dr" fi # Warn if this is not a svn working directory. if [ \( ! -d ".svn" \) -a \( ! -d "$1/.svn" \) ] ; then echo "*** $progname: Error: not a svn directory." 1>&2 exit_rv=1 exit fi if [ x"$walk" != x ] ; then walk_single_file "$@" else diff_revisions "$@" fi