git-shortlog.perlon commit git-branch -D: make it work even when on a yet-to-be-born branch (67affd5)
   1#!/usr/bin/perl -w
   2
   3use strict;
   4use Getopt::Std;
   5use File::Basename qw(basename dirname);
   6
   7our ($opt_h, $opt_n, $opt_s);
   8getopts('hns');
   9
  10$opt_h && usage();
  11
  12sub usage {
  13        print STDERR "Usage: ${\basename $0} [-h] [-n] [-s] < <log_data>\n";
  14        exit(1);
  15}
  16
  17my (%mailmap);
  18my (%email);
  19my (%map);
  20my $pstate = 1;
  21my $n_records = 0;
  22my $n_output = 0;
  23
  24sub shortlog_entry($$) {
  25        my ($name, $desc) = @_;
  26        my $key = $name;
  27
  28        $desc =~ s#/pub/scm/linux/kernel/git/#/.../#g;
  29        $desc =~ s#\[PATCH\] ##g;
  30
  31        # store description in array, in email->{desc list} map
  32        if (exists $map{$key}) {
  33                # grab ref
  34                my $obj = $map{$key};
  35
  36                # add desc to array
  37                push(@$obj, $desc);
  38        } else {
  39                # create new array, containing 1 item
  40                my @arr = ($desc);
  41
  42                # store ref to array
  43                $map{$key} = \@arr;
  44        }
  45}
  46
  47# sort comparison function
  48sub by_name($$) {
  49        my ($a, $b) = @_;
  50
  51        uc($a) cmp uc($b);
  52}
  53sub by_nbentries($$) {
  54        my ($a, $b) = @_;
  55        my $a_entries = $map{$a};
  56        my $b_entries = $map{$b};
  57
  58        @$b_entries - @$a_entries || by_name $a, $b;
  59}
  60
  61my $sort_method = $opt_n ? \&by_nbentries : \&by_name;
  62
  63sub summary_output {
  64        my ($obj, $num, $key);
  65
  66        foreach $key (sort $sort_method keys %map) {
  67                $obj = $map{$key};
  68                $num = @$obj;
  69                printf "%s: %u\n", $key, $num;
  70                $n_output += $num;
  71        }
  72}
  73
  74sub shortlog_output {
  75        my ($obj, $num, $key, $desc);
  76
  77        foreach $key (sort $sort_method keys %map) {
  78                $obj = $map{$key};
  79                $num = @$obj;
  80
  81                # output author
  82                printf "%s (%u):\n", $key, $num;
  83
  84                # output author's 1-line summaries
  85                foreach $desc (reverse @$obj) {
  86                        print "  $desc\n";
  87                        $n_output++;
  88                }
  89
  90                # blank line separating author from next author
  91                print "\n";
  92        }
  93}
  94
  95sub changelog_input {
  96        my ($author, $desc);
  97
  98        while (<>) {
  99                # get author and email
 100                if ($pstate == 1) {
 101                        my ($email);
 102
 103                        next unless /^[Aa]uthor:?\s*(.*?)\s*<(.*)>/;
 104
 105                        $n_records++;
 106
 107                        $author = $1;
 108                        $email = $2;
 109                        $desc = undef;
 110
 111                        # cset author fixups
 112                        if (exists $mailmap{$email}) {
 113                                $author = $mailmap{$email};
 114                        } elsif (exists $mailmap{$author}) {
 115                                $author = $mailmap{$author};
 116                        } elsif (!$author) {
 117                                $author = $email;
 118                        }
 119                        $email{$author}{$email}++;
 120                        $pstate++;
 121                }
 122
 123                # skip to blank line
 124                elsif ($pstate == 2) {
 125                        next unless /^\s*$/;
 126                        $pstate++;
 127                }
 128
 129                # skip to non-blank line
 130                elsif ($pstate == 3) {
 131                        next unless /^\s*?(.*)/;
 132
 133                        # skip lines that are obviously not
 134                        # a 1-line cset description
 135                        next if /^\s*From: /;
 136
 137                        chomp;
 138                        $desc = $1;
 139
 140                        &shortlog_entry($author, $desc);
 141
 142                        $pstate = 1;
 143                }
 144        
 145                else {
 146                        die "invalid parse state $pstate";
 147                }
 148        }
 149}
 150
 151sub read_mailmap {
 152        my ($fh, $mailmap) = @_;
 153        while (<$fh>) {
 154                chomp;
 155                if (/^([^#].*?)\s*<(.*)>/) {
 156                        $mailmap->{$2} = $1;
 157                }
 158        }
 159}
 160
 161sub setup_mailmap {
 162        read_mailmap(\*DATA, \%mailmap);
 163        if (-f '.mailmap') {
 164                my $fh = undef;
 165                open $fh, '<', '.mailmap';
 166                read_mailmap($fh, \%mailmap);
 167                close $fh;
 168        }
 169}
 170
 171sub finalize {
 172        #print "\n$n_records records parsed.\n";
 173
 174        if ($n_records != $n_output) {
 175                die "parse error: input records != output records\n";
 176        }
 177        if (0) {
 178                for my $author (sort keys %email) {
 179                        my $e = $email{$author};
 180                        for my $email (sort keys %$e) {
 181                                print STDERR "$author <$email>\n";
 182                        }
 183                }
 184        }
 185}
 186
 187&setup_mailmap;
 188&changelog_input;
 189$opt_s ? &summary_output : &shortlog_output;
 190&finalize;
 191exit(0);
 192
 193
 194__DATA__
 195#
 196# Even with git, we don't always have name translations.
 197# So have an email->real name table to translate the
 198# (hopefully few) missing names
 199#
 200Adrian Bunk <bunk@stusta.de>
 201Andreas Herrmann <aherrman@de.ibm.com>
 202Andrew Morton <akpm@osdl.org>
 203Andrew Vasquez <andrew.vasquez@qlogic.com>
 204Christoph Hellwig <hch@lst.de>
 205Corey Minyard <minyard@acm.org>
 206David Woodhouse <dwmw2@shinybook.infradead.org>
 207Domen Puncer <domen@coderock.org>
 208Douglas Gilbert <dougg@torque.net>
 209Ed L Cashin <ecashin@coraid.com>
 210Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 211Felix Moeller <felix@derklecks.de>
 212Frank Zago <fzago@systemfabricworks.com>
 213Greg Kroah-Hartman <gregkh@suse.de>
 214James Bottomley <jejb@mulgrave.(none)>
 215James Bottomley <jejb@titanic.il.steeleye.com>
 216Jeff Garzik <jgarzik@pretzel.yyz.us>
 217Jens Axboe <axboe@suse.de>
 218Kay Sievers <kay.sievers@vrfy.org>
 219Mitesh shah <mshah@teja.com>
 220Morten Welinder <terra@gnome.org>
 221Morten Welinder <welinder@anemone.rentec.com>
 222Morten Welinder <welinder@darter.rentec.com>
 223Morten Welinder <welinder@troll.com>
 224Nguyen Anh Quynh <aquynh@gmail.com>
 225Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
 226Peter A Jonsson <pj@ludd.ltu.se>
 227Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
 228Rudolf Marek <R.Marek@sh.cvut.cz>
 229Rui Saraiva <rmps@joel.ist.utl.pt>
 230Sachin P Sant <ssant@in.ibm.com>
 231Santtu Hyrkk\e,Av\e(B <santtu.hyrkko@gmail.com>
 232Simon Kelley <simon@thekelleys.org.uk>
 233Tejun Heo <htejun@gmail.com>
 234Tony Luck <tony.luck@intel.com>