#!/usr/bin/env perl1#2# SPDX-License-Identifier: ISC3#4# Copyright (c) 2017, 2020 Todd C. Miller <[email protected]>5#6# Permission to use, copy, modify, and distribute this software for any7# purpose with or without fee is hereby granted, provided that the above8# copyright notice and this permission notice appear in all copies.9#10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17#18# Simple script to massage "git log" output into a GNU style ChangeLog.19# The goal is to emulate "hg log --template=changelog" via perl format.2021use Getopt::Std;22use Text::Wrap;23use strict;24use warnings;2526# Git log format: author date, author name, author email27# abbreviated commit hash28# raw commit body29my $format="%ad %aN <%aE>%n%h%n%B%n";3031# Parse options and build up "git log" command32my @cmd = ( "git" );33my %opts;34getopts('mR:', \%opts);35push(@cmd, "--git-dir", $opts{"R"}) if exists $opts{"R"};36push(@cmd, "log", "--log-size", "--name-only", "--date=short", "--format=$format", @ARGV);3738open(LOG, '-|', @cmd) || die "$0: unable to run git log: $!";3940my $hash;41my $body;42my @files;43my $key_date = "";44my $log_size = 0;45my @lines;46my $hash_link = "https://git.sudo.ws/sudo/commit/?id=";4748# Wrap like "hg log --template=changelog"49$Text::Wrap::columns = 77;50# Don't preserve tabs51$Text::Wrap::unexpand = 0;5253while (<LOG>) {54chomp;55if (/^log size (\d+)$/) {56$log_size = $1;5758# Print previous entry if there is one59print_entry($hash, $body, @files) if defined($hash);6061# Init new entry62undef $hash;63undef $body;64undef @files;65undef @lines;6667# Read entry and split on newlines68read(LOG, my $buf, $log_size) ||69die "$0: unable to read $log_size bytes: $!\n";70@lines = split(/\r?\n/, $buf);7172# Check for continued entry (duplicate Date + Author)73$_ = shift(@lines);74# Strip author email address for markdown75s/\s*<[^>]+>$// if exists $opts{'m'};7677if ($_ ne $key_date) {78# New entry79print "$_\n\n";80$key_date = $_;81}8283# Hash comes first84$hash = shift(@lines);8586# Commit message body (multi-line)87my $sep = "";88foreach (@lines) {89last if $_ eq "--HG--";90if ($_ eq "") {91$sep = "\n\n";92next;93}94s/^\s+//;95s/\s+$//;96$body .= ${sep} . $_;97$sep = " ";98}99} else {100# Not a log entry, must be the file list101push(@files, $_) unless $_ eq "";102}103}104105# Print the last entry106print_entry($hash, $body, @files) if defined($hash);107108exit(0);109110sub print_entry111{112if (exists $opts{'m'}) {113print_entry_markdown(@_);114} else {115print_entry_plain(@_);116}117}118119sub print_entry_plain120{121my $hash = shift;122my $body = shift;123my $files = "* " . join(", ", @_) . ":";124125print wrap("\t", "\t", $files) . "\n";126print fill("\t", "\t", $body) . "\n";127print "\t[$hash]\n\n";128}129130sub print_entry_markdown131{132my $hash = shift;133my $body = shift;134my $files = ": * " . join(", ", @_) . ": ";135136# Obfuscate email addresses in body137$body =~ s/([^@ ]+@)[\w\.-]+\.(com|org|edu|ws|io)/$1.../g;138139# Escape email chars in body140$body =~ s/([@<>])/\\$1/g;141142# Expand GitHub issue and bugzilla links143$body =~ s@(GitHub issue #)(\d+)@[$1$2](https://github.com/sudo-project/sudo/issues/$2)@;144$body =~ s@(Bug #)(\d+)@[$1$2](https://bugzilla.sudo.ws/show_bug.cgi?id=$2)@;145146print wrap("", " ", $files) . "\n";147print fill(" ", " ", $body) . "\n";148print " [[${hash}]](${hash_link}${hash})\n\n";149}150151152