Blame gitlog-to-changelog

Packit 8101d8
#!/usr/bin/perl
Packit 8101d8
# Convert git log output to ChangeLog format.
Packit 8101d8
Packit 8101d8
my $VERSION = '2008-12-21 12:07'; # UTC
Packit 8101d8
# The definition above must lie within the first 8 lines in order
Packit 8101d8
# for the Emacs time-stamp write hook (at end) to update it.
Packit 8101d8
# If you change this file with Emacs, please let the write hook
Packit 8101d8
# do its job.  Otherwise, update this string manually.
Packit 8101d8
Packit 8101d8
# Copyright (C) 2008 Free Software Foundation, Inc.
Packit 8101d8
Packit 8101d8
# This program is free software: you can redistribute it and/or modify
Packit 8101d8
# it under the terms of the GNU General Public License as published by
Packit 8101d8
# the Free Software Foundation, either version 3 of the License, or
Packit 8101d8
# (at your option) any later version.
Packit 8101d8
Packit 8101d8
# This program is distributed in the hope that it will be useful,
Packit 8101d8
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8101d8
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8101d8
# GNU General Public License for more details.
Packit 8101d8
Packit 8101d8
# You should have received a copy of the GNU General Public License
Packit 8101d8
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8101d8
Packit 8101d8
# Written by Jim Meyering
Packit 8101d8
Packit 8101d8
use strict;
Packit 8101d8
use warnings;
Packit 8101d8
use Getopt::Long;
Packit 8101d8
use POSIX qw(strftime);
Packit 8101d8
Packit 8101d8
(my $ME = $0) =~ s|.*/||;
Packit 8101d8
Packit 8101d8
# use File::Coda; # http://meyering.net/code/Coda/
Packit 8101d8
END {
Packit 8101d8
  defined fileno STDOUT or return;
Packit 8101d8
  close STDOUT and return;
Packit 8101d8
  warn "$ME: failed to close standard output: $!\n";
Packit 8101d8
  $? ||= 1;
Packit 8101d8
}
Packit 8101d8
Packit 8101d8
sub usage ($)
Packit 8101d8
{
Packit 8101d8
  my ($exit_code) = @_;
Packit 8101d8
  my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
Packit 8101d8
  if ($exit_code != 0)
Packit 8101d8
    {
Packit 8101d8
      print $STREAM "Try `$ME --help' for more information.\n";
Packit 8101d8
    }
Packit 8101d8
  else
Packit 8101d8
    {
Packit 8101d8
      print $STREAM <
Packit 8101d8
Usage: $ME [OPTIONS] [ARGS]
Packit 8101d8
Packit 8101d8
Convert git log output to ChangeLog format.  If present, any ARGS
Packit 8101d8
are passed to "git log".  To avoid ARGS being parsed as options to
Packit 8101d8
$ME, they may be preceded by '--'.
Packit 8101d8
Packit 8101d8
OPTIONS:
Packit 8101d8
Packit 8101d8
   --since=DATE convert only the logs since DATE;
Packit 8101d8
                  the default is to convert all log entries.
Packit 8101d8
Packit 8101d8
   --help       display this help and exit
Packit 8101d8
   --version    output version information and exit
Packit 8101d8
Packit 8101d8
EXAMPLE:
Packit 8101d8
Packit 8101d8
  $ME --since=2008-01-01 > ChangeLog
Packit 8101d8
  $ME -- -n 5 foo > last-5-commits-to-branch-foo
Packit 8101d8
Packit 8101d8
EOF
Packit 8101d8
    }
Packit 8101d8
  exit $exit_code;
Packit 8101d8
}
Packit 8101d8
Packit 8101d8
# If the string $S is a well-behaved file name, simply return it.
Packit 8101d8
# If it contains white space, quotes, etc., quote it, and return the new string.
Packit 8101d8
sub shell_quote($)
Packit 8101d8
{
Packit 8101d8
  my ($s) = @_;
Packit 8101d8
  if ($s =~ m![^\w+/.,-]!)
Packit 8101d8
    {
Packit 8101d8
      # Convert each single quote to '\''
Packit 8101d8
      $s =~ s/\'/\'\\\'\'/g;
Packit 8101d8
      # Then single quote the string.
Packit 8101d8
      $s = "'$s'";
Packit 8101d8
    }
Packit 8101d8
  return $s;
Packit 8101d8
}
Packit 8101d8
Packit 8101d8
sub quoted_cmd(@)
Packit 8101d8
{
Packit 8101d8
  return join (' ', map {shell_quote $_} @_);
Packit 8101d8
}
Packit 8101d8
Packit 8101d8
{
Packit 8101d8
  my $since_date = '1970-01-01 UTC';
Packit 8101d8
  GetOptions
Packit 8101d8
    (
Packit 8101d8
     help => sub { usage 0 },
Packit 8101d8
     version => sub { print "$ME version $VERSION\n"; exit },
Packit 8101d8
     'since=s' => \$since_date,
Packit 8101d8
    ) or usage 1;
Packit 8101d8
Packit 8101d8
  my @cmd = (qw (git log --log-size), "--since=$since_date",
Packit 8101d8
             '--pretty=format:%ct  %an  <%ae>%n%n%s%n%b%n', @ARGV);
Packit 8101d8
  open PIPE, '-|', @cmd
Packit 8101d8
    or die ("$ME: failed to run `". quoted_cmd (@cmd) ."': $!\n"
Packit 8101d8
            . "(Is your Git too old?  Version 1.5.1 or later is required.)\n");
Packit 8101d8
Packit 8101d8
  my $prev_date_line = '';
Packit 8101d8
  while (1)
Packit 8101d8
    {
Packit 8101d8
      defined (my $in = <PIPE>)
Packit 8101d8
        or last;
Packit 8101d8
      $in =~ /^log size (\d+)$/
Packit 8101d8
        or die "$ME:$.: Invalid line (expected log size):\n$in";
Packit 8101d8
      my $log_nbytes = $1;
Packit 8101d8
Packit 8101d8
      my $log;
Packit 8101d8
      my $n_read = read PIPE, $log, $log_nbytes;
Packit 8101d8
      $n_read == $log_nbytes
Packit 8101d8
        or die "$ME:$.: unexpected EOF\n";
Packit 8101d8
Packit 8101d8
      my @line = split "\n", $log;
Packit 8101d8
      my $author_line = shift @line;
Packit 8101d8
      defined $author_line
Packit 8101d8
        or die "$ME:$.: unexpected EOF\n";
Packit 8101d8
      $author_line =~ /^(\d+)  (.*>)$/
Packit 8101d8
        or die "$ME:$.: Invalid line "
Packit 8101d8
          . "(expected date/author/email):\n$author_line\n";
Packit 8101d8
Packit 8101d8
      my $date_line = sprintf "%s  $2\n", strftime ("%F", localtime ($1));
Packit 8101d8
      # If this line would be the same as the previous date/name/email
Packit 8101d8
      # line, then arrange not to print it.
Packit 8101d8
      if ($date_line ne $prev_date_line)
Packit 8101d8
        {
Packit 8101d8
          $prev_date_line eq ''
Packit 8101d8
            or print "\n";
Packit 8101d8
          print $date_line;
Packit 8101d8
        }
Packit 8101d8
      $prev_date_line = $date_line;
Packit 8101d8
Packit 8101d8
      # Omit "Signed-off-by..." lines.
Packit 8101d8
      @line = grep !/^Signed-off-by: .*>$/, @line;
Packit 8101d8
Packit 8101d8
      # Remove leading and trailing blank lines.
Packit 8101d8
      while ($line[0] =~ /^\s*$/) { shift @line; }
Packit 8101d8
      while ($line[$#line] =~ /^\s*$/) { pop @line; }
Packit 8101d8
Packit 8101d8
      # Prefix each non-empty line with a TAB.
Packit 8101d8
      @line = map { length $_ ? "\t$_" : '' } @line;
Packit 8101d8
Packit 8101d8
      print "\n", join ("\n", @line), "\n";
Packit 8101d8
Packit 8101d8
      defined ($in = <PIPE>)
Packit 8101d8
        or last;
Packit 8101d8
      $in ne "\n"
Packit 8101d8
        and die "$ME:$.: unexpected line:\n$in";
Packit 8101d8
    }
Packit 8101d8
Packit 8101d8
  close PIPE
Packit 8101d8
    or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n";
Packit 8101d8
  # FIXME-someday: include $PROCESS_STATUS in the diagnostic
Packit 8101d8
}
Packit 8101d8
Packit 8101d8
# Local Variables:
Packit 8101d8
# indent-tabs-mode: nil
Packit 8101d8
# eval: (add-hook 'write-file-hooks 'time-stamp)
Packit 8101d8
# time-stamp-start: "my $VERSION = '"
Packit 8101d8
# time-stamp-format: "%:y-%02m-%02d %02H:%02M"
Packit 8101d8
# time-stamp-time-zone: "UTC"
Packit 8101d8
# time-stamp-end: "'; # UTC"
Packit 8101d8
# End: