From 30915862cd380e263ee8c0f1d65307c32acc0926 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Mon, 7 Apr 2003 02:45:26 +0000 Subject: [PATCH] New upstream version. committer: mfx 1049683526 +0000 --- scripts/cvs2cl.pl | 159 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 33 deletions(-) diff --git a/scripts/cvs2cl.pl b/scripts/cvs2cl.pl index 71c8d76f..d0dfd262 100644 --- a/scripts/cvs2cl.pl +++ b/scripts/cvs2cl.pl @@ -9,14 +9,15 @@ exec perl -w -x $0 ${1+"$@"} # -*- mode: perl; perl-indent-level: 2; -*- ### ### ############################################################## -## $Revision: 2.46 $ -## $Date: 2003/01/18 13:14:52 $ +## $Revision: 2.47 $ +## $Date: 2003/03/10 16:08:30 $ ## $Author: fluffy $ ## ## (C) 2001,2002,2003 Martyn J. Pearce , under the GNU GPL. ## (C) 1999 Karl Fogel , under the GNU GPL. ## ## (Extensively hacked on by Melissa O'Neill .) +## (Gecos hacking by Robin Johnson .) ## ## cvs2cl.pl is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -38,6 +39,7 @@ use strict; use Text::Wrap; use Time::Local; use File::Basename; +use User::pwent; # The Plan: @@ -78,7 +80,7 @@ use File::Basename; my $Log_Source_Command = "cvs log"; # In case we have to print it out: -my $VERSION = '$Revision: 2.46 $'; +my $VERSION = '$Revision: 2.47 $'; $VERSION =~ s/\S+\s+(\S+)\s+\S+/$1/; ## Vars set by options: @@ -105,6 +107,15 @@ my $Cumulative = 0; # Expand usernames to email addresses based on a map file? my $User_Map_File = ""; +# Output log in chronological order? [default is reverse chronological order] +my $Chronological_Order = 0; + +# Grab user details via gecos +my $Gecos = 0; + +# User domain for gecos email addresses +my $Domain = ""; + # Output to a file or to stdout? my $Output_To_Stdout = 0; @@ -112,7 +123,10 @@ my $Output_To_Stdout = 0; my $Prune_Empty_Msgs = 0; # Tags of which not to output -my @ignore_tags; +my %ignore_tags; + +# Show only revisions with Tags +my %show_tags; # Don't call Text::Wrap on the body of the message my $No_Wrap = 0; @@ -178,6 +192,9 @@ my $Input_From_Stdin = 0; # Don't show filenames in output. my $Hide_Filenames = 0; +# Don't shorten directory names from filenames. +my $Common_Dir = 1; + # Max checkin duration. CVS checkin is not atomic, so we may have checkin # times that span a range of time. We assume that checkins will last no # longer than $Max_Checkin_Duration seconds, and that similarly, no @@ -477,6 +494,22 @@ sub derive_change_log () ($time, $author) = &parse_date_and_author ($_); if (defined ($usermap{$author}) and $usermap{$author}) { $author = $usermap{$author}; + } elsif($Domain ne "" or $Gecos == 1) { + my $email = $author; + if($Domain ne "") { + $email = $author."@".$Domain; + } + my $pw = getpwnam($author); + my $fullname; + my $office; + my $workphone; + my $homephone; + for (($fullname, $office, $workphone, $homephone) = split /\s*,\s*/, $pw->gecos) { + s/&/ucfirst(lc($pw->name))/ge; + } + if($fullname ne "") { + $author = $fullname . " <" . $email . ">"; + } } } else { @@ -769,7 +802,13 @@ sub derive_change_log () print LOG_OUT "$declaration\n\n$root\n\n"; } - foreach my $time (sort {$main::b <=> $main::a} (keys %changelog)) + my @key_list = (); + if($Chronological_Order) { + @key_list = sort {$main::a <=> $main::b} (keys %changelog); + } else { + @key_list = sort {$main::b <=> $main::a} (keys %changelog); + } + foreach my $time (@key_list) { next if ($Delta_Mode && (($time <= $Delta_StartTime) || @@ -832,17 +871,28 @@ sub derive_change_log () } FOOBIE: - while (my ($msg,$qunklist) = each %$mesghash) + # We sort here to enable predictable ordering for the testing porpoises + for my $msg (sort keys %$mesghash) { + my $qunklist = $mesghash->{$msg}; + ## MJP: 19.xii.01 : Exclude @ignore_tags - for my $ignore_tag (@ignore_tags) { + for my $ignore_tag (keys %ignore_tags) { next FOOBIE - if grep $_ eq $ignore_tag, map(@{$_->{tags}}, + if grep($_ eq $ignore_tag, map(@{$_->{tags}}, grep(defined $_->{tags}, - @$qunklist)); + @$qunklist))); } ## MJP: 19.xii.01 : End exclude @ignore_tags + # show only files with tag --show-tag $show_tag + if ( keys %show_tags ) { + next FOOBIE + if !grep(exists $show_tags{$_}, map(@{$_->{tags}}, + grep(defined $_->{tags}, + @$qunklist))); + } + my $files = &pretty_file_list ($qunklist); my $header_line; # date and author my $body; # see below @@ -1017,7 +1067,15 @@ sub pretty_file_list () } my $qunksref = shift; - my @qunkrefs = @$qunksref; + + my @qunkrefs = + grep +((! exists $_->{'tags'} or + ! grep exists $ignore_tags{$_}, @{$_->{'tags'}}) and + (! keys %show_tags or + (exists $_->{'tags'} and + grep exists $show_tags{$_}, @{$_->{'tags'}})) + ), + @$qunksref; my @filenames; my $beauty = ""; # The accumulating header string for this entry. my %non_unanimous_tags; # Tags found in a proper subset of qunks @@ -1032,17 +1090,10 @@ sub pretty_file_list () QUNKREF: foreach my $qunkref (@qunkrefs) { - ## MJP: 19.xii.01 : Exclude @ignore_tags - for my $ignore_tag (@ignore_tags) { - next QUNKREF - if grep $_ eq $ignore_tag, @{$$qunkref{'tags'}}; - } - ## MJP: 19.xii.01 : End exclude @ignore_tags - # Keep track of whether all the files in this commit were in the # same directory, and memorize it if so. We can make the output a # little more compact by mentioning the directory only once. - if ((scalar (@qunkrefs)) > 1) + if ($Common_Dir && (scalar (@qunkrefs)) > 1) { if (! (defined ($common_dir))) { @@ -1232,17 +1283,12 @@ sub pretty_file_list () # Okay; any qunks that were done according to branch are taken care # of, and marked as printed. Now print everyone else. + my %fileinfo_printed; foreach my $qunkref (@qunkrefs) { next if (defined ($$qunkref{'printed'})); # skip if already printed - if ($fbegun) { - $beauty .= ", "; - } - else { - $fbegun = 1; - } - $beauty .= substr ($$qunkref{'filename'}, length ($common_dir)); + my $b = substr ($$qunkref{'filename'}, length ($common_dir)); # todo: Shlomo's change was this: # $beauty .= substr ($$qunkref{'filename'}, # (($common_dir eq "./") ? "" : length ($common_dir))); @@ -1254,26 +1300,35 @@ sub pretty_file_list () if ($Show_Revisions) { $started_addendum = 1; - $beauty .= " ("; - $beauty .= "$$qunkref{'revision'}"; + $b .= " ("; + $b .= "$$qunkref{'revision'}"; } if ($Show_Tags && (defined $$qunkref{'tags'})) { my @tags = grep ($non_unanimous_tags{$_}, @{$$qunkref{'tags'}}); if ((scalar (@tags)) > 0) { if ($started_addendum) { - $beauty .= ", "; + $b .= ", "; } else { - $beauty .= " (tags: "; + $b .= " (tags: "; } - $beauty .= join (', ', @tags); + $b .= join (', ', @tags); $started_addendum = 1; } } if ($started_addendum) { - $beauty .= ")"; + $b .= ")"; } } + + unless ( exists $fileinfo_printed{$b} ) { + if ($fbegun) { + $beauty .= ", "; + } else { + $fbegun = 1; + } + $beauty .= $b, $fileinfo_printed{$b} = 1; + } } # Unanimous tags always come last. @@ -1591,7 +1646,7 @@ sub maybe_read_user_map_file () else { $expansions{$username} = $expansion; } - } + } # fi ($User_Map_File) close (MAPFILE); } @@ -1648,14 +1703,28 @@ sub parse_options () elsif ($arg =~ /^--fsf$/) { $FSF_Style = 1; } + elsif ($arg =~ /^--FSF$/) { + $Show_Times = 0; + $Common_Dir = 0; + } elsif ($arg =~ /^-U$|^--usermap$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; $User_Map_File = $narg; } + elsif ($arg =~ /^--gecos$/) { + $Gecos = 1; + } + elsif ($arg =~ /^--domain$/) { + my $narg = shift (@ARGV) || die "$arg needs argument.\n"; + $Domain = $narg; + } elsif ($arg =~ /^-W$|^--window$/) { defined(my $narg = shift (@ARGV)) || die "$arg needs argument.\n"; $Max_Checkin_Duration = $narg; } + elsif ($arg =~ /^--chrono$/) { + $Chronological_Order = 1; + } elsif ($arg =~ /^-I$|^--ignore$/) { my $narg = shift (@ARGV) || die "$arg needs argument.\n"; push (@Ignore_Files, $narg); @@ -1731,10 +1800,18 @@ sub parse_options () $Hide_Filenames = 1; $After_Header = ""; } + elsif ($arg =~ /^--no-common-dir$/) { + $Common_Dir = 0; + } elsif ($arg =~ /^--ignore-tag$/ ) { die "$arg needs argument.\n" unless @ARGV; - push @ignore_tags, shift @ARGV; + $ignore_tags{shift @ARGV} = 1; + } + elsif ($arg =~ /^--show-tag$/ ) { + die "$arg needs argument.\n" + unless @ARGV; + $show_tags{shift @ARGV} = 1; } else { # Just add a filename as argument to the log command @@ -1864,8 +1941,11 @@ Options/Arguments: -d, --distributed Put ChangeLogs in subdirs -f FILE, --file FILE Write to FILE instead of "ChangeLog" --fsf Use this if log data is in FSF ChangeLog style + --FSF Attempt strict FSF-standard compatible output -W SECS, --window SECS Window of time within which log entries unify -U UFILE, --usermap UFILE Expand usernames to email addresses from UFILE + --domain DOMAIN Domain to build email addresses from + --gecos Get user information from GECOS data -R REGEXP, --regexp REGEXP Include only entries that match REGEXP -I REGEXP, --ignore REGEXP Ignore files whose names match REGEXP -C, --case-insensitive Any regexp matching is done case-insensitively @@ -1880,7 +1960,20 @@ Options/Arguments: --xml Output XML instead of ChangeLog format --xml-encoding ENCODING Insert encoding clause in XML header --hide-filenames Don't show filenames (ignored for XML output) + --no-common-dir Don't shorten directory names from filenames. -P, --prune Don't show empty log messages + --ignore-tag TAG Ignore individual changes that are associated + with a given tag. May be repeated, if so, + changes that are associated with any of the + given tags are ignored. + --show-tag TAG Log only individual changes that are associated + with a given tag. May be repeated, if so, + changes that are associated with any of the + given tags are logged. + --delta FROM_TAG:TO_TAG Attempt a delta between two tags (since FROM_TAG + up to & including TO_TAG). The algorithm is a + simple date-based one (this is a *hard* problem) + so results are imperfect -g OPTS, --global-opts OPTS Invoke like this "cvs OPTS log ..." -l OPTS, --log-opts OPTS Invoke like this "cvs ... log OPTS" FILE1 [FILE2 ...] Show only log information for the named FILE(s)