Skip to content

Commit 1b984d4

Browse files
committed
git_topo_order script, to match up commits across branches.
This script is intended to substitute for cvs2cl in generating release notes and scrutinizing what got back-patched to which branches. Script by me. Support for --since by Alex Hunsaker.
1 parent 3977618 commit 1b984d4

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed

src/tools/git_topo_order

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/usr/bin/perl
2+
3+
#
4+
# Display all commits on active branches, merging together commits from
5+
# different branches that occur close together in time and with identical
6+
# log messages. Most of the time, such commits occur in the same order
7+
# on all branches, and we print them out in that order. However, if commit
8+
# A occurs before commit B on branch X and commit B occurs before commit A
9+
# on branch Y, then there's no ordering which is consistent with both
10+
# branches.
11+
#
12+
# When we encounter a situation where there's no single "best" commit to
13+
# print next, we print the one that involves the least distortion of the
14+
# commit order, summed across all branches. In the event of a further tie,
15+
# the commit from the newer branch prints first. It is best not to sort
16+
# based on timestamp, because git timestamps aren't necessarily in order
17+
# (since the timestamp is provided by the committer's machine), even though
18+
# for the portion of the history we imported from CVS, we expect that they
19+
# will be.
20+
#
21+
# Even though we don't use timestamps to order commits, it is used to
22+
# identify which commits happened at about the same time, for the purpose
23+
# of matching up commits from different branches.
24+
#
25+
26+
use strict;
27+
use warnings;
28+
require Date::Calc;
29+
require Getopt::Long;
30+
require IPC::Open2;
31+
32+
my @BRANCHES = qw(master REL9_0_STABLE REL8_4_STABLE REL8_3_STABLE
33+
REL8_2_STABLE REL8_1_STABLE REL8_0_STABLE REL7_4_STABLE);
34+
35+
my $since;
36+
Getopt::Long::GetOptions('since=s' => \$since) || usage();
37+
usage() if @ARGV;
38+
39+
my @git = qw(git log --date=iso);
40+
push @git, '--since=' . $since if defined $since;
41+
42+
my %all_commits;
43+
my %all_commits_by_branch;
44+
45+
my %commit;
46+
for my $branch (@BRANCHES) {
47+
my $commitnum = 0;
48+
IPC::Open2::open2(my $git_out, my $git_in, @git, "origin/$branch")
49+
|| die "can't run @git origin/$branch: $!";
50+
while (my $line = <$git_out>) {
51+
if ($line =~ /^commit\s+(.*)/) {
52+
push_commit(\%commit) if %commit;
53+
%commit = (
54+
'branch' => $branch,
55+
'commit' => $1,
56+
'message' => '',
57+
'commitnum' => $commitnum++,
58+
);
59+
}
60+
elsif ($line =~ /^Author:\s+(.*)/) {
61+
$commit{'author'} = $1;
62+
}
63+
elsif ($line =~ /^Date:\s+(.*)/) {
64+
$commit{'date'} = $1;
65+
}
66+
elsif ($line =~ /^\s+/) {
67+
$commit{'message'} .= $line;
68+
}
69+
}
70+
}
71+
72+
my %position;
73+
for my $branch (@BRANCHES) {
74+
$position{$branch} = 0;
75+
}
76+
while (1) {
77+
my $best_branch;
78+
my $best_inversions;
79+
for my $branch (@BRANCHES) {
80+
my $leader = $all_commits_by_branch{$branch}->[$position{$branch}];
81+
next if !defined $leader;
82+
my $inversions = 0;
83+
for my $branch2 (@BRANCHES) {
84+
if (defined $leader->{'branch_position'}{$branch2}) {
85+
$inversions += $leader->{'branch_position'}{$branch2}
86+
- $position{$branch2};
87+
}
88+
}
89+
if (!defined $best_inversions || $inversions < $best_inversions) {
90+
$best_branch = $branch;
91+
$best_inversions = $inversions;
92+
}
93+
}
94+
last if !defined $best_branch;
95+
my $winner =
96+
$all_commits_by_branch{$best_branch}->[$position{$best_branch}];
97+
print $winner->{'header'};
98+
print "Commit-Order-Inversions: $best_inversions\n"
99+
if $best_inversions != 0;
100+
print $winner->{'message'};
101+
$winner->{'done'} = 1;
102+
for my $branch (@BRANCHES) {
103+
my $leader = $all_commits_by_branch{$branch}->[$position{$branch}];
104+
if (defined $leader && $leader->{'done'}) {
105+
++$position{$branch};
106+
redo;
107+
}
108+
}
109+
}
110+
111+
sub push_commit {
112+
my ($c) = @_;
113+
my $ht = hash_commit($c);
114+
my $ts = parse_datetime($c->{'date'});
115+
my $cc;
116+
for my $candidate (@{$all_commits{$ht}}) {
117+
if (abs($ts - $candidate->{'timestamp'}) < 600
118+
&& !exists $candidate->{'branch_position'}{$c->{'branch'}})
119+
{
120+
$cc = $candidate;
121+
last;
122+
}
123+
}
124+
if (!defined $cc) {
125+
$cc = {
126+
'header' => sprintf("Author: %s\n", $c->{'author'}),
127+
'message' => $c->{'message'},
128+
'timestamp' => $ts
129+
};
130+
push @{$all_commits{$ht}}, $cc;
131+
}
132+
$cc->{'header'} .= sprintf "Branch: %s [%s] %s\n",
133+
$c->{'branch'}, substr($c->{'commit'}, 0, 9), $c->{'date'};
134+
push @{$all_commits_by_branch{$c->{'branch'}}}, $cc;
135+
$cc->{'branch_position'}{$c->{'branch'}} =
136+
-1+@{$all_commits_by_branch{$c->{'branch'}}};
137+
}
138+
139+
sub hash_commit {
140+
my ($c) = @_;
141+
return $c->{'author'} . "\0" . $c->{'message'};
142+
}
143+
144+
sub parse_datetime {
145+
my ($dt) = @_;
146+
$dt =~ /^(\d\d\d\d)-(\d\d)-(\d\d)\s+(\d\d):(\d\d):(\d\d)/;
147+
return Date::Calc::Mktime($1, $2, $3, $4, $5, $6);
148+
}
149+
150+
sub usage {
151+
print STDERR <<EOM;
152+
Usage: git-topo-order [--since=SINCE]
153+
EOM
154+
exit 1;
155+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy