git-fmt-merge-msg.perlon commit builtin-push: make it official. (54eb2d3)
   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 --no-merges --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                        R_BRANCH => [],
  79                        GENERIC => [],
  80                        # &1 == has HEAD.
  81                        # &2 == has others.
  82                        HEAD_STATUS => 0,
  83                };
  84        }
  85        if (/^branch (.*)$/) {
  86                $origin = $1;
  87                push @{$src{$src}{BRANCH}}, $1;
  88                $src{$src}{HEAD_STATUS} |= 2;
  89        }
  90        elsif (/^tag (.*)$/) {
  91                $origin = $_;
  92                push @{$src{$src}{TAG}}, $1;
  93                $src{$src}{HEAD_STATUS} |= 2;
  94        }
  95        elsif (/^remote branch (.*)$/) {
  96                $origin = $1;
  97                push @{$src{$src}{R_BRANCH}}, $1;
  98                $src{$src}{HEAD_STATUS} |= 2;
  99        }
 100        elsif (/^HEAD$/) {
 101                $origin = $src;
 102                $src{$src}{HEAD_STATUS} |= 1;
 103        }
 104        else {
 105                push @{$src{$src}{GENERIC}}, $_;
 106                $src{$src}{HEAD_STATUS} |= 2;
 107                $origin = $src;
 108        }
 109        if ($src eq '.' || $src eq $origin) {
 110                $origin =~ s/^'(.*)'$/$1/;
 111                push @origin, [$sha1, "$origin"];
 112        }
 113        else {
 114                push @origin, [$sha1, "$origin of $src"];
 115        }
 116}
 117
 118my @msg;
 119for my $src (@src) {
 120        if ($src{$src}{HEAD_STATUS} == 1) {
 121                # Only HEAD is fetched, nothing else.
 122                push @msg, $src;
 123                next;
 124        }
 125        my @this;
 126        if ($src{$src}{HEAD_STATUS} == 3) {
 127                # HEAD is fetched among others.
 128                push @this, andjoin('', '', ['HEAD']);
 129        }
 130        push @this, andjoin("branch ", "branches ",
 131                           $src{$src}{BRANCH});
 132        push @this, andjoin("remote branch ", "remote branches ",
 133                           $src{$src}{R_BRANCH});
 134        push @this, andjoin("tag ", "tags ",
 135                           $src{$src}{TAG});
 136        push @this, andjoin("commit ", "commits ",
 137                            $src{$src}{GENERIC});
 138        my $this = join(', ', @this);
 139        if ($src ne '.') {
 140                $this .= " of $src";
 141        }
 142        push @msg, $this;
 143}
 144
 145my $into = current_branch();
 146
 147print "Merge ", join("; ", @msg), $into, "\n";
 148
 149if (!repoconfig) {
 150        exit(0);
 151}
 152
 153# We limit the merge message to the latst 20 or so per each branch.
 154my $limit = 20;
 155
 156for (@origin) {
 157        my ($sha1, $name) = @$_;
 158        my @log = shortlog($sha1);
 159        if ($limit + 1 <= @log) {
 160                print "\n* $name: (" . scalar(@log) . " commits)\n";
 161        }
 162        else {
 163                print "\n* $name:\n";
 164        }
 165        my $cnt = 0;
 166        for my $log (@log) {
 167                if ($limit < ++$cnt) {
 168                        print "  ...\n";
 169                        last;
 170                }
 171                print "  $log";
 172        }
 173}