The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl

use warnings;
use strict;

use Text::Wrap qw(wrap fill $columns $huge);
use Getopt::Long;
use POSIX qw(strftime);

my $days_back  = 110365;   # Go back a year by default.
my $send_help  = 0;     # Display help and exit.
my $tag_pattern = "^\\d";

GetOptions(
	"age=s"      => \$days_back,
	"help"       => \$send_help,
	"tags=s"     => \$tag_pattern,
) or $send_help = 1;

# Find the trunk for the current repository if one isn't specified.

die(
	"$0 usage:\n",
	"  [--age DAYS]  limit report to DAYS in the past (default: 365)\n",
	"  [--tags REGEXP]  report on tags matching REGEXP (default: ^v\\d+_)\n",
) if $send_help;

my $earliest_date = strftime(
	"%FT %T +0000", gmtime(time() - $days_back * 86400)
);

$Text::Wrap::huge     = "wrap";
$Text::Wrap::columns  = 74;

chomp(my @tags = `git tag`);

{
	my $i = @tags;
	while ($i--) {
		unless ($tags[$i] =~ /$tag_pattern/o) {
			splice @tags, $i, 1;
			next;
		}

		my $commit = `git show $tags[$i] --pretty='tformat:(((((%ci)))))' | grep '(((((' | head -1`;
		die $commit unless $commit =~ /\(\(\(\(\((.+?)\)\)\)\)\)/;

		$tags[$i] = {
			'time' => $1,
			'tag'  => $tags[$i],
		};
	}
}

push @tags, { 'time' => '9999-99-99 99:99:99 +0000', 'tag' => 'HEAD' };

@tags = sort { $a->{'time'} cmp $b->{'time'} } @tags;

{
	my $i = @tags;
	while ($i--) {
		last if $tags[$i]{time} lt $earliest_date;

		my @commit;

		open my $commit, "-|", "git log $tags[$i-1]{tag}..$tags[$i]{tag} ." or die $!;
		local $/ = "\n\n";
		while (<$commit>) {
			if (/^\S/) {
				s/^/  /mg;
				push @commit, $_;
				next;
			}

			# Trim off identical leading whitespace.
			my ($whitespace) = /^(\s*)/;
			if (length $whitespace) {
				s/^$whitespace//mg;
			}

			# Re-flow the paragraph if it isn't indented from the norm.
			# This should preserve indented quoted text, wiki-style.
			unless (/^\s/) {
				push @commit, fill("    ", "    ", $_), "\n\n";
			}
			else {
				push @commit, $_;
			}
		}

		# Don't display the tag if there's nothing under it.
		next unless @commit;

		my $tag_line = "$tags[$i]{time} $tags[$i]{tag}";
		print(
			("=" x length($tag_line)), "\n",
			$tag_line, "\n",
			("=" x length($tag_line)), "\n",
			"\n",
		);

		print @commit;
	}
}

print(
	"==============\n",
	"End of Excerpt\n",
	"==============\n",
);