Vim: source control diffs

Ovid on 2008-11-28T15:51:56

Quite often I want to get a diff of the file I'm working on, but as usual, I hate leaving my editor for something I script. So I first wrote this simple Perl program (adjust to taste):

#!/usr/bin/env perl

use strict;
use warnings;

my $file = shift || die "You must supply a filename to $0";

unless ( -f $file ) {
    die "File ($file) does not exist";
}

my @log = qx(svn log "$file" --stop-on-copy);
my $branch_name = branch_name();

my $pipe = qr/[[:space:]]+\|[[:space:]]+/;

splice @log, 0, 3;   # discard first revision
while (local $_ = shift @log) {
    next unless /\Ar([[:digit:]]+)$pipe([[:word:]]+)$pipe(\S+)\s+\S+/;
    my ( $revision, $user, $datetime ) = ( $1, $2, $3 );
    print "$revision - $user - $datetime - ";
    shift @log;
    my $message = shift @log;

    # we often prefix commit messages with "$branch_name: "
    $message =~ s/^\s*$branch_name(?::\s*)?//;  # trim it if it's there
    print $message;
}

sub branch_name {
    chomp(my @svn_info = qx(svn info .));
    my ($branch_name) = $svn_info[1] =~ m{/([^/]+)\z};
    return $branch_name;
}

That produces a summarized output of the subversion log history:

13015 - ovid - 2008-11-28 - Made changes to the fixture and test file
13014 - jplindstom - 2008-11-28 - Made a change to the Assert Result Test
13012 - danquayle - 2008-11-28 - Fixed misspeeling

And this vim function:

function! SourceDiff()
    let filename = bufname("%")
    let command  = 'perl script/svn_revisions.pl "'.filename.'"'
    let result   = split( system(command), "\n" )

    if empty(result)
        echomsg("No past revisions for " . filename)
        return
    endif

    " get the list of files
    let revision = PickFromList('revision', result)

    if strlen(revision)
        let items = split(revision, " ")
        execute '!svn diff -r' . items[0] . ' "' . filename .'" | less'
    endif
endfunction

(Note that this relies on my PickFromList function)

When I bind that to a key (or keystroke), it gives me output like this:

Choose a revision:
1: 13015 - ovid - 2008-11-28 - Made changes to the fixture and test file
2: 13014 - jplindstom - 2008-11-28 - Made a change to the Assert Result Test
3: 13012 - danquayle - 2008-11-28 - Fixed misspeeling

I hit the appropriate number of the revision I want to diff against, and voila, a quick subversion diff in my vim session.


Make it work at any directory.

motoster on 2008-12-22T20:17:28

Hello, Ovid.

The fact that your script only works if the cwd is file's directory kind of frustate me, as I normally work on the root dir of the project.

Here is a patch to make it work wherever the cwd is.

--- svn_revisions.pl.ovid    2008-12-22 10:59:09.000000000 -0400
+++ svn_revisions.pl    2008-12-22 11:12:55.000000000 -0400
@@ -2,6 +2,7 @@

use strict;
use warnings;
+use File::Basename qw(dirname);

my $file = shift || die "You must supply a filename to $0";

@@ -28,7 +29,8 @@
}

sub branch_name {
-    chomp(my @svn_info = qx(svn info .));
+    my $dir = dirname($file);
+    chomp(my @svn_info = qx(svn info $dir));
     my ($branch_name) = $svn_info[1] =~ m{/([^/]+)\z};
     return $branch_name;
}

Re:Make it work at any directory.

Ovid on 2008-12-23T12:07:31

Ah, sweet. I've a nasty habit of coding stuff for my working habits.