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