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