info-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

cvdiff - branch tracing log reporter [WAS: Re: Tools to generate report


From: Robert Bresner
Subject: cvdiff - branch tracing log reporter [WAS: Re: Tools to generate report from logs?]
Date: Tue, 09 Jan 2001 13:20:05 -0600

Righto.

cvdiff -h for help. 

Basically, 
        cvdiff -rTAG 
will generate a report of every checkin of every file 
since that TAG was created. 

It follows branches... So, if 
        foo.c
was TAG'd at 1.1, then eventually made
its way to 1.2.2.100, cvdiff will trace
1.1 -> 1.2 -> 1.2.2.1 ... 1.2.2.100
and ignore the revisions that are not a 
part of the branch. 

That is what the script was originally written for,
creating reports of logs along a branch.

Hope it helps.

Rob
#!/usr/bin/perl -w

# cvdiff
# attempt two to get what I want from cvs diff/cvs log

# Copyright(c) 1999-2001
# by Robert Bresner
# for Open Link Financial, Inc
# Insert GNU license here.
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#

use strict;
use FileHandle;
use IO::File;

my( $Date1, $Date2, $DCtr);             # -D args
my( $Rev1, $Rev2, $RCtr);               # -r args
my $UseAreaprops = 0;                   # -R flag (sets rev2)
my $Recursive = 1;                      # -l flag
my $NewFlag = 0;                        # -N flag
my $Quiet = 0;                          # -q flag
my $verbose = 0;                        # -v flag
my @Files;                              # list of files to perform cvdiff on
my $NoTrace = 0;                        # Do not trace the log revision tree
my $ModRevs = 0;                        # Show revisions per mod, not per file
my %ModRevs;                            # Keep track of Module revisions for 
ModRev
my $ShortOutput = 0;                    # flag, module/file revision list only.
my( $Index, $LRev1, $LRev2, $Diff, $Module );  # These became global for -S 
mode...
my( $InputFile );                       # Use input file instead of cvs command

STDOUT->autoflush(1);
GetArgs();
Go();
PrintModRevs();

################################################################################

sub PrintModRevs
{
   return unless $ModRevs;
   return if $ShortOutput;
   foreach my $key( keys %ModRevs )  {
      print "$key => $ModRevs{$key}\n";
   }
}

sub Go
{
   # Ok, first, for each file, get revision numbers and put in ascending order.
   my $Cmd = "cvs -q diff";
   $Cmd .= " -N" if $NewFlag;
   $Cmd .= " -l" unless $Recursive;
   $Cmd .= " -r $Rev1" if $Rev1;
   $Cmd .= " -r $Rev2" if $Rev2;
   $Cmd .= " -D $Date1" if $Date1;
   $Cmd .= " -D $Date2" if $Date2;
   $Cmd .= " @Files" if @Files;

   # If a file hasn't changed from tag1 to tag2, then cvs output will be empty,
   # and this file will be skipped.

   # Diffs are done first to get the two revisions that I will trace.
   #    >cvs-1.10.8.exe diff -rMY_TAG buildsql 
   #    Index: buildsql
   #    ===================================================================
   #    RCS file: /big2/cvs/master/olf/bin/buildsql,v
   #    retrieving revision 1.32
   #    retrieving revision 1.291
   # The two retrieving revision lines are all I need (unfortunate that I have 
to
   #  get them through a cvs diff call, but what are ya gonna do?)
   # Once I have the revision numbers (the from and to of the log trace) I can 
call
   #  cvs log.

   my $FH;
   if( $InputFile )  {
      # An input file can be created by doing a full cvs log on a file dir or 
mod,
      $FH = new IO::File( "$InputFile", 'r' )
         or die "Could not open input file $InputFile: $!\n";
   }
   else  {
      $FH= new FileHandle;
      open( $FH, "$Cmd|" )
         or die "Could not open pipe '$Cmd|': $!\n";
   }
   binmode $FH;
   while( <$FH> )  {
      /^Index: (.*)$/ and $Index = $1;
      /^retrieving revision (.*)$/ and not $LRev1 and $LRev1 = $1;
      /^retrieving revision (.*)$/ and $LRev1 and $LRev2 = $1;
      /^RCS file:\s+(.*)\,v/ and do  {
         my $D1 = $1;
         if( $D1 =~ /.*?\/olf\// )  {
            ( $Module = $D1 ) =~ s/.*?\/olf\///;
            $Module =~ s/^(.*)\/.*/$1/;         # remove filename from module 
name
         }  else  {
            $Module = '';
         }
         print STDOUT "MODULE: $Module\n" if $verbose;
      };
      /^diff -r\d/ and do  {
         $LRev2 = "NONE" unless $LRev2;
         DiffThisFile($Index, $LRev1, $LRev2 );
         $Index = $LRev1 = $LRev2 = undef;
      };
   }
}


sub DiffThisFile
{
   # Not _REALLY_ a diff, per se. 
   # I got the 2 revision numbers ( the from and the to, if you will )
   #  from the diff above.
   # First I want to make sure the From #.# precedes the To #.# (damn users)
   #  then I want to trace the From #.# to the To #.#, reporting the logs
   #  of each step.

   my( $Index, $LRev1, $LRev2 ) = @_;
   # print "INDEX: $Index  LREV1: $LRev1  LREV2: $LRev2\n";# if $verbose;
   if( $LRev2 eq 'NONE' and not $ShortOutput )  {
      print "\nIndex: $Index\n" . '=' x 60 . "\nretrieving revision 
$LRev1\nretrieving revision Unknown\n"
         unless $ModRevs;
      print '-' x 60 . "\n";
      print '%%%% Cannot determine differences, but differences exist.'."\n";
   }
   else  {
      if( CompRev($LRev1, $LRev2) > 0)  {
         ($LRev1, $LRev2) = ($LRev2, $LRev1);
      }
      # Ok. Now that these are in order, I can parse the cvlog output and order 
the revision numbers.
      if( not $ShortOutput )  {
         print "\nIndex: $Index\n" . '=' x 60 . "\nretrieving revision 
$LRev1\nretrieving revision $LRev2\n"
            unless $ModRevs;
         print '-' x 60 . "\n";
      }
      my $LogFH = new FileHandle;
      my $LogCmd = 'cvs -q log';
      $LogCmd .= " $Index";
      unless( $Quiet )  {
         open( $LogFH, "$LogCmd|" )
            or die "Cannot open pipe '$LogCmd|': $!\n";
         my @AllRevs;
         my %AllRevs;
         my $ThisRev;
         # Now get the Log information for this file, this revision
         while( <$LogFH> )  {
            print if $verbose;
            if( /^revision (.*)$/ )  {
               $ThisRev = $1;
               push @AllRevs, $ThisRev;
            }
            if( /^date:/ )  {
               my $Msg = $_;
               my $BGo = 1;
               while( $BGo )  {
                  my $NLine = <$LogFH>;
                  die "EOF in entry revision: $ThisRev\n"
                     unless defined $NLine;
                  print $NLine if $verbose;
                  $NLine =~ /^[=-]+$/ and $BGo = 0;
                  $Msg .= $NLine if $BGo;
               }
               $AllRevs{$ThisRev} = $Msg;
            }
         }
         close $LogFH;
         print "ALLREVS: @AllRevs\n" if $verbose;
         while( @_) {  # Empty out @_ for the sort routine
            shift @_;
         }
         foreach my $R( sort CompRev @AllRevs )  {  # RevSort(address@hidden) ) 
 {
            if( BetRev( $R, $LRev1, $LRev2 ) )  {  # If the current entry is a 
revision between whats given...
               if( RightBranch($R, $LRev2 ) )  {   # ... and if its a branch, 
it's the right branch to follow
                  print "$LRev1 <= $R <= $LRev2\n" if $verbose;
                  if( $NoTrace )  {
                     if( CompRev($R, $LRev2 ) == 0)  {
                        if( $ModRevs )  {
                           if( $ShortOutput )  {
                              print "$Module $Index $R\n";
                           }   else  {
                              print "revision: $R\n$AllRevs{$R}" . '-' x 60 . 
"\n"
                                 unless $ModRevs;
                           }
                           if( $ModRevs )  {
                              my $Msg = $AllRevs{$R};
                              $Msg =~ s/^date.*?\n//gm;
                              chomp $Msg;
                              $ModRevs{$Msg} = exists $ModRevs{$Msg} ? 
$ModRevs{$Msg} ++ : 1;
                           }
                        }
                     }
                  }
                  else  {
                     if( $ShortOutput )  {
                        print "$Module $Index $R\n";
                     }   else  {
                        print "revision: $R\n$AllRevs{$R}" . '-' x 60 . "\n";
                     }
                  }
               }
            }
         }
      }
   }
} # Wee!

sub RightBranch
{
   # Right as in "Correct"

   my( $a, $b ) = @_;
   my( @a ) = split /\./, $a;
   my( @b ) = split /\./, $b;
   # print "$a == $b\n";
   return 0
      if( @a > @b );  # e.g.  1.2.3.4 > 1.3 -- 1.2.3 is the wrong branch to 
follow
   return 1
      if( @a < @b );  # e.g.  1.x < 1.x.x.x .. The other version number check 
determines if the number
                      #          belongs by value...

   return 0
      if( defined $a[2] and $a[1] != $b[1]);
   return 0
      if( defined $a[3] and defined $a[2] and $a[2] != $b[2]);
   return 0
      if( defined $a[4] and defined $a[3] and defined $a[2] and $a[3] != $b[3]);

   return 1;
}

sub BetRev
{
   my( $Bet, $Low, $High ) = @_;
   print "LOW:  $Low <= $Bet ?" if $verbose;
   if( CompRev( $Low, $Bet ) < 0 )  {  # Not inclusive of the low end.
      print " ... YES\n" if $verbose;
   }
   else  {
      print " ... NO\n" if $verbose;
      return 0;
   }

   print "HIGH:  $Bet <= $High ?" if $verbose;
   if( CompRev( $Bet, $High ) <= 0 )  {
      print " ... YES\n" if $verbose;
   }
   else  {
      print " ... NO\n" if $verbose;
      return 0;
   }

   return 1;
}

sub CompRev
{
   my( $Rev1, $Rev2 ) = @_ ? @_ : $a ? ($a,$b) : (undef, undef);

   return 0
      if( not defined $Rev1 or not defined $Rev2 or $Rev1 eq $Rev2);

   # print STDOUT "REV1: $Rev1  REV2: $Rev2\n";
   if( not $Rev1 or not $Rev2 )  {
      die "Cannot call orderrev without two revision!\n";
   }
   my( @Rev1 ) = split /\./, $Rev1;
   my( @Rev2 ) = split /\./, $Rev2;

   my $To = @Rev1 > @Rev2 ? @Rev1 : @Rev2;

   for( my $i = 0; $i < $To; $i ++ )  {
      return -1 if not defined $Rev1[$i];
      return 1 if not defined $Rev2[$i];
      next if $Rev1[$i] eq $Rev2[$i];

      return -1 if $Rev1[$i] < $Rev2[$i];
      return 1 if $Rev1[$i] > $Rev2[$i];
   }
   return 0;
}

sub GetArgs
{
   # Cannot use Getopt::Std, since -r, -D can be used more than once.
   # I wrote this before I knew about long opts module
   my $RCtr = 0;
   my $DCtr = 0;
   my $usage = '';
   while( <DATA> )  {
      $usage .= $_;
   }

   while( @ARGV )  {
      my $a = shift @ARGV;
      if( $a =~ /^\-D(.*?)$/ )  {
         my $arg = $1 ? $1 : shift @ARGV;
         die "-D requires a date. $0 -h for help\n"
            unless $arg;
         $a = '-D';
         print "GOT -D ARG: $a $arg\n" if $verbose;
         $DCtr ++;
         die "-D option is limited to two usages. Don't rock the boat, man.\n"
            if( $DCtr > 2 );
         my $msg = '$Date'.$DCtr.' = $arg';
         eval( $msg );
         die "Error occurred during eval of '$msg': address@hidden"
            if( $@ );
      }
      elsif( $a =~ /^\-r(.*?)$/ )  {
         my $arg = $1 ? $1 : shift @ARGV;
         die "-r requires a tagname. $0 -h for help\n"
            unless $arg;
         $a = '-r';
         print "GOT -r ARG: $a $arg\n" if $verbose;
         $RCtr ++;
         die "-r option is limited to two usages. Don't rock the boat, man.\n"
            if( $RCtr > 2 );
         die "Cannot use 2 -r with -R flag enabled\n"
            if( $RCtr == 2 and $UseAreaprops );
         my $msg = '$Rev'.$RCtr.' = $arg';
         eval( $msg );
         die "Error occurred during eval of '$msg': address@hidden"
            if( $@ );
      }
      elsif( $a =~ /^\-I(.*?)/ )  {
         my $arg = $1 ? $1 : shift @ARGV;
         die "-I requires an input file name. $0 -h for help\n"
            unless $arg;
         print "GOT -I: ARG: -I $arg\n" if $verbose;
         $InputFile = $arg;
      }
      elsif( $a =~ /^\-l/ )  {
         print "GOT -l ARG: $a\n" if $verbose;
         $Recursive = 0;
      }
      elsif( $a =~ /^\-m/ )  {
         print "GOT -m ARG: $a\n" if $verbose;
         $ModRevs = 1;
         $NoTrace = 1;
      }
      elsif( $a =~ /^\-S/ )  {
         print "GOT -S ARG: $a\n" if $verbose;
         $ShortOutput = 1;
      }
      elsif( $a =~ /^\-N/ )  {
         print "GOT -N ARG: $a\n" if $verbose;
         $NewFlag = 1 ;
      }
      elsif( $a =~ /^\-T/ )  {
         print "GOT -T ARG: $a\n" if $verbose;
         $NoTrace = 1;
      }
      elsif( $a =~ /^\-q/ )  {
         print "GOT -q ARG: $a\n" if $verbose;
         $Quiet = 1;
      }
      elsif( $a =~ /^\-V/ )  {
         print "GOT -V ARG: $a\n" if $verbose;
         $verbose = 1;
      }
      elsif( $a =~ /^\-R/ )  {
         print "GOT -R ARG: $a\n" if $verbose;
         $UseAreaprops = 1;
         die "Cannot use 2 -r with -R flag enabled\n"
            if( $RCtr == 2 and $UseAreaprops );
         # Go get the areaprops BRANCH_TAG
         my $Msg = "use Olf;\nrequire 'olf.pl';\nmy \$wd = 
workdir();\nget_area_properties(\$wd, \$wd);\n\$Rev2 = \$main::AREA{BRANCH_TAG} 
? \$main::AREA{BRANCH_TAG} : 'MAINLINE';\n";
         eval( $Msg );
         die "Error occurred during eval of '$Msg': address@hidden'ly you need 
to be in an Olf area to use the -R switch.\n"
            if $@;
         if( defined $Rev2 )  {
            print STDOUT "\nUsing BRANCH_TAG: $Rev2\n";
         }
      }
      elsif( $a =~ /^\-h/ )  {
         print "GOT -h ARG: $a\n" if $verbose;
         die "$usage\n";
      }
      elsif( $a =~ /^\-/ )  {
         print "GOT UNKNOWN ARG: $a\n" if $verbose;
         die "Unknown option: $a\n$0 -h for help\n";
      }
      else  {
         print "GOT FILE: $a\n" if $verbose;
         push @Files, $a;
      }
   }
}

#       -m      show revisions per module instead of per file3

__DATA__
usage: cvdiff [options] [file ...]
options:
        -l      Local directory only, not recursive
        -D d1   Diff revision for date against working file.
        -D d2   Diff rev1/date1 against date2.
        -I file Use a diff file instead of cvs diff command
        -N      include diffs for added and removed files.
        -r rev1 Diff revision for rev1 against working file.
        -r rev2 Diff rev1/date1 against rev2.
        -R      use areaprops BRANCH_TAG value for rev2
        -T      turn off revision trace, only show last revision
        -q      quiet, suppress the cvs log information
        -V      verbose
        -h      show this help message

reply via email to

[Prev in Thread] Current Thread [Next in Thread]