Merge with master to pick up gitk updates.
[git] / git-shortlog
1 #!/usr/bin/perl -w
2
3 use strict;
4
5 #
6 # Even with git, we don't always have name translations.
7 # So have an email->real name table to translate the
8 # (hopefully few) missing names
9 #
10 my %mailmap = (
11         'R.Marek@sh.cvut.cz' => 'Rudolf Marek',
12         'Ralf.Wildenhues@gmx.de' => 'Ralf Wildenhues',
13         'aherrman@de.ibm.com' => 'Andreas Herrmann',
14         'akpm@osdl.org' => 'Andrew Morton',
15         'andrew.vasquez@qlogic.com' => 'Andrew Vasquez',
16         'aquynh@gmail.com' => 'Nguyen Anh Quynh',
17         'axboe@suse.de' => 'Jens Axboe',
18         'blaisorblade@yahoo.it' => 'Paolo \'Blaisorblade\' Giarrusso',
19         'bunk@stusta.de' => 'Adrian Bunk',
20         'domen@coderock.org' => 'Domen Puncer',
21         'dougg@torque.net' => 'Douglas Gilbert',
22         'dwmw2@shinybook.infradead.org' => 'David Woodhouse',
23         'ecashin@coraid.com' => 'Ed L Cashin',
24         'felix@derklecks.de' => 'Felix Moeller',
25         'fzago@systemfabricworks.com' => 'Frank Zago',
26         'gregkh@suse.de' => 'Greg Kroah-Hartman',
27         'hch@lst.de' => 'Christoph Hellwig',
28         'htejun@gmail.com' => 'Tejun Heo',
29         'jejb@mulgrave.(none)' => 'James Bottomley',
30         'jejb@titanic.il.steeleye.com' => 'James Bottomley',
31         'jgarzik@pretzel.yyz.us' => 'Jeff Garzik',
32         'johnpol@2ka.mipt.ru' => 'Evgeniy Polyakov',
33         'kay.sievers@vrfy.org' => 'Kay Sievers',
34         'minyard@acm.org' => 'Corey Minyard',
35         'mshah@teja.com' => 'Mitesh shah',
36         'pj@ludd.ltu.se' => 'Peter A Jonsson',
37         'rmps@joel.ist.utl.pt' => 'Rui Saraiva',
38         'santtu.hyrkko@gmail.com' => 'Santtu Hyrkkö',
39         'simon@thekelleys.org.uk' => 'Simon Kelley',
40         'ssant@in.ibm.com' => 'Sachin P Sant',
41         'terra@gnome.org' => 'Morten Welinder',
42         'tony.luck@intel.com' => 'Tony Luck',
43         'welinder@anemone.rentec.com' => 'Morten Welinder',
44         'welinder@darter.rentec.com' => 'Morten Welinder',
45         'welinder@troll.com' => 'Morten Welinder',
46 );
47
48 my (%map);
49 my $pstate = 1;
50 my $n_records = 0;
51 my $n_output = 0;
52
53
54 sub shortlog_entry($$) {
55         my ($name, $desc) = @_;
56         my $key = $name;
57
58         $desc =~ s#/pub/scm/linux/kernel/git/#/.../#g;
59         $desc =~ s#\[PATCH\] ##g;
60
61         # store description in array, in email->{desc list} map
62         if (exists $map{$key}) {
63                 # grab ref
64                 my $obj = $map{$key};
65
66                 # add desc to array
67                 push(@$obj, $desc);
68         } else {
69                 # create new array, containing 1 item
70                 my @arr = ($desc);
71
72                 # store ref to array
73                 $map{$key} = \@arr;
74         }
75 }
76
77 # sort comparison function
78 sub by_name($$) {
79         my ($a, $b) = @_;
80
81         uc($a) cmp uc($b);
82 }
83
84 sub shortlog_output {
85         my ($obj, $key, $desc);
86
87         foreach $key (sort by_name keys %map) {
88                 # output author
89                 printf "%s:\n", $key;
90
91                 # output author's 1-line summaries
92                 $obj = $map{$key};
93                 foreach $desc (reverse @$obj) {
94                         print "  $desc\n";
95                         $n_output++;
96                 }
97
98                 # blank line separating author from next author
99                 print "\n";
100         }
101 }
102
103 sub changelog_input {
104         my ($author, $desc);
105
106         while (<>) {
107                 # get author and email
108                 if ($pstate == 1) {
109                         my ($email);
110
111                         next unless /^Author: (.*)<(.*)>.*$/;
112         
113                         $n_records++;
114         
115                         $author = $1;
116                         $email = $2;
117                         $desc = undef;
118
119                         # trim trailing whitespace.
120                         # why doesn't chomp work?
121                         while ($author && ($author =~ /\s$/)) {
122                                 chop $author;
123                         }
124         
125                         # cset author fixups
126                         if (exists $mailmap{$email}) {
127                                 $author = $mailmap{$email};
128                         } elsif (exists $mailmap{$author}) {
129                                 $author = $mailmap{$author};
130                         } elsif ((!$author) || ($author eq "")) {
131                                 $author = $email;
132                         }
133         
134                         $pstate++;
135                 }
136         
137                 # skip to blank line
138                 elsif ($pstate == 2) {
139                         next unless /^\s*$/;
140                         $pstate++;
141                 }
142         
143                 # skip to non-blank line
144                 elsif ($pstate == 3) {
145                         next unless /^\s*(\S.*)$/;
146
147                         # skip lines that are obviously not
148                         # a 1-line cset description
149                         next if /^\s*From: /;
150
151                         chomp;
152                         $desc = $1;
153         
154                         &shortlog_entry($author, $desc);
155         
156                         $pstate = 1;
157                 }
158         
159                 else {
160                         die "invalid parse state $pstate";
161                 }
162         }
163 }
164
165 sub finalize {
166         #print "\n$n_records records parsed.\n";
167
168         if ($n_records != $n_output) {
169                 die "parse error: input records != output records\n";
170         }
171 }
172
173 &changelog_input;
174 &shortlog_output;
175 &finalize;
176 exit(0);
177