#!/usr/bin/perl # Generate a ChangeLog file from a CVS log. # Written by Robert Krawitz # This code is in the public domain and may be used # for any purpose. %logmsgs = (); # Index by date, time, and author %fileversions = (); $skipme = 0; @cvsdirs=`find . -type d -name CVS -print`; map { chomp; s,^\./,, } @cvsdirs; foreach $d (@cvsdirs) { if (open ENTRIES, "$d/Entries") { local ($rootdir) = $d; $rootdir =~ s/CVS$//; while () { local ($type, $file, $version, @junk) = split /\//; if ($type eq "") { $file = "$rootdir$file"; $fileversions{$file} = $version; } } close ENTRIES; } } sub compare_versions { # vw: version of the working file # vl: version from the log # The idea is that we want versions on the current branch, on branches # leading to the current branch, and on the root prior to the current # branch. # # Example: the current file is 1.5.12.2.4.3 # # We want versions: # 1.1 # 1.2 # 1.3 # 1.4 # 1.5 # 1.5.12.1 # 1.5.12.2 # 1.5.12.2.4.1 # 1.5.12.2.4.2 # 1.5.12.2.4.3 # # We look at the numbers in pairs. The first number in each pair is # the branch number; the second number is the version on the branch. # The pairs are of the form (B, V). # # If the number of components in the log version is greater than the # number of components in the working version, we aren't interested. # This file cannot be a predecessor of the working version; it is # either a branch off the working version, or it is an entirely different # branch. # # We next iterate over all pairs in the log version. The following must # be true for all pairs: # # Bw = Bl # Vw >= Vl # # Note that there's no problem if the number of components in the # working version exceeds the number of components in the log version. # # There is a special case: If the working version doesn't exist at all, # we return true if the log version is on the mainline. This lets us # see log messages from files that have been deleted. my ($vw, $vl) = @_; if ($vw eq "") { return 2; } my (@vvw) = split /\./, $vw; my (@vvl) = split /\./, $vl; if ($#vvl > $#vvw) { return 3; } my ($i); for ($i = 0; $i < $#vvl; $i += 2) { my ($bl) = $vvl[$i]; my ($vl) = $vvl[$i + 1]; my ($bw) = $vvw[$i]; my ($vw) = $vvw[$i + 1]; if ($bw != $bl || $vw < $vl) { return 0; } } return 1; } while (<>) { if (/^Working file: /) { chomp; ($ignore, $ignore, $currentfile) = split; while (<>) { if (/^----------------------------$/) { last; } } next; } elsif (/^----------------------------$/) { next; } elsif (/^revision /) { ($ignore, $revision) = split; my ($check) =&compare_versions($fileversions{$currentfile}, $revision); # # Special case -- if a file is not in the current sandbox, but is # at top level, log it; otherwise if it is not in the current # sandbox, don't log it. # if (($check == 2 && !($currentfile =~ /\//) && ($currentfile =~ /\.[chly]$/)) || $check == 1) { $skipme = 0; } else { $skipme = 1; } } elsif (/^date: /) { ($ignore, $date, $time, $ignore, $author, $ignore, $ignore, $ignore, $plus, $minus, $ignore) = split; # $time =~ s/[0-9]:[0-9][0-9];$//; # $time =~ s/[0-9][0-9];$//; $author =~ s/;$//; $body = ""; $firstline = 1; while (<>) { if (/^----------------------------$/) { last; } elsif (/^=============================================================================$/) { last; } elsif ($firstline && /^branches:[ \t]+[0-9]+(\.[0-9]+)+;$/) { next; } else { $body .= $_; $firstline = 0; } } $junkbody = $body; $junkbody =~ s/\s//g; $junkbody .= "x"; $datetimeauthor = "$date $time $author $junkbody $currentfile $revision"; if ($skipme == 0) { $logmsgs{$datetimeauthor} = $body; $plus{$datetimeauthor} = $plus; $minus{$datetimeauthor} = $minus; } } # Other junk we ignore } $prevmsg=""; $header=""; @fileinfo = (); @chlog = reverse sort keys %logmsgs; foreach $_ (@chlog) { ($date, $time, $author, $junk, $file, $revision) = split; $date =~ s,/,-,g; $msg = $logmsgs{$_}; if ($prevmsg eq $msg) { push @fileinfo, "\t\t$file ($revision) ($plus{$_} $minus{$_})\n"; } else { if ($prevmsg ne "" || $#fileinfo >= 0) { print $header; $filestuff = join "", sort @fileinfo; $filestuff =~ s/\t/\tFiles:/; print "$filestuff\n"; $prevmsg =~ s/^/\t/g; $prevmsg =~ s/\n/\n\t/g; $prevmsg =~ s/[ \t]+\n/\n/g; $prevmsg =~ s/[ \t]+$//g; print "$prevmsg\n"; } $header = "$date\t<$author\@sourceforge.net>\n\n"; $prevmsg = $msg; @fileinfo = (); push @fileinfo, "\t\t$file ($revision) ($plus{$_} $minus{$_})\n"; } } if ($prevmsg ne "" or $fileinfo ne "") { print "$date\t<$author\@sourceforge.net>\n\n"; $filestuff = join "", sort @fileinfo; $filestuff =~ s/\t/\tFiles:/; print "$filestuff\n"; $prevmsg =~ s/^/\t/g; $prevmsg =~ s/\n/\n\t/g; $prevmsg =~ s/[ \t]+\n/\n/g; $prevmsg =~ s/[ \t]+$//g; print "$prevmsg\n"; }