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