Blame build-aux/gitlog-to-changelog

Packit Service 9646c7
eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}'
Packit Service 9646c7
  & eval 'exec perl -wS "$0" $argv:q'
Packit Service 9646c7
    if 0;
Packit Service 9646c7
# Convert git log output to ChangeLog format.
Packit Service 9646c7
Packit Service 9646c7
my $VERSION = '2012-01-18 07:50'; # UTC
Packit Service 9646c7
# The definition above must lie within the first 8 lines in order
Packit Service 9646c7
# for the Emacs time-stamp write hook (at end) to update it.
Packit Service 9646c7
# If you change this file with Emacs, please let the write hook
Packit Service 9646c7
# do its job.  Otherwise, update this string manually.
Packit Service 9646c7
Packit Service 9646c7
# Copyright (C) 2008-2012 Free Software Foundation, Inc.
Packit Service 9646c7
Packit Service 9646c7
# This program is free software: you can redistribute it and/or modify
Packit Service 9646c7
# it under the terms of the GNU General Public License as published by
Packit Service 9646c7
# the Free Software Foundation, either version 3 of the License, or
Packit Service 9646c7
# (at your option) any later version.
Packit Service 9646c7
Packit Service 9646c7
# This program is distributed in the hope that it will be useful,
Packit Service 9646c7
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 9646c7
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 9646c7
# GNU General Public License for more details.
Packit Service 9646c7
Packit Service 9646c7
# You should have received a copy of the GNU General Public License
Packit Service 9646c7
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service 9646c7
Packit Service 9646c7
# Written by Jim Meyering
Packit Service 9646c7
Packit Service 9646c7
use strict;
Packit Service 9646c7
use warnings;
Packit Service 9646c7
use Getopt::Long;
Packit Service 9646c7
use POSIX qw(strftime);
Packit Service 9646c7
Packit Service 9646c7
(my $ME = $0) =~ s|.*/||;
Packit Service 9646c7
Packit Service 9646c7
# use File::Coda; # http://meyering.net/code/Coda/
Packit Service 9646c7
END {
Packit Service 9646c7
  defined fileno STDOUT or return;
Packit Service 9646c7
  close STDOUT and return;
Packit Service 9646c7
  warn "$ME: failed to close standard output: $!\n";
Packit Service 9646c7
  $? ||= 1;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
sub usage ($)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($exit_code) = @_;
Packit Service 9646c7
  my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
Packit Service 9646c7
  if ($exit_code != 0)
Packit Service 9646c7
    {
Packit Service 9646c7
      print $STREAM "Try '$ME --help' for more information.\n";
Packit Service 9646c7
    }
Packit Service 9646c7
  else
Packit Service 9646c7
    {
Packit Service 9646c7
      print $STREAM <
Packit Service 9646c7
Usage: $ME [OPTIONS] [ARGS]
Packit Service 9646c7
Packit Service 9646c7
Convert git log output to ChangeLog format.  If present, any ARGS
Packit Service 9646c7
are passed to "git log".  To avoid ARGS being parsed as options to
Packit Service 9646c7
$ME, they may be preceded by '--'.
Packit Service 9646c7
Packit Service 9646c7
OPTIONS:
Packit Service 9646c7
Packit Service 9646c7
   --amend=FILE FILE maps from an SHA1 to perl code (i.e., s/old/new/) that
Packit Service 9646c7
                  makes a change to SHA1's commit log text or metadata.
Packit Service 9646c7
   --append-dot append a dot to the first line of each commit message if
Packit Service 9646c7
                  there is no other punctuation or blank at the end.
Packit Service 9646c7
   --no-cluster never cluster commit messages under the same date/author
Packit Service 9646c7
                  header; the default is to cluster adjacent commit messages
Packit Service 9646c7
                  if their headers are the same and neither commit message
Packit Service 9646c7
                  contains multiple paragraphs.
Packit Service 9646c7
   --since=DATE convert only the logs since DATE;
Packit Service 9646c7
                  the default is to convert all log entries.
Packit Service 9646c7
   --format=FMT set format string for commit subject and body;
Packit Service 9646c7
                  see 'man git-log' for the list of format metacharacters;
Packit Service 9646c7
                  the default is '%s%n%b%n'
Packit Service 9646c7
Packit Service 9646c7
   --help       display this help and exit
Packit Service 9646c7
   --version    output version information and exit
Packit Service 9646c7
Packit Service 9646c7
EXAMPLE:
Packit Service 9646c7
Packit Service 9646c7
  $ME --since=2008-01-01 > ChangeLog
Packit Service 9646c7
  $ME -- -n 5 foo > last-5-commits-to-branch-foo
Packit Service 9646c7
Packit Service 9646c7
SPECIAL SYNTAX:
Packit Service 9646c7
Packit Service 9646c7
The following types of strings are interpreted specially when they appear
Packit Service 9646c7
at the beginning of a log message line.  They are not copied to the output.
Packit Service 9646c7
Packit Service 9646c7
  Copyright-paperwork-exempt: Yes
Packit Service 9646c7
    Append the "(tiny change)" notation to the usual "date name email"
Packit Service 9646c7
    ChangeLog header to mark a change that does not require a copyright
Packit Service 9646c7
    assignment.
Packit Service 9646c7
  Co-authored-by: Joe User <user\@example.com>
Packit Service 9646c7
    List the specified name and email address on a second
Packit Service 9646c7
    ChangeLog header, denoting a co-author.
Packit Service 9646c7
  Signed-off-by: Joe User <user\@example.com>
Packit Service 9646c7
    These lines are simply elided.
Packit Service 9646c7
Packit Service 9646c7
In a FILE specified via --amend, comment lines (starting with "#") are ignored.
Packit Service 9646c7
FILE must consist of <SHA,CODE+> pairs where SHA is a 40-byte SHA1 (alone on
Packit Service 9646c7
a line) referring to a commit in the current project, and CODE refers to one
Packit Service 9646c7
or more consecutive lines of Perl code.  Pairs must be separated by one or
Packit Service 9646c7
more blank line.
Packit Service 9646c7
Packit Service 9646c7
Here is sample input for use with --amend=FILE, from coreutils:
Packit Service 9646c7
Packit Service 9646c7
3a169f4c5d9159283548178668d2fae6fced3030
Packit Service 9646c7
# fix typo in title:
Packit Service 9646c7
s/all tile types/all file types/
Packit Service 9646c7
Packit Service 9646c7
1379ed974f1fa39b12e2ffab18b3f7a607082202
Packit Service 9646c7
# Due to a bug in vc-dwim, I mis-attributed a patch by Paul to myself.
Packit Service 9646c7
# Change the author to be Paul.  Note the escaped "@":
Packit Service 9646c7
s,Jim .*>,Paul Eggert <eggert\\\@cs.ucla.edu>,
Packit Service 9646c7
Packit Service 9646c7
EOF
Packit Service 9646c7
    }
Packit Service 9646c7
  exit $exit_code;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
# If the string $S is a well-behaved file name, simply return it.
Packit Service 9646c7
# If it contains white space, quotes, etc., quote it, and return the new string.
Packit Service 9646c7
sub shell_quote($)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($s) = @_;
Packit Service 9646c7
  if ($s =~ m![^\w+/.,-]!)
Packit Service 9646c7
    {
Packit Service 9646c7
      # Convert each single quote to '\''
Packit Service 9646c7
      $s =~ s/\'/\'\\\'\'/g;
Packit Service 9646c7
      # Then single quote the string.
Packit Service 9646c7
      $s = "'$s'";
Packit Service 9646c7
    }
Packit Service 9646c7
  return $s;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
sub quoted_cmd(@)
Packit Service 9646c7
{
Packit Service 9646c7
  return join (' ', map {shell_quote $_} @_);
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
# Parse file F.
Packit Service 9646c7
# Comment lines (starting with "#") are ignored.
Packit Service 9646c7
# F must consist of <SHA,CODE+> pairs where SHA is a 40-byte SHA1
Packit Service 9646c7
# (alone on a line) referring to a commit in the current project, and
Packit Service 9646c7
# CODE refers to one or more consecutive lines of Perl code.
Packit Service 9646c7
# Pairs must be separated by one or more blank line.
Packit Service 9646c7
sub parse_amend_file($)
Packit Service 9646c7
{
Packit Service 9646c7
  my ($f) = @_;
Packit Service 9646c7
Packit Service 9646c7
  open F, '<', $f
Packit Service 9646c7
    or die "$ME: $f: failed to open for reading: $!\n";
Packit Service 9646c7
Packit Service 9646c7
  my $fail;
Packit Service 9646c7
  my $h = {};
Packit Service 9646c7
  my $in_code = 0;
Packit Service 9646c7
  my $sha;
Packit Service 9646c7
  while (defined (my $line = <F>))
Packit Service 9646c7
    {
Packit Service 9646c7
      $line =~ /^\#/
Packit Service 9646c7
        and next;
Packit Service 9646c7
      chomp $line;
Packit Service 9646c7
      $line eq ''
Packit Service 9646c7
        and $in_code = 0, next;
Packit Service 9646c7
Packit Service 9646c7
      if (!$in_code)
Packit Service 9646c7
        {
Packit Service 9646c7
          $line =~ /^([0-9a-fA-F]{40})$/
Packit Service 9646c7
            or (warn "$ME: $f:$.: invalid line; expected an SHA1\n"),
Packit Service 9646c7
              $fail = 1, next;
Packit Service 9646c7
          $sha = lc $1;
Packit Service 9646c7
          $in_code = 1;
Packit Service 9646c7
          exists $h->{$sha}
Packit Service 9646c7
            and (warn "$ME: $f:$.: duplicate SHA1\n"),
Packit Service 9646c7
              $fail = 1, next;
Packit Service 9646c7
        }
Packit Service 9646c7
      else
Packit Service 9646c7
        {
Packit Service 9646c7
          $h->{$sha} ||= '';
Packit Service 9646c7
          $h->{$sha} .= "$line\n";
Packit Service 9646c7
        }
Packit Service 9646c7
    }
Packit Service 9646c7
  close F;
Packit Service 9646c7
Packit Service 9646c7
  $fail
Packit Service 9646c7
    and exit 1;
Packit Service 9646c7
Packit Service 9646c7
  return $h;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
{
Packit Service 9646c7
  my $since_date;
Packit Service 9646c7
  my $format_string = '%s%n%b%n';
Packit Service 9646c7
  my $amend_file;
Packit Service 9646c7
  my $append_dot = 0;
Packit Service 9646c7
  my $cluster = 1;
Packit Service 9646c7
  GetOptions
Packit Service 9646c7
    (
Packit Service 9646c7
     help => sub { usage 0 },
Packit Service 9646c7
     version => sub { print "$ME version $VERSION\n"; exit },
Packit Service 9646c7
     'since=s' => \$since_date,
Packit Service 9646c7
     'format=s' => \$format_string,
Packit Service 9646c7
     'amend=s' => \$amend_file,
Packit Service 9646c7
     'append-dot' => \$append_dot,
Packit Service 9646c7
     'cluster!' => \$cluster,
Packit Service 9646c7
    ) or usage 1;
Packit Service 9646c7
Packit Service 9646c7
Packit Service 9646c7
  defined $since_date
Packit Service 9646c7
    and unshift @ARGV, "--since=$since_date";
Packit Service 9646c7
Packit Service 9646c7
  # This is a hash that maps an SHA1 to perl code (i.e., s/old/new/)
Packit Service 9646c7
  # that makes a correction in the log or attribution of that commit.
Packit Service 9646c7
  my $amend_code = defined $amend_file ? parse_amend_file $amend_file : {};
Packit Service 9646c7
Packit Service 9646c7
  my @cmd = (qw (git log --log-size),
Packit Service 9646c7
             '--pretty=format:%H:%ct  %an  <%ae>%n%n'.$format_string, @ARGV);
Packit Service 9646c7
  open PIPE, '-|', @cmd
Packit Service 9646c7
    or die ("$ME: failed to run '". quoted_cmd (@cmd) ."': $!\n"
Packit Service 9646c7
            . "(Is your Git too old?  Version 1.5.1 or later is required.)\n");
Packit Service 9646c7
Packit Service 9646c7
  my $prev_multi_paragraph;
Packit Service 9646c7
  my $prev_date_line = '';
Packit Service 9646c7
  my @prev_coauthors = ();
Packit Service 9646c7
  while (1)
Packit Service 9646c7
    {
Packit Service 9646c7
      defined (my $in = <PIPE>)
Packit Service 9646c7
        or last;
Packit Service 9646c7
      $in =~ /^log size (\d+)$/
Packit Service 9646c7
        or die "$ME:$.: Invalid line (expected log size):\n$in";
Packit Service 9646c7
      my $log_nbytes = $1;
Packit Service 9646c7
Packit Service 9646c7
      my $log;
Packit Service 9646c7
      my $n_read = read PIPE, $log, $log_nbytes;
Packit Service 9646c7
      $n_read == $log_nbytes
Packit Service 9646c7
        or die "$ME:$.: unexpected EOF\n";
Packit Service 9646c7
Packit Service 9646c7
      # Extract leading hash.
Packit Service 9646c7
      my ($sha, $rest) = split ':', $log, 2;
Packit Service 9646c7
      defined $sha
Packit Service 9646c7
        or die "$ME:$.: malformed log entry\n";
Packit Service 9646c7
      $sha =~ /^[0-9a-fA-F]{40}$/
Packit Service 9646c7
        or die "$ME:$.: invalid SHA1: $sha\n";
Packit Service 9646c7
Packit Service 9646c7
      # If this commit's log requires any transformation, do it now.
Packit Service 9646c7
      my $code = $amend_code->{$sha};
Packit Service 9646c7
      if (defined $code)
Packit Service 9646c7
        {
Packit Service 9646c7
          eval 'use Safe';
Packit Service 9646c7
          my $s = new Safe;
Packit Service 9646c7
          # Put the unpreprocessed entry into "$_".
Packit Service 9646c7
          $_ = $rest;
Packit Service 9646c7
Packit Service 9646c7
          # Let $code operate on it, safely.
Packit Service 9646c7
          my $r = $s->reval("$code")
Packit Service 9646c7
            or die "$ME:$.:$sha: failed to eval \"$code\":\n$@\n";
Packit Service 9646c7
Packit Service 9646c7
          # Note that we've used this entry.
Packit Service 9646c7
          delete $amend_code->{$sha};
Packit Service 9646c7
Packit Service 9646c7
          # Update $rest upon success.
Packit Service 9646c7
          $rest = $_;
Packit Service 9646c7
        }
Packit Service 9646c7
Packit Service 9646c7
      my @line = split "\n", $rest;
Packit Service 9646c7
      my $author_line = shift @line;
Packit Service 9646c7
      defined $author_line
Packit Service 9646c7
        or die "$ME:$.: unexpected EOF\n";
Packit Service 9646c7
      $author_line =~ /^(\d+)  (.*>)$/
Packit Service 9646c7
        or die "$ME:$.: Invalid line "
Packit Service 9646c7
          . "(expected date/author/email):\n$author_line\n";
Packit Service 9646c7
Packit Service 9646c7
      # Format 'Copyright-paperwork-exempt: Yes' as a standard ChangeLog
Packit Service 9646c7
      # `(tiny change)' annotation.
Packit Service 9646c7
      my $tiny = (grep (/^Copyright-paperwork-exempt:\s+[Yy]es$/, @line)
Packit Service 9646c7
                  ? '  (tiny change)' : '');
Packit Service 9646c7
Packit Service 9646c7
      my $date_line = sprintf "%s  %s$tiny\n",
Packit Service 9646c7
        strftime ("%F", localtime ($1)), $2;
Packit Service 9646c7
Packit Service 9646c7
      my @coauthors = grep /^Co-authored-by:.*$/, @line;
Packit Service 9646c7
      # Omit meta-data lines we've already interpreted.
Packit Service 9646c7
      @line = grep !/^(?:Signed-off-by:[ ].*>$
Packit Service 9646c7
                       |Co-authored-by:[ ]
Packit Service 9646c7
                       |Copyright-paperwork-exempt:[ ]
Packit Service 9646c7
                       )/x, @line;
Packit Service 9646c7
Packit Service 9646c7
      # Remove leading and trailing blank lines.
Packit Service 9646c7
      if (@line)
Packit Service 9646c7
        {
Packit Service 9646c7
          while ($line[0] =~ /^\s*$/) { shift @line; }
Packit Service 9646c7
          while ($line[$#line] =~ /^\s*$/) { pop @line; }
Packit Service 9646c7
        }
Packit Service 9646c7
Packit Service 9646c7
      # Record whether there are two or more paragraphs.
Packit Service 9646c7
      my $multi_paragraph = grep /^\s*$/, @line;
Packit Service 9646c7
Packit Service 9646c7
      # Format 'Co-authored-by: A U Thor <email@example.com>' lines in
Packit Service 9646c7
      # standard multi-author ChangeLog format.
Packit Service 9646c7
      for (@coauthors)
Packit Service 9646c7
        {
Packit Service 9646c7
          s/^Co-authored-by:\s*/\t    /;
Packit Service 9646c7
          s/\s*</  </;
Packit Service 9646c7
Packit Service 9646c7
          /<.*?@.*\..*>/
Packit Service 9646c7
            or warn "$ME: warning: missing email address for "
Packit Service 9646c7
              . substr ($_, 5) . "\n";
Packit Service 9646c7
        }
Packit Service 9646c7
Packit Service 9646c7
      # If clustering of commit messages has been disabled, if this header
Packit Service 9646c7
      # would be different from the previous date/name/email/coauthors header,
Packit Service 9646c7
      # or if this or the previous entry consists of two or more paragraphs,
Packit Service 9646c7
      # then print the header.
Packit Service 9646c7
      if ( ! $cluster
Packit Service 9646c7
          || $date_line ne $prev_date_line
Packit Service 9646c7
          || "@coauthors" ne "@prev_coauthors"
Packit Service 9646c7
          || $multi_paragraph
Packit Service 9646c7
          || $prev_multi_paragraph)
Packit Service 9646c7
        {
Packit Service 9646c7
          $prev_date_line eq ''
Packit Service 9646c7
            or print "\n";
Packit Service 9646c7
          print $date_line;
Packit Service 9646c7
          @coauthors
Packit Service 9646c7
            and print join ("\n", @coauthors), "\n";
Packit Service 9646c7
        }
Packit Service 9646c7
      $prev_date_line = $date_line;
Packit Service 9646c7
      @prev_coauthors = @coauthors;
Packit Service 9646c7
      $prev_multi_paragraph = $multi_paragraph;
Packit Service 9646c7
Packit Service 9646c7
      # If there were any lines
Packit Service 9646c7
      if (@line == 0)
Packit Service 9646c7
        {
Packit Service 9646c7
          warn "$ME: warning: empty commit message:\n  $date_line\n";
Packit Service 9646c7
        }
Packit Service 9646c7
      else
Packit Service 9646c7
        {
Packit Service 9646c7
          if ($append_dot)
Packit Service 9646c7
            {
Packit Service 9646c7
              # If the first line of the message has enough room, then
Packit Service 9646c7
              if (length $line[0] < 72)
Packit Service 9646c7
                {
Packit Service 9646c7
                  # append a dot if there is no other punctuation or blank
Packit Service 9646c7
                  # at the end.
Packit Service 9646c7
                  $line[0] =~ /[[:punct:]\s]$/
Packit Service 9646c7
                    or $line[0] .= '.';
Packit Service 9646c7
                }
Packit Service 9646c7
            }
Packit Service 9646c7
Packit Service 9646c7
          # Prefix each non-empty line with a TAB.
Packit Service 9646c7
          @line = map { length $_ ? "\t$_" : '' } @line;
Packit Service 9646c7
Packit Service 9646c7
          print "\n", join ("\n", @line), "\n";
Packit Service 9646c7
        }
Packit Service 9646c7
Packit Service 9646c7
      defined ($in = <PIPE>)
Packit Service 9646c7
        or last;
Packit Service 9646c7
      $in ne "\n"
Packit Service 9646c7
        and die "$ME:$.: unexpected line:\n$in";
Packit Service 9646c7
    }
Packit Service 9646c7
Packit Service 9646c7
  close PIPE
Packit Service 9646c7
    or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n";
Packit Service 9646c7
  # FIXME-someday: include $PROCESS_STATUS in the diagnostic
Packit Service 9646c7
Packit Service 9646c7
  # Complain about any unused entry in the --amend=F specified file.
Packit Service 9646c7
  my $fail = 0;
Packit Service 9646c7
  foreach my $sha (keys %$amend_code)
Packit Service 9646c7
    {
Packit Service 9646c7
      warn "$ME:$amend_file: unused entry: $sha\n";
Packit Service 9646c7
      $fail = 1;
Packit Service 9646c7
    }
Packit Service 9646c7
Packit Service 9646c7
  exit $fail;
Packit Service 9646c7
}
Packit Service 9646c7
Packit Service 9646c7
# Local Variables:
Packit Service 9646c7
# mode: perl
Packit Service 9646c7
# indent-tabs-mode: nil
Packit Service 9646c7
# eval: (add-hook 'write-file-hooks 'time-stamp)
Packit Service 9646c7
# time-stamp-start: "my $VERSION = '"
Packit Service 9646c7
# time-stamp-format: "%:y-%02m-%02d %02H:%02M"
Packit Service 9646c7
# time-stamp-time-zone: "UTC"
Packit Service 9646c7
# time-stamp-end: "'; # UTC"
Packit Service 9646c7
# End: