git-fmt-merge-msg.perlon commit Merge branch 'pj/portable' into next (deddce6)
   1#!/usr/bin/perl -w
   2#
   3# Copyright (c) 2005 Junio C Hamano
   4#
   5# Read .git/FETCH_HEAD and make a human readable merge message
   6# by grouping branches and tags together to form a single line.
   7
   8use strict;
   9
  10my @src;
  11my %src;
  12sub andjoin {
  13        my ($label, $labels, $stuff) = @_;
  14        my $l = scalar @$stuff;
  15        my $m = '';
  16        if ($l == 0) {
  17                return ();
  18        }
  19        if ($l == 1) {
  20                $m = "$label$stuff->[0]";
  21        }
  22        else {
  23                $m = ("$labels" .
  24                      join (', ', @{$stuff}[0..$l-2]) .
  25                      " and $stuff->[-1]");
  26        }
  27        return ($m);
  28}
  29
  30sub repoconfig {
  31        my $val;
  32        eval {
  33                my $pid = open(my $fh, '-|');
  34                if (!$pid) {
  35                        exec('git-repo-config', '--get', 'merge.summary');
  36                }
  37                ($val) = <$fh>;
  38                close $fh;
  39        };
  40        return $val;
  41}
  42
  43sub current_branch {
  44        my $fh;
  45        my $pid = open($fh, '-|');
  46        die "$!" unless defined $pid;
  47        if (!$pid) {
  48            exec('git-symbolic-ref', 'HEAD') or die "$!";
  49        }
  50        my ($bra) = <$fh>;
  51        chomp($bra);
  52        close $fh or die "$!";
  53        $bra =~ s|^refs/heads/||;
  54        if ($bra ne 'master') {
  55                $bra = " into $bra";
  56        } else {
  57                $bra = "";
  58        }
  59        return $bra;
  60}
  61
  62sub shortlog {
  63        my ($tip) = @_;
  64        my ($fh, @result);
  65        my $pid = open($fh, '-|');
  66        die "$!" unless defined $pid;
  67        if (!$pid) {
  68            exec('git-log', '--topo-order',
  69                 '--pretty=oneline', $tip, '^HEAD') or die "$!";
  70        }
  71        while (<$fh>) {
  72                s/^[0-9a-f]{40}\s+//;
  73                push @result, $_;
  74        }
  75        close $fh or die "$!";
  76        return @result;
  77}
  78
  79my @origin = ();
  80while (<>) {
  81        my ($bname, $tname, $gname, $src, $sha1, $origin);
  82        chomp;
  83        s/^([0-9a-f]*)  //;
  84        $sha1 = $1;
  85        next if (/^not-for-merge/);
  86        s/^     //;
  87        if (s/ of (.*)$//) {
  88                $src = $1;
  89        } else {
  90                # Pulling HEAD
  91                $src = $_;
  92                $_ = 'HEAD';
  93        }
  94        if (! exists $src{$src}) {
  95                push @src, $src;
  96                $src{$src} = {
  97                        BRANCH => [],
  98                        TAG => [],
  99                        GENERIC => [],
 100                        # &1 == has HEAD.
 101                        # &2 == has others.
 102                        HEAD_STATUS => 0,
 103                };
 104        }
 105        if (/^branch (.*)$/) {
 106                $origin = $1;
 107                push @{$src{$src}{BRANCH}}, $1;
 108                $src{$src}{HEAD_STATUS} |= 2;
 109        }
 110        elsif (/^tag (.*)$/) {
 111                $origin = $_;
 112                push @{$src{$src}{TAG}}, $1;
 113                $src{$src}{HEAD_STATUS} |= 2;
 114        }
 115        elsif (/^HEAD$/) {
 116                $origin = $src;
 117                $src{$src}{HEAD_STATUS} |= 1;
 118        }
 119        else {
 120                push @{$src{$src}{GENERIC}}, $_;
 121                $src{$src}{HEAD_STATUS} |= 2;
 122                $origin = $src;
 123        }
 124        if ($src eq '.' || $src eq $origin) {
 125                $origin =~ s/^'(.*)'$/$1/;
 126                push @origin, [$sha1, "$origin"];
 127        }
 128        else {
 129                push @origin, [$sha1, "$origin of $src"];
 130        }
 131}
 132
 133my @msg;
 134for my $src (@src) {
 135        if ($src{$src}{HEAD_STATUS} == 1) {
 136                # Only HEAD is fetched, nothing else.
 137                push @msg, $src;
 138                next;
 139        }
 140        my @this;
 141        if ($src{$src}{HEAD_STATUS} == 3) {
 142                # HEAD is fetched among others.
 143                push @this, andjoin('', '', ['HEAD']);
 144        }
 145        push @this, andjoin("branch ", "branches ",
 146                           $src{$src}{BRANCH});
 147        push @this, andjoin("tag ", "tags ",
 148                           $src{$src}{TAG});
 149        push @this, andjoin("commit ", "commits ",
 150                            $src{$src}{GENERIC});
 151        my $this = join(', ', @this);
 152        if ($src ne '.') {
 153                $this .= " of $src";
 154        }
 155        push @msg, $this;
 156}
 157
 158my $into = current_branch();
 159
 160print "Merge ", join("; ", @msg), $into, "\n";
 161
 162if (!repoconfig) {
 163        exit(0);
 164}
 165
 166# We limit the merge message to the latst 20 or so per each branch.
 167my $limit = 20;
 168
 169for (@origin) {
 170        my ($sha1, $name) = @$_;
 171        my @log = shortlog($sha1);
 172        if ($limit + 1 <= @log) {
 173                print "\n* $name: (" . scalar(@log) . " commits)\n";
 174        }
 175        else {
 176                print "\n* $name:\n";
 177        }
 178        my $cnt = 0;
 179        for my $log (@log) {
 180                if ($limit < ++$cnt) {
 181                        print "  ...\n";
 182                        last;
 183                }
 184                print "  $log";
 185        }
 186}