How to set kdiff3 as merge tool for SVN

22,886

Solution 1

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.

Solution 2

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

Solution 3

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

Solution 4

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.

Share:
22,886

Related videos on Youtube

gruszczy
Author by

gruszczy

I lead and manage the Google Assistant on Speakers team.

Updated on July 09, 2022

Comments

  • gruszczy
    gruszczy almost 2 years

    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?

  • hlovdal
    hlovdal over 11 years
    To also work with file names containing spaces, use kdiff3 "$1" "$2" "$3" -o "$4".
  • PapaFreud
    PapaFreud over 11 years
    For me it starts kdiff3 ok, but it doesn't seem to work. I do a svn merge, when there's a conflict I do "launch (l)" and it opens kdiff3 but then the merge doesn't seem to work. It saves to .svn/tmp but the svn merge command just repeats itself asking me again what to do. EDIT: OK, my bad. I does work, but you need to do first a (l) launch, do the merge, and then select (r) resolved.
  • Charles Duffy
    Charles Duffy over 11 years
    That script won't be able to handle filenames with spaces without a lot of fixes to its quoting -- changing references to $older to be "$older" and the like.
  • Adam Spiers
    Adam Spiers over 10 years
    Maybe you found it here?
  • Marco Righele
    Marco Righele about 10 years
    I'd add that changing /etc/subversion/config will change the default for every user. If you want to change just your default (or you don't have permission to change the global configuration file), you can edit $HOME/.subversion/config (in unix) or %appdata%\subversion\config (in Windows). In that case don't forget to put the option under the [helpers] section.
  • ronnyfm
    ronnyfm almost 10 years
    Tried it, it didn't work on Mavericks, open kidff but without the files.
  • blong
    blong over 8 years
    In using this solution to resolve a merge-conflict, kdiff3 seemed to work great. Now, I'd like to set kdiff3 as my diff-tool for SVN, but using diff-cmd = kdiff3ForSvn in my svn config file doesn't seem to perform the right action (it looks like it's attempting to auto merge). Has anyone tried using kdiff3 for only diff viewing?
  • Kenny Tai Huynh
    Kenny Tai Huynh over 8 years
    @yvoyer: could you please help me?! I also follow the guide as your comment. But I type the command: kdiff3. it just opens the kdiff3 app. I also tried: svn diff kdiff3 svnpath. It also does not work. Could you please share the command that svn can check and merge with kdiff3?! Thanks so much!
  • yvoyer
    yvoyer over 8 years
    @KennyTaiHuynh, sorry its been a while that I've worked with svn, and kdiff3 for that mather. My old pcs with this information are long gone.
  • Kenny Tai Huynh
    Kenny Tai Huynh over 8 years
    @yvoyer: That's fine. Thanks for your reply. :)
  • Shuman
    Shuman over 6 years
    Can I use this script for svn update conflicts? It will have different set of files: file.mine file.rOLDREV file.rNEWREV