1eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}' 2 & eval 'exec perl -wS "$0" $argv:q' 3 if 0; 4# Convert git log output to ChangeLog format. 5 6my $VERSION = '2011-10-31 16:06'; # UTC 7# The definition above must lie within the first 8 lines in order 8# for the Emacs time-stamp write hook (at end) to update it. 9# If you change this file with Emacs, please let the write hook 10# do its job. Otherwise, update this string manually. 11 12# Copyright (C) 2008-2011 Free Software Foundation, Inc. 13 14# This program is free software: you can redistribute it and/or modify 15# it under the terms of the GNU General Public License as published by 16# the Free Software Foundation, either version 3 of the License, or 17# (at your option) any later version. 18 19# This program is distributed in the hope that it will be useful, 20# but WITHOUT ANY WARRANTY; without even the implied warranty of 21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22# GNU General Public License for more details. 23 24# You should have received a copy of the GNU General Public License 25# along with this program. If not, see <http://www.gnu.org/licenses/>. 26 27# Written by Jim Meyering 28 29use strict; 30use warnings; 31use Getopt::Long; 32use POSIX qw(strftime); 33 34(my $ME = $0) =~ s|.*/||; 35 36# use File::Coda; # http://meyering.net/code/Coda/ 37END { 38 defined fileno STDOUT or return; 39 close STDOUT and return; 40 warn "$ME: failed to close standard output: $!\n"; 41 $? ||= 1; 42} 43 44sub usage ($) 45{ 46 my ($exit_code) = @_; 47 my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); 48 if ($exit_code != 0) 49 { 50 print $STREAM "Try `$ME --help' for more information.\n"; 51 } 52 else 53 { 54 print $STREAM <<EOF; 55Usage: $ME [OPTIONS] [ARGS] 56 57Convert git log output to ChangeLog format. If present, any ARGS 58are passed to "git log". To avoid ARGS being parsed as options to 59$ME, they may be preceded by '--'. 60 61OPTIONS: 62 63 --since=DATE convert only the logs since DATE; 64 the default is to convert all log entries. 65 --format=FMT set format string for commit subject and body; 66 see 'man git-log' for the list of format metacharacters; 67 the default is '%s%n%b%n' 68 --append-dot append a dot to the first line of each commit message if 69 there is no other punctuation or blank at the end. 70 71 --help display this help and exit 72 --version output version information and exit 73 74EXAMPLE: 75 76 $ME --since=2008-01-01 > ChangeLog 77 $ME -- -n 5 foo > last-5-commits-to-branch-foo 78 79EOF 80 } 81 exit $exit_code; 82} 83 84# If the string $S is a well-behaved file name, simply return it. 85# If it contains white space, quotes, etc., quote it, and return the new string. 86sub shell_quote($) 87{ 88 my ($s) = @_; 89 if ($s =~ m![^\w+/.,-]!) 90 { 91 # Convert each single quote to '\'' 92 $s =~ s/\'/\'\\\'\'/g; 93 # Then single quote the string. 94 $s = "'$s'"; 95 } 96 return $s; 97} 98 99sub quoted_cmd(@) 100{ 101 return join (' ', map {shell_quote $_} @_); 102} 103 104{ 105 my $since_date; 106 my $format_string = '%s%n%b%n'; 107 my $append_dot = 0; 108 GetOptions 109 ( 110 help => sub { usage 0 }, 111 version => sub { print "$ME version $VERSION\n"; exit }, 112 'since=s' => \$since_date, 113 'format=s' => \$format_string, 114 'append-dot' => \$append_dot, 115 ) or usage 1; 116 117 defined $since_date 118 and unshift @ARGV, "--since=$since_date"; 119 120 my @cmd = (qw (git log --log-size), 121 '--pretty=format:%ct %an <%ae>%n%n'.$format_string, @ARGV); 122 open PIPE, '-|', @cmd 123 or die ("$ME: failed to run `". quoted_cmd (@cmd) ."': $!\n" 124 . "(Is your Git too old? Version 1.5.1 or later is required.)\n"); 125 126 my $prev_date_line = ''; 127 while (1) 128 { 129 defined (my $in = <PIPE>) 130 or last; 131 $in =~ /^log size (\d+)$/ 132 or die "$ME:$.: Invalid line (expected log size):\n$in"; 133 my $log_nbytes = $1; 134 135 my $log; 136 my $n_read = read PIPE, $log, $log_nbytes; 137 $n_read == $log_nbytes 138 or die "$ME:$.: unexpected EOF\n"; 139 140 my @line = split "\n", $log; 141 my $author_line = shift @line; 142 defined $author_line 143 or die "$ME:$.: unexpected EOF\n"; 144 $author_line =~ /^(\d+) (.*>)$/ 145 or die "$ME:$.: Invalid line " 146 . "(expected date/author/email):\n$author_line\n"; 147 148 my $date_line = sprintf "%s $2\n", strftime ("%F", localtime ($1)); 149 # If this line would be the same as the previous date/name/email 150 # line, then arrange not to print it. 151 if ($date_line ne $prev_date_line) 152 { 153 $prev_date_line eq '' 154 or print "\n"; 155 print $date_line; 156 } 157 $prev_date_line = $date_line; 158 159 # Omit "Signed-off-by..." lines. 160 @line = grep !/^Signed-off-by: .*>$/, @line; 161 162 # Remove leading and trailing blank lines. 163 if (@line) 164 { 165 while ($line[0] =~ /^\s*$/) { shift @line; } 166 while ($line[$#line] =~ /^\s*$/) { pop @line; } 167 } 168 169 # If there were any lines 170 if (@line == 0) 171 { 172 warn "$ME: warning: empty commit message:\n $date_line\n"; 173 } 174 else 175 { 176 if ($append_dot) 177 { 178 # If the first line of the message has enough room, then 179 if (length $line[0] < 72) 180 { 181 # append a dot if there is no other punctuation or blank 182 # at the end. 183 $line[0] =~ /[[:punct:]\s]$/ 184 or $line[0] .= '.'; 185 } 186 } 187 188 # Prefix each non-empty line with a TAB. 189 @line = map { length $_ ? "\t$_" : '' } @line; 190 191 print "\n", join ("\n", @line), "\n"; 192 } 193 194 defined ($in = <PIPE>) 195 or last; 196 $in ne "\n" 197 and die "$ME:$.: unexpected line:\n$in"; 198 } 199 200 close PIPE 201 or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n"; 202 # FIXME-someday: include $PROCESS_STATUS in the diagnostic 203} 204 205# Local Variables: 206# mode: perl 207# indent-tabs-mode: nil 208# eval: (add-hook 'write-file-hooks 'time-stamp) 209# time-stamp-start: "my $VERSION = '" 210# time-stamp-format: "%:y-%02m-%02d %02H:%02M" 211# time-stamp-time-zone: "UTC" 212# time-stamp-end: "'; # UTC" 213# End: 214