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