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 = '2009-10-30 13:46'; # 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 69 --help display this help and exit 70 --version output version information and exit 71 72EXAMPLE: 73 74 $ME --since=2008-01-01 > ChangeLog 75 $ME -- -n 5 foo > last-5-commits-to-branch-foo 76 77EOF 78 } 79 exit $exit_code; 80} 81 82# If the string $S is a well-behaved file name, simply return it. 83# If it contains white space, quotes, etc., quote it, and return the new string. 84sub shell_quote($) 85{ 86 my ($s) = @_; 87 if ($s =~ m![^\w+/.,-]!) 88 { 89 # Convert each single quote to '\'' 90 $s =~ s/\'/\'\\\'\'/g; 91 # Then single quote the string. 92 $s = "'$s'"; 93 } 94 return $s; 95} 96 97sub quoted_cmd(@) 98{ 99 return join (' ', map {shell_quote $_} @_); 100} 101 102{ 103 my $since_date = '1970-01-01 UTC'; 104 my $format_string = '%s%n%b%n'; 105 GetOptions 106 ( 107 help => sub { usage 0 }, 108 version => sub { print "$ME version $VERSION\n"; exit }, 109 'since=s' => \$since_date, 110 'format=s' => \$format_string, 111 ) or usage 1; 112 113 my @cmd = (qw (git log --log-size), "--since=$since_date", 114 '--pretty=format:%ct %an <%ae>%n%n'.$format_string, @ARGV); 115 open PIPE, '-|', @cmd 116 or die ("$ME: failed to run `". quoted_cmd (@cmd) ."': $!\n" 117 . "(Is your Git too old? Version 1.5.1 or later is required.)\n"); 118 119 my $prev_date_line = ''; 120 while (1) 121 { 122 defined (my $in = <PIPE>) 123 or last; 124 $in =~ /^log size (\d+)$/ 125 or die "$ME:$.: Invalid line (expected log size):\n$in"; 126 my $log_nbytes = $1; 127 128 my $log; 129 my $n_read = read PIPE, $log, $log_nbytes; 130 $n_read == $log_nbytes 131 or die "$ME:$.: unexpected EOF\n"; 132 133 my @line = split "\n", $log; 134 my $author_line = shift @line; 135 defined $author_line 136 or die "$ME:$.: unexpected EOF\n"; 137 $author_line =~ /^(\d+) (.*>)$/ 138 or die "$ME:$.: Invalid line " 139 . "(expected date/author/email):\n$author_line\n"; 140 141 my $date_line = sprintf "%s $2\n", strftime ("%F", localtime ($1)); 142 # If this line would be the same as the previous date/name/email 143 # line, then arrange not to print it. 144 if ($date_line ne $prev_date_line) 145 { 146 $prev_date_line eq '' 147 or print "\n"; 148 print $date_line; 149 } 150 $prev_date_line = $date_line; 151 152 # Omit "Signed-off-by..." lines. 153 @line = grep !/^Signed-off-by: .*>$/, @line; 154 155 # If there were any lines 156 if (@line == 0) 157 { 158 warn "$ME: warning: empty commit message:\n $date_line\n"; 159 } 160 else 161 { 162 # Remove leading and trailing blank lines. 163 while ($line[0] =~ /^\s*$/) { shift @line; } 164 while ($line[$#line] =~ /^\s*$/) { pop @line; } 165 166 # Prefix each non-empty line with a TAB. 167 @line = map { length $_ ? "\t$_" : '' } @line; 168 169 print "\n", join ("\n", @line), "\n"; 170 } 171 172 defined ($in = <PIPE>) 173 or last; 174 $in ne "\n" 175 and die "$ME:$.: unexpected line:\n$in"; 176 } 177 178 close PIPE 179 or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n"; 180 # FIXME-someday: include $PROCESS_STATUS in the diagnostic 181} 182 183# Local Variables: 184# mode: perl 185# indent-tabs-mode: nil 186# eval: (add-hook 'write-file-hooks 'time-stamp) 187# time-stamp-start: "my $VERSION = '" 188# time-stamp-format: "%:y-%02m-%02d %02H:%02M" 189# time-stamp-time-zone: "UTC" 190# time-stamp-end: "'; # UTC" 191# End: 192