#!/usr/bin/perl -w use strict; use Getopt::Std; use File::Basename qw(basename dirname); our ($opt_h, $opt_n, $opt_s); getopts('hns'); $opt_h && usage(); sub usage { print STDERR "Usage: ${\basename $0} [-h] [-n] [-s] < \n"; exit(1); } my (%mailmap); my (%email); my (%map); my $pstate = 1; my $n_records = 0; my $n_output = 0; sub shortlog_entry($$) { my ($name, $desc) = @_; my $key = $name; $desc =~ s#/pub/scm/linux/kernel/git/#/.../#g; $desc =~ s#\[PATCH\] ##g; # store description in array, in email->{desc list} map if (exists $map{$key}) { # grab ref my $obj = $map{$key}; # add desc to array push(@$obj, $desc); } else { # create new array, containing 1 item my @arr = ($desc); # store ref to array $map{$key} = \@arr; } } # sort comparison function sub by_name($$) { my ($a, $b) = @_; uc($a) cmp uc($b); } sub by_nbentries($$) { my ($a, $b) = @_; my $a_entries = $map{$a}; my $b_entries = $map{$b}; @$b_entries - @$a_entries || by_name $a, $b; } my $sort_method = $opt_n ? \&by_nbentries : \&by_name; sub summary_output { my ($obj, $num, $key); foreach $key (sort $sort_method keys %map) { $obj = $map{$key}; $num = @$obj; printf "%s: %u\n", $key, $num; $n_output += $num; } } sub shortlog_output { my ($obj, $num, $key, $desc); foreach $key (sort $sort_method keys %map) { $obj = $map{$key}; $num = @$obj; # output author printf "%s (%u):\n", $key, $num; # output author's 1-line summaries foreach $desc (reverse @$obj) { print " $desc\n"; $n_output++; } # blank line separating author from next author print "\n"; } } sub changelog_input { my ($author, $desc); while (<>) { # get author and email if ($pstate == 1) { my ($email); next unless /^[Aa]uthor:?\s*(.*?)\s*<(.*)>/; $n_records++; $author = $1; $email = $2; $desc = undef; # cset author fixups if (exists $mailmap{$email}) { $author = $mailmap{$email}; } elsif (exists $mailmap{$author}) { $author = $mailmap{$author}; } elsif (!$author) { $author = $email; } $email{$author}{$email}++; $pstate++; } # skip to blank line elsif ($pstate == 2) { next unless /^\s*$/; $pstate++; } # skip to non-blank line elsif ($pstate == 3) { next unless /^\s*?(.*)/; # skip lines that are obviously not # a 1-line cset description next if /^\s*From: /; chomp; $desc = $1; &shortlog_entry($author, $desc); $pstate = 1; } else { die "invalid parse state $pstate"; } } } sub read_mailmap { my ($fh, $mailmap) = @_; while (<$fh>) { chomp; if (/^([^#].*?)\s*<(.*)>/) { $mailmap->{$2} = $1; } } } sub setup_mailmap { read_mailmap(\*DATA, \%mailmap); if (-f '.mailmap') { my $fh = undef; open $fh, '<', '.mailmap'; read_mailmap($fh, \%mailmap); close $fh; } } sub finalize { #print "\n$n_records records parsed.\n"; if ($n_records != $n_output) { die "parse error: input records != output records\n"; } if (0) { for my $author (sort keys %email) { my $e = $email{$author}; for my $email (sort keys %$e) { print STDERR "$author <$email>\n"; } } } } &setup_mailmap; &changelog_input; $opt_s ? &summary_output : &shortlog_output; &finalize; exit(0); __DATA__ # # Even with git, we don't always have name translations. # So have an email->real name table to translate the # (hopefully few) missing names # Adrian Bunk Andreas Herrmann Andrew Morton Andrew Vasquez Christoph Hellwig Corey Minyard David Woodhouse Domen Puncer Douglas Gilbert Ed L Cashin Evgeniy Polyakov Felix Moeller Frank Zago Greg Kroah-Hartman James Bottomley James Bottomley Jeff Garzik Jens Axboe Kay Sievers Mitesh shah Morten Welinder Morten Welinder Morten Welinder Morten Welinder Nguyen Anh Quynh Paolo 'Blaisorblade' Giarrusso Peter A Jonsson Ralf Wildenhues Rudolf Marek Rui Saraiva Sachin P Sant Santtu Hyrkk,Av(B Simon Kelley Tejun Heo Tony Luck