2010-09-21 06:18:20 +02:00
|
|
|
#!/usr/bin/perl
|
|
|
|
|
2010-09-26 01:31:26 +02:00
|
|
|
#
|
|
|
|
# src/tools/git_changelog
|
2010-09-21 06:18:20 +02:00
|
|
|
#
|
|
|
|
# Display all commits on active branches, merging together commits from
|
|
|
|
# different branches that occur close together in time and with identical
|
2010-09-26 06:21:51 +02:00
|
|
|
# log messages.
|
|
|
|
#
|
|
|
|
# Most of the time, matchable commits occur in the same order on all branches,
|
|
|
|
# and we print them out in that order. However, if commit A occurs before
|
|
|
|
# commit B on branch X and commit B occurs before commit A on branch Y, then
|
|
|
|
# there's no ordering which is consistent with both branches.
|
2010-09-21 06:18:20 +02:00
|
|
|
#
|
|
|
|
# When we encounter a situation where there's no single "best" commit to
|
|
|
|
# print next, we print the one that involves the least distortion of the
|
2010-09-26 06:21:51 +02:00
|
|
|
# commit order, summed across all branches. In the event of a tie on the
|
|
|
|
# distortion measure (which is actually the common case: normally, the
|
|
|
|
# distortion is zero), we choose the commit with latest timestamp. If
|
|
|
|
# that's a tie too, the commit from the newer branch prints first.
|
2010-09-21 06:18:20 +02:00
|
|
|
#
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
2010-09-26 02:50:57 +02:00
|
|
|
require Time::Local;
|
2010-09-21 06:18:20 +02:00
|
|
|
require Getopt::Long;
|
|
|
|
require IPC::Open2;
|
|
|
|
|
2010-09-26 01:31:26 +02:00
|
|
|
# Adjust this list when the set of active branches changes.
|
2010-09-21 06:18:20 +02:00
|
|
|
my @BRANCHES = qw(master REL9_0_STABLE REL8_4_STABLE REL8_3_STABLE
|
|
|
|
REL8_2_STABLE REL8_1_STABLE REL8_0_STABLE REL7_4_STABLE);
|
|
|
|
|
2010-09-26 01:31:26 +02:00
|
|
|
# Might want to make this parameter user-settable.
|
|
|
|
my $timestamp_slop = 600;
|
|
|
|
|
2010-09-21 06:18:20 +02:00
|
|
|
my $since;
|
|
|
|
Getopt::Long::GetOptions('since=s' => \$since) || usage();
|
|
|
|
usage() if @ARGV;
|
|
|
|
|
|
|
|
my @git = qw(git log --date=iso);
|
|
|
|
push @git, '--since=' . $since if defined $since;
|
|
|
|
|
|
|
|
my %all_commits;
|
|
|
|
my %all_commits_by_branch;
|
|
|
|
|
|
|
|
for my $branch (@BRANCHES) {
|
2010-09-26 02:50:57 +02:00
|
|
|
my $pid =
|
|
|
|
IPC::Open2::open2(my $git_out, my $git_in, @git, "origin/$branch")
|
|
|
|
|| die "can't run @git origin/$branch: $!";
|
2010-09-26 06:21:51 +02:00
|
|
|
my $commitnum = 0;
|
|
|
|
my %commit;
|
2010-09-21 06:18:20 +02:00
|
|
|
while (my $line = <$git_out>) {
|
|
|
|
if ($line =~ /^commit\s+(.*)/) {
|
|
|
|
push_commit(\%commit) if %commit;
|
|
|
|
%commit = (
|
|
|
|
'branch' => $branch,
|
|
|
|
'commit' => $1,
|
|
|
|
'message' => '',
|
|
|
|
'commitnum' => $commitnum++,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
elsif ($line =~ /^Author:\s+(.*)/) {
|
|
|
|
$commit{'author'} = $1;
|
|
|
|
}
|
|
|
|
elsif ($line =~ /^Date:\s+(.*)/) {
|
|
|
|
$commit{'date'} = $1;
|
|
|
|
}
|
2010-09-26 02:50:57 +02:00
|
|
|
elsif ($line =~ /^\s\s/) {
|
2010-09-21 06:18:20 +02:00
|
|
|
$commit{'message'} .= $line;
|
|
|
|
}
|
|
|
|
}
|
2010-09-26 06:21:51 +02:00
|
|
|
push_commit(\%commit) if %commit;
|
2010-09-26 02:50:57 +02:00
|
|
|
waitpid($pid, 0);
|
|
|
|
my $child_exit_status = $? >> 8;
|
|
|
|
die "@git origin/$branch failed" if $child_exit_status != 0;
|
2010-09-21 06:18:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
my %position;
|
|
|
|
for my $branch (@BRANCHES) {
|
|
|
|
$position{$branch} = 0;
|
|
|
|
}
|
2010-09-26 02:50:57 +02:00
|
|
|
|
2010-09-21 06:18:20 +02:00
|
|
|
while (1) {
|
|
|
|
my $best_branch;
|
|
|
|
my $best_inversions;
|
2010-09-26 06:21:51 +02:00
|
|
|
my $best_timestamp;
|
2010-09-21 06:18:20 +02:00
|
|
|
for my $branch (@BRANCHES) {
|
|
|
|
my $leader = $all_commits_by_branch{$branch}->[$position{$branch}];
|
|
|
|
next if !defined $leader;
|
|
|
|
my $inversions = 0;
|
|
|
|
for my $branch2 (@BRANCHES) {
|
|
|
|
if (defined $leader->{'branch_position'}{$branch2}) {
|
|
|
|
$inversions += $leader->{'branch_position'}{$branch2}
|
|
|
|
- $position{$branch2};
|
|
|
|
}
|
|
|
|
}
|
2010-09-26 06:21:51 +02:00
|
|
|
if (!defined $best_inversions ||
|
|
|
|
$inversions < $best_inversions ||
|
|
|
|
($inversions == $best_inversions &&
|
|
|
|
$leader->{'timestamp'} > $best_timestamp)) {
|
2010-09-21 06:18:20 +02:00
|
|
|
$best_branch = $branch;
|
|
|
|
$best_inversions = $inversions;
|
2010-09-26 06:21:51 +02:00
|
|
|
$best_timestamp = $leader->{'timestamp'};
|
2010-09-21 06:18:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
last if !defined $best_branch;
|
|
|
|
my $winner =
|
|
|
|
$all_commits_by_branch{$best_branch}->[$position{$best_branch}];
|
|
|
|
print $winner->{'header'};
|
|
|
|
print "Commit-Order-Inversions: $best_inversions\n"
|
|
|
|
if $best_inversions != 0;
|
2010-09-26 02:50:57 +02:00
|
|
|
print "\n";
|
2010-09-21 06:18:20 +02:00
|
|
|
print $winner->{'message'};
|
2010-09-26 02:50:57 +02:00
|
|
|
print "\n";
|
2010-09-21 06:18:20 +02:00
|
|
|
$winner->{'done'} = 1;
|
|
|
|
for my $branch (@BRANCHES) {
|
|
|
|
my $leader = $all_commits_by_branch{$branch}->[$position{$branch}];
|
|
|
|
if (defined $leader && $leader->{'done'}) {
|
|
|
|
++$position{$branch};
|
|
|
|
redo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub push_commit {
|
|
|
|
my ($c) = @_;
|
|
|
|
my $ht = hash_commit($c);
|
|
|
|
my $ts = parse_datetime($c->{'date'});
|
|
|
|
my $cc;
|
|
|
|
for my $candidate (@{$all_commits{$ht}}) {
|
2010-09-26 01:31:26 +02:00
|
|
|
if (abs($ts - $candidate->{'timestamp'}) < $timestamp_slop
|
2010-09-21 06:18:20 +02:00
|
|
|
&& !exists $candidate->{'branch_position'}{$c->{'branch'}})
|
|
|
|
{
|
|
|
|
$cc = $candidate;
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!defined $cc) {
|
|
|
|
$cc = {
|
|
|
|
'header' => sprintf("Author: %s\n", $c->{'author'}),
|
|
|
|
'message' => $c->{'message'},
|
|
|
|
'timestamp' => $ts
|
|
|
|
};
|
|
|
|
push @{$all_commits{$ht}}, $cc;
|
|
|
|
}
|
|
|
|
$cc->{'header'} .= sprintf "Branch: %s [%s] %s\n",
|
|
|
|
$c->{'branch'}, substr($c->{'commit'}, 0, 9), $c->{'date'};
|
|
|
|
push @{$all_commits_by_branch{$c->{'branch'}}}, $cc;
|
|
|
|
$cc->{'branch_position'}{$c->{'branch'}} =
|
|
|
|
-1+@{$all_commits_by_branch{$c->{'branch'}}};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub hash_commit {
|
|
|
|
my ($c) = @_;
|
|
|
|
return $c->{'author'} . "\0" . $c->{'message'};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub parse_datetime {
|
|
|
|
my ($dt) = @_;
|
2010-09-26 02:50:57 +02:00
|
|
|
$dt =~ /^(\d\d\d\d)-(\d\d)-(\d\d)\s+(\d\d):(\d\d):(\d\d)\s+([-+])(\d\d)(\d\d)$/;
|
|
|
|
my $gm = Time::Local::timegm($6, $5, $4, $3, $2-1, $1);
|
|
|
|
my $tzoffset = ($8 * 60 + $9) * 60;
|
|
|
|
$tzoffset = - $tzoffset if $7 eq '-';
|
|
|
|
return $gm - $tzoffset;
|
2010-09-21 06:18:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sub usage {
|
|
|
|
print STDERR <<EOM;
|
2010-09-26 01:31:26 +02:00
|
|
|
Usage: git_changelog [--since=SINCE]
|
2010-09-21 06:18:20 +02:00
|
|
|
EOM
|
|
|
|
exit 1;
|
|
|
|
}
|