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