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