Michal Marek 1038f4
#!/usr/bin/perl
Michal Marek 38d7c7
Michal Marek 1038f4
use strict;
Michal Marek 1038f4
use warnings;
Michal Marek 38d7c7
Michal Marek 1038f4
use POSIX qw(strftime setlocale LC_ALL);
Michal Marek 1038f4
Michal Marek 1038f4
$ENV{'TZ'} = "CET";
Michal Marek 1038f4
setlocale(LC_ALL, "C");
Michal Marek 1038f4
Michal Marek cadbf0
{
Michal Marek 0e177b
my ($last_ts, $last_email, $last_commit, $last_message) = (0, "");
Michal Marek 1038f4
sub print_commit {
Michal Marek 1038f4
	my ($commit, $email, $ts, @message) = @_;
Michal Marek 1038f4
Michal Marek 94e5c6
	return unless $commit;
Michal Marek 1038f4
	# display series by the same author and with the same author date
Michal Marek 1038f4
	# as a single changelog entry (see scripts/log2)
Michal Marek 1038f4
	if ($last_ts != $ts || $last_email ne $email) {
Michal Marek 1038f4
		if ($last_commit) {
Michal Marek 1038f4
			print "- commit " . substr($last_commit, 0, 7) . "\n\n";
Michal Marek 1038f4
		}
Michal Marek 1038f4
		return unless @message;
Michal Marek 1038f4
		print "-" x 67 . "\n";
Michal Marek 1038f4
		print strftime("%a %b %e %H:%M:%S %Z %Y - $email\n\n",
Michal Marek 1038f4
			localtime($ts));
Michal Marek 1038f4
		$last_commit = $commit;
Michal Marek 0e177b
		$last_message = "";
Michal Marek 1038f4
	}
Michal Marek 1038f4
	$last_ts = $ts;
Michal Marek 1038f4
	$last_email = $email;
Michal Marek 1038f4
	my $first = 1;
Michal Marek 1038f4
	for my $line (@message) {
Michal Marek 1038f4
		if ($line !~ /^[- ] /) {
Michal Marek 1038f4
			if ($first) {
Michal Marek 1038f4
				$line = "- $line";
Michal Marek 1038f4
			} else {
Michal Marek 1038f4
				$line = "  $line";
Michal Marek 1038f4
			}
Michal Marek 1038f4
		}
Michal Marek 1038f4
		$first = 0;
Michal Marek 1038f4
	}
Michal Marek 0e177b
	my $msg = join("\n", @message);
Michal Marek 0e177b
	if ($msg eq $last_message) {
Michal Marek 0e177b
		# avoid printing cherry-picked commits twice
Michal Marek 0e177b
		# FIXME: Handle the case where a whole patch series is
Michal Marek 0e177b
		# cherry-picked one by one. At the same time, we do not want
Michal Marek 0e177b
		# to filter commits, that have the same changelog within a
Michal Marek 0e177b
		# series, but are different. See for example
Michal Marek 0e177b
		# git grep 'e1000e: update driver version number' SLE11-SP3
Michal Marek 0e177b
		# and many others
Michal Marek 0e177b
		return;
Michal Marek 0e177b
	}
Michal Marek 0e177b
	$last_message = $msg;
Michal Marek 0e177b
	print $msg, "\n";
Michal Marek 1038f4
}
Michal Marek cadbf0
}
Michal Marek cadbf0
Michal Marek 41ed25
sub parse_gitlog {
Michal Marek 41ed25
	my $fh = shift;
Michal Marek 41ed25
Michal Marek 41ed25
	my @res;
Michal Marek 41ed25
	my $cur = { message => [] };
Michal Marek 41ed25
	my @states = qw(commit tree parent author committer blank message);
Michal Marek 41ed25
	my $st = 0;
Takashi Iwai a384f3
	my $gpgsig = 0;
Michal Marek 41ed25
	while (my $line = <$fh>) {
Michal Marek 94e5c6
		next if $line =~ /^#/;
Michal Marek 41ed25
		chomp($line);
Michal Marek 41ed25
		my $expect = $states[$st];
Michal Marek 41ed25
		if ($expect eq "blank") {
Takashi Iwai a384f3
			if ($gpgsig > 0) {
Takashi Iwai a384f3
				if ($line =~ /-----END PGP SIGNATURE-----/) {
Takashi Iwai a384f3
					$gpgsig = 0;
Takashi Iwai a384f3
				}
Takashi Iwai a384f3
				next;
Takashi Iwai a384f3
			}
Takashi Iwai a384f3
			if ($line =~ /^gpgsig/) {
Takashi Iwai a384f3
				$gpgsig = 1;
Takashi Iwai a384f3
				next;
Takashi Iwai a384f3
			}
Michal Marek 41ed25
			if ($line ne "") {
Michal Marek 41ed25
				die "Malformed git rev-parse output ($cur->{commit}): expected blank line, got \"$line\"\n";
Michal Marek 41ed25
			}
Michal Marek 41ed25
			$st++;
Michal Marek 41ed25
			next;
Michal Marek 1038f4
		}
Michal Marek 41ed25
		if ($expect eq "message") {
Michal Marek 41ed25
			if ($line eq "") {
Michal Marek 41ed25
				push(@res, $cur);
Michal Marek 41ed25
				$cur = { message => [] };
Michal Marek 41ed25
				$st = 0;
Michal Marek 41ed25
				next;
Michal Marek 41ed25
			}
Michal Marek 41ed25
			if ($line !~ s/^ {4}//) {
Michal Marek 41ed25
				die "Malformed git rev-parse output ($cur->{commit}): expected log message, got \"$line\"\n";
Michal Marek 41ed25
			}
Michal Marek 41ed25
			next unless $line;
Michal Marek 41ed25
			# delete Signed-off-by: et al
Michal Marek 41ed25
			next if $line =~ /^[A-Z][-a-zA-Z]+-by: /;
Michal Marek 41ed25
			push(@{$cur->{message}}, $line) if $line;
Michal Marek 1038f4
			next;
Michal Marek 1038f4
		}
Michal Marek 41ed25
		# parsing commit headers
Michal Marek 94e5c6
		next if $expect eq "commit" && $line eq "";
Michal Marek 41ed25
		(my $got = $line) =~ s/ .*//;
Michal Marek 41ed25
		# Root commit has no "parent" header. Multiple "parent" headers are
Michal Marek 41ed25
		# not possible, since we use --no-merges
Michal Marek 41ed25
		if ($expect eq "parent" && $got eq "author") {
Michal Marek 41ed25
			$expect = $states[++$st];
Michal Marek 1038f4
		}
Michal Marek 41ed25
		if ($got ne $expect) {
Michal Marek 41ed25
			$cur->{commit} ||= "commit unknown";
Michal Marek 41ed25
			die "Malformed git rev-parse output ($cur->{commit}): expected \"$expect\", got \"$got\"\n";
Michal Marek 41ed25
		}
Michal Marek 41ed25
		if ($got eq "commit") {
Michal Marek 41ed25
			($cur->{commit} = $line) =~ s/^commit //;
Michal Marek 41ed25
		} elsif ($got eq "author") {
Michal Marek 41ed25
			($cur->{email} = $line) =~ s/.*<(.+)>.*/$1/;
Michal Marek 41ed25
			($cur->{ts} = $line) =~ s/.*> (\d+) [-+]\d{4}$/$1/;
Michal Marek 41ed25
			if (!$cur->{email} || !$cur->{ts}) {
Michal Marek 41ed25
				die "Malformed author header ($cur->{commit}): $line\n";
Michal Marek 41ed25
			}
Michal Marek 1038f4
		}
Michal Marek 41ed25
		$st++;
Michal Marek 1038f4
	}
Michal Marek 41ed25
	return @res;
Michal Marek 1038f4
}
Michal Marek 41ed25
Takashi Iwai 2d2481
my $excludes_file;
Takashi Iwai 2d2481
if ($ARGV[0] eq "--excludes") {
Takashi Iwai 2d2481
	shift(@ARGV);
Takashi Iwai 2d2481
	$excludes_file = shift(@ARGV);
Takashi Iwai 2d2481
}
Takashi Iwai 2d2481
Michal Marek 94e5c6
my @fixups;
Michal Marek 94e5c6
if ($ARGV[0] eq "--fixups") {
Michal Marek 94e5c6
	shift(@ARGV);
Michal Marek 94e5c6
	my $fixups_file = shift(@ARGV);
Michal Marek 94e5c6
	open(my $fh, '<', $fixups_file) or die "$fixups_file: $!\n";
Michal Marek 94e5c6
	@fixups = parse_gitlog($fh);
Michal Marek 94e5c6
	close($fh);
Michal Marek 94e5c6
}
Michal Marek 94e5c6
Michal Marek 41ed25
open(my $pipe, '-|', "git", "rev-list", "--no-merges", "--pretty=raw", @ARGV)
Michal Marek 41ed25
	or die "Error running git rev-list: $!\n";
Michal Marek 41ed25
my @commits = parse_gitlog($pipe);
Michal Marek 1038f4
close($pipe) or die "Error running git rev-list: $!\n";
Michal Marek 1038f4
Michal Marek 94e5c6
# apply any fixups
Michal Marek 94e5c6
my %commits_h = map { $_->{commit} => $_ } @commits;
Michal Marek 94e5c6
for my $fix (@fixups) {
Michal Marek 94e5c6
	my $orig = $commits_h{$fix->{commit}};
Michal Marek 94e5c6
	if (!$fix->{message}) {
Michal Marek 94e5c6
		# delete the original commit
Michal Marek 94e5c6
		$orig->{commit} = undef;
Michal Marek 94e5c6
	} else {
Michal Marek 94e5c6
		$orig->{email} = $fix->{email};
Michal Marek 94e5c6
		$orig->{ts} = $fix->{ts};
Michal Marek 94e5c6
		$orig->{message} = $fix->{message};
Michal Marek 94e5c6
	}
Michal Marek 94e5c6
}
Michal Marek 94e5c6
Takashi Iwai 2d2481
if ($excludes_file) {
Takashi Iwai 2d2481
	open(my $fh, '<', $excludes_file) or die "$excludes_file: $!\n";
Takashi Iwai 2d2481
	while (my $id = <$fh>) {
Takashi Iwai 2d2481
		next if $id =~ /^#/;
Takashi Iwai 2d2481
		chomp($id);
Takashi Iwai 2d2481
		# delete the original commit
Takashi Iwai 2d2481
		$commits_h{$id}->{commit} = undef;
Takashi Iwai 2d2481
	}
Takashi Iwai 2d2481
	close($fh);
Takashi Iwai 2d2481
}
Takashi Iwai 2d2481
Michal Marek 1038f4
for my $c (sort { $b->{ts} - $a->{ts} } @commits) {
Michal Marek 1038f4
	print_commit($c->{commit}, $c->{email}, $c->{ts}, @{$c->{message}});
Michal Marek 1038f4
}
Michal Marek 1038f4
# print "- commit 1234567" for the last commit
Michal Marek 1038f4
print_commit($commits[$#commits]->{commit}, "", 0);