gitlog-to-changelog 5.4 KB

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