|
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);
|