#!/usr/bin/perl -w
#
# Copyright (c) 2020 SUSE LLC, Adrian Schroeter
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program (see the file COPYING); if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
################################################################
#
# Extract new changelog text from staged *.changes files for a
# git commit message.
#
# Usage: prepare-commit-message [--help] [<msgfile>]
#
# When called with a message file argument (as a git prepare-commit-msg
# hook), the extracted text is prepended to that file.
# When called without arguments, the text is printed to stdout.
#

use strict;

sub echo_help {
    print "\n
Prepare commit message from .changes files
==========================================

Extracts new changelog text from staged *.changes files for a git
commit message.

The tool reads the git staging area, finds all *.changes files that
have been modified, and extracts only the newly added changelog entries.
Timestamp lines, separator lines, and diff metadata are filtered out.

If nothing new is found, output is empty and the exit code is 0 (the
commit is not aborted).

Usage: prepare-commit-message [--help]
       prepare-commit-message <msgfile>

When called with a message file argument (as a git prepare-commit-msg
hook), the extracted text is prepended to that file. When called without
arguments, the text is printed to stdout.

";
}

my $msgfile;
for my $arg (@ARGV) {
  if ($arg eq "--help" || $arg eq "-h") {
    echo_help();
    exit(0);
  }
  if ($arg =~ /^-/) {
    die("Unknown switch $arg\n");
  }
  # First non-switch argument is the message file (from git hook $1)
  $msgfile = $arg unless defined $msgfile;
}

my $sep = "-" x 67;

my @files = `git diff --cached --name-only -- '*.changes' 2>/dev/null`;
chomp @files;
@files = grep { -f $_ } @files;

exit 0 unless @files;

my $msg = "";
my $after_sep = 0;

for my $file (@files) {
  my @diff = `git diff --cached -- "$file" 2>/dev/null`;
  next unless @diff;

  for my $line (@diff) {
    chomp $line;

    # Only process added lines (starting with +)
    next unless $line =~ /^\+(.*)/;
    my $content = $1;

    # Skip diff metadata
    next if $content =~ /^(diff --git |index |--- |\+\+ )/;

    # After a separator, the next weekday-prefixed line is a timestamp
    if ($after_sep) {
      if ($content =~ /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) /) {
        $after_sep = 0;
        next;
      }
      $after_sep = 0;
    }

    # Track separators
    if ($content eq $sep) {
      $after_sep = 1;
      next;
    }

    # Skip empty lines
    next if $content =~ /^\s*$/;

    $msg .= "$content\n";
  }
}

$msg =~ s/\s+\z//;

exit 0 unless length $msg;

# Deduplicate consecutive identical lines
my @lines = split /\n/, $msg;
my @deduped;
my $prev = "";
for my $line (@lines) {
  if ($line ne $prev) {
    push @deduped, $line;
    $prev = $line;
  }
}

my $output = join("\n", @deduped) . "\n";

if (defined $msgfile) {
  # Prepend to existing commit message file
  open(my $fh, '<', $msgfile) or die("Cannot read $msgfile: $!\n");
  my $existing = do { local $/; <$fh> };
  close($fh);

  open(my $out, '>', $msgfile) or die("Cannot write $msgfile: $!\n");
  print $out $output;
  print $out $existing;
  close($out);
} else {
  print $output;
}
