#!/usr/bin/perl -w # # cvsfindchange # # 2006-08-15: Written by Steven J. DeRose, sderose@acm.org. use strict; my $version = "2010-09-12"; my $csize = 10; # how many like to -show around the target my $max = 0; # max num of version to look back my $pause = 0; my $quiet = 0; my $showAll = 0; my $showDiff = 0; # display diff to user at end? my $strict = 0; my $verbose = 0; # Process arguments # while ($ARGV[0]) { if (index($ARGV[0],"--")==0) { $ARGV[0] = substr($ARGV[0],1); } if ($ARGV[0] eq "-q") { $quiet = 1; } elsif ($ARGV[0] eq "-show") { $showDiff = 1; } elsif ($ARGV[0] eq "-strict") { $strict = 1; } elsif ($ARGV[0] eq "-pause") { $pause = 1; } elsif ($ARGV[0] eq "-all") { $showAll = 1; } elsif ($ARGV[0] eq "-max") { shift; $max = $ARGV[0] - 0; ($max > 0) || die "-max value '$ARGV[0]' too small.\n"; } elsif ($ARGV[0] eq "-context") { shift; $csize = int($ARGV[0] - 0); $showDiff = 1; } elsif ($ARGV[0] eq "-version") { warn "Version of $version, by Steven J. DeRose.\n"; exit; } elsif (substr($ARGV[0],0,1) eq "-") { ($ARGV[0] eq '-h' or $ARGV[0] eq '-help') || print "Unknown option '$ARGV[0]'.\n"; showUsage(); exit; } else { last; } shift; } my $tgt = $ARGV[0]; my $file = $ARGV[1]; ($file && -e $file) || die "Couldn't find input file.\n"; # Find out how many revisions CVS has for the file # my $latest = `cvs status $file | grep 'Repository revision:'`; $latest =~ s/.*: *//; $latest =~ s/^[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\).*$/\1/; chomp($latest); my @foo = split(/\./,$latest); $foo[1] += 0; ($quiet) || print "Latest revision of '$file' is '$foo[0].$foo[1]'.\n"; # Pairwise diff working backward my $found = 0; my $lastRev = "$foo[0].$foo[1]"; $lastRev =~ s/^\s*//; for (my $i=$foo[1]-1; $i>0; $i--) { my $thisRev = "$foo[0].$i"; $thisRev =~ s/^\s*//; my $cmd = "cvs diff -r $thisRev -r $lastRev"; ($quiet) || print " Trying '$cmd'\n"; my $lines = `$cmd 2>/dev/null | grep '$tgt'`; if ($strict) { # Do a tighter test my $fromlines = `$cmd 2>/dev/null | grep '^<.*$tgt'`; if ($fromlines) { $lines = ""; } # decide it's not really a hit } if ($lines) { my $found = 1; print "Change found at version $thisRev.\n"; ($quiet) || system "cvs log -r$thisRev $file"; if ($showDiff) { system "$cmd 2>/dev/null | grep --context $csize $tgt" . " | more"; print "Change found at version $thisRev.\n"; } if (!$showAll) { last; } if ($pause) { print "(press Return to continue, or 'q' and Return to quit)...\n"; my $response = readline; chomp($response); if ($response eq "q") { exit; } } } $lastRev = $thisRev; if ($max != 0) { if (--$max <= 0) { last; } } } # for (!$quiet) && (!$found) && warn "'$tgt' was not found.\n"; exit; ############################################################################### sub showUsage() { warn " =head1 Usage findCvsVersionWithChange 'target' file Searches the CVS history of the file, for which revision(s) had a diff including 'target'. =head1 Options =over =item * B<-all> Find all versions that added the change, not just the latest =item * B<-csize n> Set number of context lines to -show around the target (default 10 if you just say -show. -csize implies -show) =item * B<-max n> Maximum number of versions to go back (default unlimited) =item * B<-pause> Pause for user after each diff is found (not working) =item * B<-q> Suppress most messages. =item * B<-show> Show the actual diff that was found, not just what version. =item * B<-strict> Don't accept just any change where line contains target, but require that target be in one and not the other. =item * B<-version> Display version information and exit. =back =head1 Ownership This work by Steven J. DeRose is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License. For further information on this license, see http://creativecommons.org/licenses/by-sa/3.0/. The author's present email is sderose at acm.org. For the most recent version, see http://www.derose.net/steve/utilities/. =cut "; }