Merge branch 'jc/perl'
[git.git] / git-fmt-merge-msg.perl
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
8 use strict;
9
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 }
29
30 sub 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
43 sub 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
62 sub 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
79 my @origin = ();
80 while (<>) {
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
133 my @msg;
134 for 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
158 my $into = current_branch();
159
160 print "Merge ", join("; ", @msg), $into, "\n";
161
162 if (!repoconfig) {
163         exit(0);
164 }
165
166 # We limit the merge message to the latst 20 or so per each branch.
167 my $limit = 20;
168
169 for (@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 }