How to set kdiff3 as merge tool for SVN

23.4k views Asked by At

I would like to be able to resolve conflicts using kdiff3, when SVN notifies me about the conflict. How can I set it as a default tool for this?

4

There are 4 answers

5
Jon Ander Ortiz Durántez On BEST ANSWER

Go to the Subversion configuration file (/etc/subversion/config or ~/.subversion/config), and set merge-tool-cmd variable with your favourite tool:

### Set merge-tool-cmd to the command used to invoke your external
### merging tool of choice. Subversion will pass 4 arguments to
### the specified command: base theirs mine merged
# merge-tool-cmd = merge_command

Although there is a problem with kdiff3 which does not support four plain arguments (SVN passes four plain arguments to kdiff3, and it does not work), so it is usually called with a simple script to translate the arguments, e.g., "kdiff3caller":

#!/bin/sh
kdiff3 "$1" "$2" "$3" -o "$4"

This kdiff3 problem and solution is explained here.

0
Adam On

A solution that is shorter and works with later versions of SVN (tested on SVN 1.7.7):

Create a script ~/svn-merge-kdiff

#!/bin/bash

# Useful when something fails
LOG=~/svn-merge-kdiff-last-run.log
echo "arguments passed to $0: $@" > $LOG

# Now, don't think you will get the $1, $2, etc... by referencing.
# At first, you have to copy it to an array
for i in $@; do
    args=( ${args[@]} $i )
done

echo "parsed args" >> $LOG
for i in ${args[@]}; do
    echo $i >> $LOG
done

# I keep it in case something changes
if [ "${args[1]}" == "-m" ] && [ "${args[2]}" == "-L" ] && [ "${args[3]}" == ".mine" ];then
    command="kdiff3 --L1 ${args[5]} --base ${args[9]} --L2 ${args[7]} ${args[10]} --L3 ${args[3]} ${args[8]} -o merged"
    $command
    if [[ $? -ne 0 ]]; then
        echo "$command failed" >> $LOG
        exit 1
    fi

    # You have to do this, otherwise after the merge you will see... empty file(?)
    cat merged

    rm merged
    exit 0
fi

exit -1

Bind it to svn in ~/.subversion/config

diff3-cmd = ~/svn-merge-kdiff
0
chetto On

The script from yvoyer's answer works great for me, and I am using SVN 1.4. I think the previous answer from Jon Ander Ortiz Durántez works for SVN 1.5 and greater, and this script works for SVN versions prior to 1.5. It appears there were changes to --diff-cmd & --diff3-cmd for version 1.5. Compare scripts in the following 2 SVN docs to see some differences:

Michael Bradley's script is really useful since now if I get a conflict during svn update it kicks into kdiff3 instead of barfing all over the file with the ">>>>>>>>" conflict markers which are so difficult to resolve if you have complex conficts. The diff3-cmd works for both merge and update.

I add diff3-cmd = /usr/local/bin/svndiff3 to ~/.subversion/config (or use --diff3-cmd on the cmdline) since I wrote my own script to send svn diff to sdiff and is specified by --diff-cmd.

This script is posted at yolinux, and a slightly modified version (that handles auto-merging) is posted here Jawspeak.

6
yvoyer On

I found this script somewhere I can't remember. but the author is Michael Bradley.

My answer is similar to Jon Ander Ortiz Durántez's answers. So if his answer doesn't work, you have a backup. I once tried something like he suggested, but it always output some error with the parameters until I found this scripts which resolved everything.

Create a script file and set diff-cmd = /path/to/script.sh in your ~/.subversion/config

#!/bin/bash

# Return an errorcode of 0 on successful merge, 1 if unresolved conflicts
# remain in the result.  Any other errorcode will be treated as fatal.
# Author: Michael Bradley

#NOTE: all output must be redirected to stderr with "1>&2" as all stdout output is written to the output file

# Must be called by subversion in "~/.subversion/config" file
# Add config : "diff-cmd = /path/to/script/myKdiff3.sh" 

VDIFF3="kdiff3"
DIFF3="diff3" 
DIFF="kdiff3"  

promptUser ()
{
    read answer
    case "${answer}" in

        "M"         ) 
        echo "" 1>&2
        echo "Attempting to merge ${baseFileName} with ${DIFF}" 1>&2
        $VDIFF3 $older $mine $theirs --L1 $labelOlder --L2 $labelMine --L3 $labelTheirs -o $output 1>&2
        bLoop=1
        if [ -f $output ]; then
            if [ -s $output ]; then
                #output succesfully written
                bLoop=0
            fi
        fi
        if [ $bLoop = 0 ]; then
            cat $output
            rm -f $output
            exit 0
        else
            echo "Merge failed, try again" 1>&2
        fi

        ;;

        "m"         ) 
        echo "" 1>&2
        echo "Attempting to auto-merge ${baseFileName}" 1>&2
        diff3 -L $labelMine -L $labelOlder -L $labelTheirs -Em $mine $older $theirs > $output
        if [ $? = 1 ]; then
            #Can't auto merge
            rm -f $output
            $VDIFF3 $older $mine $theirs --L1 $labelOlder --L2 $labelMine --L3 $labelTheirs -o $output --auto 1>&2
            bLoop=1
            if [ -f $output ]; then
                if [ -s $output ]; then
                    #output succesfully written
                    bLoop=0
                fi
            fi
            if [ $bLoop = 0 ]; then
                cat $output
                rm -f $output
                exit 0
            else
                echo "Merge failed, try again" 1>&2
            fi
        else
            #We can automerge, and we already did it
            cat $output
            rm -f $output
            exit 0
        fi
        ;;

        "diff3" | "Diff3" | "DIFF3"  )
        echo "" 1>&2
        echo "Diffing..." 1>&2
        $VDIFF3 $older $mine $theirs --L1 $labelOlder --L2 $labelMine --L3 $labelTheirs 1>&2
        ;;

        "diff" | "Diff" | "DIFF"  )
        echo "" 1>&2
        echo "Diffing..." 1>&2
        $DIFF $mine $theirs -L $labelMine -L $labelTheirs 1>&2
        ;;

        "A" | "a"   ) 
        echo "" 1>&2
        echo "Accepting remote version of file..." 1>&2
        cat ${theirs}
        exit 0
        ;;

        "I" | "i"   ) 
        echo "" 1>&2
        echo "Keeping local modifications..." 1>&2
        cat ${mine}
        exit 0
        ;;

        "R" | "r"   ) 
        echo "" 1>&2
        echo "Reverting to base..." 1>&2
        cat ${older}
        exit 0
        ;;

        "D" | "d"   ) 
        echo "" 1>&2
        echo "Runnig diff3..." 1>&2
        diff3 -L $labelMine -L $labelOlder -L $labelTheirs -Em $mine $older $theirs
        #Exit with return vaule of the diff3 (to write out files if necessary)
        exit $?
        ;;

        "S" | "s"   ) 
        echo "" 1>&2
        echo "Saving for later..." 1>&2
        cat ${mine}
        #Exit with return vaule of 1 to force writting of files
        exit 1
        ;;

        "Fail" | "fail" | "FAIL"   ) 
        echo "" 1>&2
        echo "Failing..." 1>&2 
        exit 2
        ;;

        "H" | "h"   ) 
        echo "" 1>&2
        echo "USAGE OPTIONS:" 1>&2 
        echo "  [A]ccept    Accept $labelTheirs and throw out local modifications" 1>&2
        echo "  [D]efault   Use diff3 to merge files (same behavior as vanilla SVN)" 1>&2
        echo "  [Fail]      Kills the command (not suggested)" 1>&2
        echo "  [H]elp      Print this message" 1>&2
        echo "  [I]gnore    Keep your locally modified version as is" 1>&2
        echo "  [M]erge     Manually merge using ${VDIFF3}" 1>&2
        echo "  [m]erge     Same as "M" but attempts to automerge if possible" 1>&2
        echo "  [R]evert    Revert to base version (${labelOlder})" 1>&2
        echo "  [S]ave      Same as 'I' but writes out rold, rnew, and rmine files to deal with later" 1>&2
        echo "  [diff]      Type 'diff' to diff versions $labelMine and $labelTheirsthe before making a descision" 1>&2
        echo "  [diff3]     Type 'diff3' to diff all three versions before making a descision" 1>&2
        echo "" 1>&2
        ;;

        *   ) 
        echo "'${answer}' is not an option, try again." 1>&2
        ;;
    esac 
}

if [ -z $2 ]
then
    echo ERROR: This script expects to be called by subversion
    exit 1
fi

if [ $2 = "-m" ]
then
    #Setup vars
    labelMine=${4}
    labelOlder=${6}
    labelTheirs=${8}
    mine=${9}
    older=${10}
    theirs=${11}
    output=${9}.svnDiff3TempOutput
    baseFileName=`echo $mine | sed -e "s/.tmp$//"`

    #Prompt user for direction
    while [ 1 ]
    do
        echo "" 1>&2
        echo "${baseFileName} requires merging." 1>&2 
        echo "" 1>&2
        echo "What would you like to do?" 1>&2
        echo "[M]erge [A]ccept [I]gnore [R]evert [D]efault [H]elp" 1>&2 
        promptUser
    done
else
    L="-L"         #Argument option for left label
    R="-L"         #Argument option for right label
    label1=$3       #Left label
    label2=$5       #Right label
    file1=$6        #Left file
    file2=$7        #Right file

    $DIFF $file1 $file2 $L "$label1" $L "$label2" &
    #$DIFF $file1 $file2 &
    #wait for the command to finish
    wait
fi
exit 0