#!/usr/bin/perl -w # # Script to analyze CVSup logs, # written by Ben Smithurst # # Tested with logs from cvsupping the FreeBSD CVS repository, but it should # work with any repository really. # # The latest copy of this file can be found at # . # # $BCPS: src/cvsuplog/cvsuplog,v 1.23 2001/07/12 04:07:54 ben Exp $ use strict; my $now; my ($action, $file, $what); my ($i, $rev, $time, $who, $tag); my (@buf, @unknown_lines); my %isdir; my ($coll); my (%authorcount, %collcount, @dircount); my (%data, $ref, $u, $c, $key); die "run with no arguments, log file on STDIN" if @ARGV; # Set this to your local mirror, if you have one. my $cvsweb = "http://www.FreeBSD.org/cgi/cvsweb.cgi/"; print "CVSup log\n"; print "\n"; print "\n"; print "

CVSup log

\n"; while () { chop; # ignore certain things... next if /^ Clear default branch/ || /^Parsing supfile/ || /^Connect(ing|ed) to/ || /^Server software version/ || /^Negotiating file attribute support/ || /^Exchanging collection information/ || /^Establishing .* data connection/ || /^Running/ || /^Shutting down/ || /^Finished/; if (/^ ((Add|Delete) tag (.*) -> (.*)|Add delta ([\d.]+) ([\d.]+) (.*))/) { if (defined $5) { $rev = $5; $time = $6; $who = $7; @buf = split /\./, $time; die "bad time: $time" unless @buf == 6; $buf[0] += 1900 if $buf[0] < 100; $time = "$buf[0]-$buf[1]-$buf[2] $buf[3]:$buf[4]:$buf[5]"; $collcount{$coll}++; $authorcount{$who}++; update_dircount($file); push @$ref, "Update to $rev (at $time by $who)"; } else { $tag = $3; $rev = $4; push @$ref, "$2 tag $tag ". "at revision $rev"; } next; } if (/^Updating collection (.*)/) { $coll = $1; $collcount{$coll} ||= 0; $data{$coll} ||= {}; next; } if (/^ (Touch|Edit|Rsync|Append to|Create|Mkdir|SetAttrs|Delete) (.*)/) { $action = $1; $file = $2; if ($action eq "Mkdir" || $isdir{$file}) { $what = "directory"; regdir($file); } else { $what = "file"; ($i = $file) =~ s#/[^/]+$##; regdir($i); } $file =~ s/,v( -> Attic|)$/$1/; $key = "$file $action"; if ($action eq "Edit") { $data{$coll}{$key} ||= []; $ref = $data{$coll}{$key}; } else { $data{$coll}{$key} = "$action $what"; update_dircount($key); $collcount{$coll}++; } next; } push @unknown_lines, $_; } print "

Collection statistics

\n"; foreach (sort keys %data) { my $attic; print "

Collection ``$_''

\n"; print "
    \n"; foreach $u (sort keys %{$data{$_}}) { $ref = $data{$_}{$u}; if ($u =~ s/ -> Attic//) { $attic = " (moved to Attic)"; } else { $attic = ""; } $u =~ s/ .*//; if (ref $ref) { $i = 0; foreach $c (@$ref) { if ($c !~ /^Add tag/) { $i = 1; last; } } next unless $i; print "
  • Edit file $u$attic"; print "\n
      \n"; foreach $c (sort @$ref) { print "
    • $c
    • \n"; } print "
    \n"; } else { print "
  • $ref $u"; } print "
  • \n"; } print "
\n"; } print "

Summary

\n"; if (%authorcount) { print "

Number of updates per committer

\n"; print "
    "; foreach (sort { $authorcount{$b} <=> $authorcount{$a} or $a cmp $b } keys %authorcount) { print "
  • $_: $authorcount{$_} updates
  • \n"; } print "
\n"; } if (%collcount) { print "

Number of updates per collection

\n"; print "
    "; foreach (sort { $collcount{$b} <=> $collcount{$a} or $a cmp $b } keys %collcount) { print "
  • $_: $collcount{$_} updates
  • \n"; } print "
\n"; } if (@dircount) { print "

Number of updates by directory

\n"; for ($i = 0; $i < 4; $i++) { last unless defined $dircount[$i]; print "

Level ", $i + 1, "

\n
    \n"; foreach (sort { $dircount[$i]{$b} <=> $dircount[$i]{$a} or $a cmp $b } keys %{$dircount[$i]}) { next unless $isdir{$_}; print "
  • $_: $dircount[$i]{$_} updates
  • \n"; } print "
"; } } if (@unknown_lines) { print "

Unknown log lines

\n
\n";
	print "$_\n" foreach @unknown_lines;
	print "
\n"; } $now = time(); print "

Created by ", "", "cvsuplog"; printf " at %s", maketime($now); # only print the logfile time if STDIN is a plain file, and the time is # different to the current time. if ((@buf = stat STDIN) && -f _ && ($buf[9] - $now > 60 || $now - $buf[9] > 60)) { printf " from a logfile created at %s", maketime($buf[9]); } print ".

\n"; exit; # return the specified time in human-readable format sub maketime { my @buf = localtime shift; return sprintf "%04d-%02d-%02d %02d:%02d:%02d", $buf[5] + 1900, $buf[4] + 1, $buf[3], $buf[2], $buf[1], $buf[0]; } # record that the specified path is a directory, used by other things later # on. sub regdir { my $path = shift; do { $isdir{$path} = 1; } while ($path =~ s#/[^/]+$##); } sub update_dircount { my (@bits, $i); my $file = shift; @bits = split m#/#, $file; $i = $#bits; for (;;) { $dircount[$i--]{$file}++; return unless $file =~ s#/[^/]+$##; } }