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