Car insurance
Home > Development, English > Subversion diff with vimdiff improved

Subversion diff with vimdiff improved

February 9th, 2012 Leave a comment Go to comments

Almost three years ago, I published a bash wrapper function for the svn command on this blog. This shell function allows to use external tools when calling svn diff, for example colordiff, Apple’s FileMerge on OS X or vimdiff.

Since then, I improved the file by using less forks with type -fp, added quotes where I encountered filenames with spaces, etc. However, it is basically still the same as back then. Here, I left out the parts about the other diff functions so you get an idea how this works without requiring you to read the other blog post:

function svn() {
    local svn=$(type -fp svn)
    case "$1" in
        diff-vim)
            shift;
            $svn diff --diff-cmd $HOME/libexec/svndiff -x vimdiff "$@"
            ;;
        *)
            $svn "$@"
            ;;
    esac
}

This approach has one drawback for practical use of the svn diff-vim command: sometimes, svn does not use the original file name, but creates a temporary file first. This hinders editing of such a diff using vimdiff, as edits can not be saved easily to the actual specified file.

[Side note: this happens as soon as a file has a svn:keywords property and keywords such as $Id$ were expanded. In this case, the diff does not use the original file path but a temporary file to avoid changes introduced by keyword expansion. (details)]

This behavior became annoying for me, so I improved the original approach and present the new version below. This time, I am using svn cat to manually retrieve the older version of the file to a temporary location and then call vimdiff with the file specified and the older version. This became more complex as I also wanted to support the -r syntax to specify the revision to compare against:

function svn() {
    local svn=$(type -fp svn)
    case "$1" in
        diff-vim)
            shift;
 
            local file=""
            local rev="-rBASE"
 
            while [ $# -gt 0 ]; do
                case "$1" in
                    --)
                        break;
                        ;;
                    -r*)
                        rev="$1"
                        if [ "$1" == "-r" ]; then
                            shift
                            rev="-r$1"
                        fi
                        ;;
                    --revision)
                        shift
                        rev="-r$1"
                        ;;
                    -*)
                        echo "svn: invalid option: $1"
                        return 1
                        ;;
                    *)
                        if [ -n "$file" ]; then
                            echo "svn: diff-vim works with one single filename only" >&2
                            return 1
                        fi
                        file="$1"
                        ;;
                esac
                shift;
            done;
 
            local tmp=$(mktemp -t svn-diff-vim || return 1).${file##*.}
            $svn cat $rev "$file" > $tmp || return 1
            chmod a-w $tmp || return 1
            vimdiff "$file" $tmp || return 1
            rm -f $tmp || return 1
            ;;
        *)
            $svn "$@"
            ;;
    esac
}

A known drawback of this approach is that you can no longer use the -c option to view changes introduced in a specific revision, or specify a peg revision with the file@rev syntax. For me, these aren’t showstoppers, as the most common use case for me is to revert only some parts of a file. Using the presented function above, this is quite easy with svn diff-vim and the vim commands moving changes from one file to the other: do for diff-obtain and dp for diff-put.

You can also get the full file with all implemented diff commands as a download. This file is meant to be sourced from your .bashrc. You can share and use this script as you will, it is hereby placed into Public Domain.

As always, I am happy to hear about your experiences with this script, suggestions or better solutions in the comments below!

Categories: Development, English Tags: , , ,
  1. No comments yet.
  1. No trackbacks yet.