Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 1 Jun 2002 04:17:54 +0300
From:      Giorgos Keramidas <keramida@FreeBSD.org>
To:        Matt Dillon <dillon@FreeBSD.org>
Cc:        freebsd-hackers@FreeBSD.org
Subject:   Adding diffs to commit-mail on the fly
Message-ID:  <20020601011754.GA4357@hades.hell.gr>

next in thread | raw e-mail | index | archive | help
Hi Matt and everyone,

I remember that Matt had mentioned adding diffs of the commits to the
commit-mail, which would make it possible to quickly view what changed
without leaving the MUA and manually searching for the proper CVS
diff/rdiff incantation to view the diff.

The idea was resting at the back of my mind, and tonight I hacked this
Perl script that seems to mostly do what I want, when I pipe a single
message to it's STDIN in mbox format.  Given a script in Perl or awk
or whatever that works like this one, how would you all integrate this
with procmail to let it filter all the commit mail of FreeBSD?

The code of the script is a bit clumsily written, but here it is for
anyone who cares to play with it.  You can see a sample of the output
of this script, in mbox file format, at:

http://www.FreeBSD.org/~keramida/files/freebsd-rdiff.txt

- Giorgos

======================================================================
#!/usr/bin/perl
#
# A filter that copies all the lines from stdin unchanged, until it
# matches the magic FreeBSD commit-mail line that says files and
# revisions follow.  When that line is matched, do our stuff until the
# line that matches /^$/; when all file changes are finished.

# If this is set to non-zero, then trim EOL whitespace from the
# lines of the commit log before copying it to STDOUT.
$mail_delete_eol_spaces = 1;

# Where would I find the CVSROOT that mirrors the FreeBSD repository?
$cvs_root_dir = "/home/ncvs";

# Copy all the lines of STDIN to STDOUT, until we find the part of the
# message that contains the file change list.  Keep the changelist
# lines in an array, and then after the changelist is copied verbatim
# to STDOUT too, parse the changelist and call cvs(1) as needed to
# view diffs.

$in_changelist = 0;		# set when we're reading changelist lines
@changeset = ();		# start with an empty changeset array
while (defined($line = <STDIN>)) {
    chomp $line;
    if ($mail_delete_eol_spaces) {
	$line =~ s/\s*$//;
    }

    if (($in_changelist eq 0) && ($line =~ m/^  Revision  Changes    Path$/)) {
	print "$line\n";
	$in_changelist = 1;
    } elsif ($in_changelist ne 0) {
	if ($line =~ m/^$/) {
	    $in_changelist = 0;
	    &show_changeset(\@changeset);
	} else {
	    unless ($line =~ m/^\s*$/) {
		$line =~ s/^\s+//;
		push (@changeset,$line);
	    }
	}
	print "$line\n";
    } else {
	print "$line\n";
    }
}

sub show_changeset {
    my $aref = shift;

    die "No changeset" unless (defined($aref));

    print "\n";
    print "--- patch begins ---\n";
    foreach $line (@$aref) {
	($rev, $added, $deleted, $rcsfile) = &valid_changeline($line);
	$lastrev = &rev_previous($rev);
	$pipecmd = "cvs -q -d ${cvs_root_dir} rdiff -u -r$lastrev -r$rev $rcsfile";
	open(CVSDIFF, "${pipecmd}|") or die "Could not open input pipe from CVS";
	while (defined($in = <CVSDIFF>)) {
	    chomp $in;
	    print "$in\n";
	}
	close(CVSDIFF);
    }
    print "--- patch ends ---\n";
}

sub valid_changeline {
    my $line = shift;
    die "No line passed" unless (defined($line));

    ($rev, $added, $deleted, $rcsfile) = split /\s+/, $line;
    die "Invalid revision \[$rev\]"
	unless &valid_rcsrev($rev);
    die "Invalid number of added lines \[$added\]"
	unless &valid_rcslines($added);
    die "Invalid number of deleted lines \[$deleted\]"
	unless &valid_rcslines($deleted);
    die "No rcsfile specified in changeline \[$line\]"
	unless (defined($rcsfile));
    return ($rev, $added, $deleted, $rcsfile);
}

sub valid_rcsrev {
    my $rev = shift;
    die "No RCS version to validate"
	unless (defined($rev));

    my @foo = split /\./, $rev;
    return 0 if ($#foo == -1);

    foreach $k (@foo) {
	return 0 if ($k = m/\D/);
    }
    return 1;
}

sub valid_rcslines {
    my $num = shift;
    die "No RCS lines to validate"
	unless (defined($num));

    return ($num =~ m/^[+-]?\d+$/);
}

sub rev_previous {
    my $rev = shift;
    die "No revision specified" unless (defined($rev));

    die "Invalid revision number \[$rev\]" unless &valid_rcsrev($rev);

    @parts = split /\./, $rev;
    if (($#parts % 2) eq 0) {
	# This is a branch revision number.
	# Just delete the last part to find the branchpoint address.
	$#parts--;
    } else {
	# This is a real revision number.  Attempt to either reduce
	# the last numeric value, or remove the two last parts (when
	# possible) to find the last revision before a branch point
	# when the last part is equal to 0.
	$last = $parts[$#parts];
	if ($last gt 1) {
	    $parts[$#parts]--;
	} elsif ($#parts gt 1) {
	    $#parts -= 2;
	}
    }

    return "" . join('.', @parts) . "";
}
======================================================================

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020601011754.GA4357>