| #!/usr/bin/perl -w |
| # |
| # Copyright (c) 2005 Junio C Hamano |
| # |
| # Read .git/FETCH_HEAD and make a human readable merge message |
| # by grouping branches and tags together to form a single line. |
| |
| use strict; |
| |
| my @src; |
| my %src; |
| sub andjoin { |
| my ($label, $labels, $stuff) = @_; |
| my $l = scalar @$stuff; |
| my $m = ''; |
| if ($l == 0) { |
| return (); |
| } |
| if ($l == 1) { |
| $m = "$label$stuff->[0]"; |
| } |
| else { |
| $m = ("$labels" . |
| join (', ', @{$stuff}[0..$l-2]) . |
| " and $stuff->[-1]"); |
| } |
| return ($m); |
| } |
| |
| sub repoconfig { |
| my ($val) = qx{git-repo-config --get merge.summary}; |
| return $val; |
| } |
| |
| sub current_branch { |
| my ($bra) = qx{git-symbolic-ref HEAD}; |
| chomp($bra); |
| $bra =~ s|^refs/heads/||; |
| if ($bra ne 'master') { |
| $bra = " into $bra"; |
| } else { |
| $bra = ""; |
| } |
| return $bra; |
| } |
| |
| sub shortlog { |
| my ($tip) = @_; |
| my @result; |
| foreach ( qx{git-log --no-merges --topo-order --pretty=oneline $tip ^HEAD} ) { |
| s/^[0-9a-f]{40}\s+//; |
| push @result, $_; |
| } |
| die "git-log failed\n" if $?; |
| return @result; |
| } |
| |
| my @origin = (); |
| while (<>) { |
| my ($bname, $tname, $gname, $src, $sha1, $origin); |
| chomp; |
| s/^([0-9a-f]*) //; |
| $sha1 = $1; |
| next if (/^not-for-merge/); |
| s/^ //; |
| if (s/ of (.*)$//) { |
| $src = $1; |
| } else { |
| # Pulling HEAD |
| $src = $_; |
| $_ = 'HEAD'; |
| } |
| if (! exists $src{$src}) { |
| push @src, $src; |
| $src{$src} = { |
| BRANCH => [], |
| TAG => [], |
| R_BRANCH => [], |
| GENERIC => [], |
| # &1 == has HEAD. |
| # &2 == has others. |
| HEAD_STATUS => 0, |
| }; |
| } |
| if (/^branch (.*)$/) { |
| $origin = $1; |
| push @{$src{$src}{BRANCH}}, $1; |
| $src{$src}{HEAD_STATUS} |= 2; |
| } |
| elsif (/^tag (.*)$/) { |
| $origin = $_; |
| push @{$src{$src}{TAG}}, $1; |
| $src{$src}{HEAD_STATUS} |= 2; |
| } |
| elsif (/^remote branch (.*)$/) { |
| $origin = $1; |
| push @{$src{$src}{R_BRANCH}}, $1; |
| $src{$src}{HEAD_STATUS} |= 2; |
| } |
| elsif (/^HEAD$/) { |
| $origin = $src; |
| $src{$src}{HEAD_STATUS} |= 1; |
| } |
| else { |
| push @{$src{$src}{GENERIC}}, $_; |
| $src{$src}{HEAD_STATUS} |= 2; |
| $origin = $src; |
| } |
| if ($src eq '.' || $src eq $origin) { |
| $origin =~ s/^'(.*)'$/$1/; |
| push @origin, [$sha1, "$origin"]; |
| } |
| else { |
| push @origin, [$sha1, "$origin of $src"]; |
| } |
| } |
| |
| my @msg; |
| for my $src (@src) { |
| if ($src{$src}{HEAD_STATUS} == 1) { |
| # Only HEAD is fetched, nothing else. |
| push @msg, $src; |
| next; |
| } |
| my @this; |
| if ($src{$src}{HEAD_STATUS} == 3) { |
| # HEAD is fetched among others. |
| push @this, andjoin('', '', ['HEAD']); |
| } |
| push @this, andjoin("branch ", "branches ", |
| $src{$src}{BRANCH}); |
| push @this, andjoin("remote branch ", "remote branches ", |
| $src{$src}{R_BRANCH}); |
| push @this, andjoin("tag ", "tags ", |
| $src{$src}{TAG}); |
| push @this, andjoin("commit ", "commits ", |
| $src{$src}{GENERIC}); |
| my $this = join(', ', @this); |
| if ($src ne '.') { |
| $this .= " of $src"; |
| } |
| push @msg, $this; |
| } |
| |
| my $into = current_branch(); |
| |
| print "Merge ", join("; ", @msg), $into, "\n"; |
| |
| if (!repoconfig) { |
| exit(0); |
| } |
| |
| # We limit the merge message to the latst 20 or so per each branch. |
| my $limit = 20; |
| |
| for (@origin) { |
| my ($sha1, $name) = @$_; |
| my @log = shortlog($sha1); |
| if ($limit + 1 <= @log) { |
| print "\n* $name: (" . scalar(@log) . " commits)\n"; |
| } |
| else { |
| print "\n* $name:\n"; |
| } |
| my $cnt = 0; |
| for my $log (@log) { |
| if ($limit < ++$cnt) { |
| print " ...\n"; |
| last; |
| } |
| print " $log"; |
| } |
| } |