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