git-fmt-merge-msg.perlon commit fmt-merge-msg: say which branch things were merged into unless 'master' (2b02045)
   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 $fh;
  32        my $val;
  33        eval {
  34                open $fh, '-|', 'git-repo-config', '--get', 'merge.summary'
  35                    or die "$!";
  36                ($val) = <$fh>;
  37                close $fh;
  38        };
  39        return $val;
  40}
  41
  42sub current_branch {
  43        my $fh;
  44        open $fh, '-|', 'git-symbolic-ref', 'HEAD' or die "$!";
  45        my ($bra) = <$fh>;
  46        $bra =~ s|^refs/heads/||;
  47        if ($bra ne 'master') {
  48                $bra = " into $bra";
  49        } else {
  50                $bra = "";
  51        }
  52
  53        return $bra;
  54}
  55
  56sub shortlog {
  57        my ($tip, $limit) = @_;
  58        my ($fh, @result);
  59        open $fh, '-|', ('git-log', "--max-count=$limit", '--topo-order',
  60                         '--pretty=oneline', $tip, '^HEAD')
  61            or die "$!";
  62        while (<$fh>) {
  63                s/^[0-9a-f]{40}\s+//;
  64                push @result, $_;
  65        }
  66        close $fh or die "$!";
  67        return @result;
  68}
  69
  70my @origin = ();
  71while (<>) {
  72        my ($bname, $tname, $gname, $src, $sha1, $origin);
  73        chomp;
  74        s/^([0-9a-f]*)  //;
  75        $sha1 = $1;
  76        next if (/^not-for-merge/);
  77        s/^     //;
  78        if (s/ of (.*)$//) {
  79                $src = $1;
  80        } else {
  81                # Pulling HEAD
  82                $src = $_;
  83                $_ = 'HEAD';
  84        }
  85        if (! exists $src{$src}) {
  86                push @src, $src;
  87                $src{$src} = {
  88                        BRANCH => [],
  89                        TAG => [],
  90                        GENERIC => [],
  91                        # &1 == has HEAD.
  92                        # &2 == has others.
  93                        HEAD_STATUS => 0,
  94                };
  95        }
  96        if (/^branch (.*)$/) {
  97                $origin = $1;
  98                push @{$src{$src}{BRANCH}}, $1;
  99                $src{$src}{HEAD_STATUS} |= 2;
 100        }
 101        elsif (/^tag (.*)$/) {
 102                $origin = $_;
 103                push @{$src{$src}{TAG}}, $1;
 104                $src{$src}{HEAD_STATUS} |= 2;
 105        }
 106        elsif (/^HEAD$/) {
 107                $origin = $src;
 108                $src{$src}{HEAD_STATUS} |= 1;
 109        }
 110        else {
 111                push @{$src{$src}{GENERIC}}, $_;
 112                $src{$src}{HEAD_STATUS} |= 2;
 113                $origin = $src;
 114        }
 115        if ($src eq '.' || $src eq $origin) {
 116                $origin =~ s/^'(.*)'$/$1/;
 117                push @origin, [$sha1, "$origin"];
 118        }
 119        else {
 120                push @origin, [$sha1, "$origin of $src"];
 121        }
 122}
 123
 124my @msg;
 125for my $src (@src) {
 126        if ($src{$src}{HEAD_STATUS} == 1) {
 127                # Only HEAD is fetched, nothing else.
 128                push @msg, $src;
 129                next;
 130        }
 131        my @this;
 132        if ($src{$src}{HEAD_STATUS} == 3) {
 133                # HEAD is fetched among others.
 134                push @this, andjoin('', '', ['HEAD']);
 135        }
 136        push @this, andjoin("branch ", "branches ",
 137                           $src{$src}{BRANCH});
 138        push @this, andjoin("tag ", "tags ",
 139                           $src{$src}{TAG});
 140        push @this, andjoin("commit ", "commits ",
 141                            $src{$src}{GENERIC});
 142        my $this = join(', ', @this);
 143        if ($src ne '.') {
 144                $this .= " of $src";
 145        }
 146        push @msg, $this;
 147}
 148
 149my $into = current_branch();
 150
 151print "Merge ", join("; ", @msg), $into, "\n";
 152
 153if (!repoconfig) {
 154        exit(0);
 155}
 156
 157# We limit the merge message to the latst 20 or so per each branch.
 158my $limit = 20;
 159
 160for (@origin) {
 161        my ($sha1, $name) = @$_;
 162        my @log = shortlog($sha1, $limit + 1);
 163        if ($limit + 1 <= @log) {
 164                print "\n* $name: (" . scalar(@log) . " commits)\n";
 165        }
 166        else {
 167                print "\n* $name:\n";
 168        }
 169        my $cnt = 0;
 170        for my $log (@log) {
 171                if ($limit < ++$cnt) {
 172                        print "  ...\n";
 173                        last;
 174                }
 175                print "  $log";
 176        }
 177}