Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit bf429ce

Browse files
committed
Improve git_changelog as per discussion with Robert Haas.
1. Resurrect the behavior where old commits on master will have Branch: labels for branches sprouted after the commit was made. I'm still dubious about this mode, but if you want it, say --post-date or -p. 2. Annotate the Branch: labels with the release or branch in which the commit was publicly released. For example, on a release branch you could see Branch: REL8_3_STABLE Release: REL8_3_2 [92c3a80] 2008-03-29 00:15:37 +0000 showing that the fix was released in 8.3.2. Commits on master will usually instead have notes like Branch: master Release: REL8_4_BR [6fc9d42] 2008-03-29 00:15:28 +0000 showing that this commit is ancestral to release branches 8.4 and later. If no Release: marker appears, the commit hasn't yet made it into any release. 3. Add support for release branches older than 7.4. 4. The implementation is improved by running git log on each branch only back to where the branch sprouts from master. This saves a good deal of time (about 50% of the runtime when generating the complete history). We generate the post-date-mode tags via a direct understanding that they should be applied to master commits made before the branch sprouted, rather than backing into them via matching (which isn't any too reliable when people used identical log messages for successive commits).
1 parent e440e12 commit bf429ce

File tree

1 file changed

+119
-21
lines changed

1 file changed

+119
-21
lines changed

src/tools/git_changelog

Lines changed: 119 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@
55
#
66
# Display all commits on active branches, merging together commits from
77
# different branches that occur close together in time and with identical
8-
# log messages.
8+
# log messages. Commits are annotated with branch and release info thus:
9+
# Branch: REL8_3_STABLE Release: REL8_3_2 [92c3a8004] 2008-03-29 00:15:37 +0000
10+
# This shows that the commit on REL8_3_STABLE was released in 8.3.2.
11+
# Commits on master will usually instead have notes like
12+
# Branch: master Release: REL8_4_BR [6fc9d4272] 2008-03-29 00:15:28 +0000
13+
# showing that this commit is ancestral to release branches 8.4 and later.
14+
# If no Release: marker appears, the commit hasn't yet made it into any
15+
# release.
916
#
1017
# Most of the time, matchable commits occur in the same order on all branches,
1118
# and we print them out in that order. However, if commit A occurs before
@@ -26,36 +33,84 @@ require Time::Local;
2633
require Getopt::Long;
2734
require IPC::Open2;
2835

29-
# Adjust this list when the set of active branches changes.
36+
# Adjust this list when the set of interesting branches changes.
37+
# (We could get this from "git branches", but not worth the trouble.)
38+
# NB: master must be first!
3039
my @BRANCHES = qw(master REL9_0_STABLE REL8_4_STABLE REL8_3_STABLE
31-
REL8_2_STABLE REL8_1_STABLE REL8_0_STABLE REL7_4_STABLE);
40+
REL8_2_STABLE REL8_1_STABLE REL8_0_STABLE REL7_4_STABLE REL7_3_STABLE
41+
REL7_2_STABLE REL7_1_STABLE REL7_0_PATCHES REL6_5_PATCHES REL6_4);
3242

3343
# Might want to make this parameter user-settable.
3444
my $timestamp_slop = 600;
3545

46+
my $post_date = 0;
3647
my $since;
37-
Getopt::Long::GetOptions('since=s' => \$since) || usage();
48+
Getopt::Long::GetOptions('post-date' => \$post_date,
49+
'since=s' => \$since) || usage();
3850
usage() if @ARGV;
3951

4052
my @git = qw(git log --date=iso);
4153
push @git, '--since=' . $since if defined $since;
4254

55+
# Collect the release tag data
56+
my %rel_tags;
57+
58+
{
59+
my $cmd = "git for-each-ref refs/tags";
60+
my $pid = IPC::Open2::open2(my $git_out, my $git_in, $cmd)
61+
|| die "can't run $cmd: $!";
62+
while (my $line = <$git_out>) {
63+
if ($line =~ m|^([a-f0-9]+)\s+commit\s+refs/tags/(\S+)|) {
64+
my $commit = $1;
65+
my $tag = $2;
66+
if ($tag =~ /^REL\d+_\d+$/ ||
67+
$tag =~ /^REL\d+_\d+_\d+$/) {
68+
$rel_tags{$commit} = $tag;
69+
}
70+
}
71+
}
72+
waitpid($pid, 0);
73+
my $child_exit_status = $? >> 8;
74+
die "$cmd failed" if $child_exit_status != 0;
75+
}
76+
77+
# Collect the commit data
4378
my %all_commits;
4479
my %all_commits_by_branch;
80+
# This remembers where each branch sprouted from master. Note the values
81+
# will be wrong if --since terminates the log listing before the branch
82+
# sprouts; but in that case it doesn't matter since we also won't reach
83+
# the part of master where it would matter.
84+
my %sprout_tags;
4585

4686
for my $branch (@BRANCHES) {
47-
my $pid =
48-
IPC::Open2::open2(my $git_out, my $git_in, @git, "origin/$branch")
49-
|| die "can't run @git origin/$branch: $!";
87+
my @cmd = @git;
88+
if ($branch eq "master") {
89+
push @cmd, "origin/$branch";
90+
} else {
91+
push @cmd, "--parents";
92+
push @cmd, "master..origin/$branch";
93+
}
94+
my $pid = IPC::Open2::open2(my $git_out, my $git_in, @cmd)
95+
|| die "can't run @cmd: $!";
96+
my $last_tag = undef;
97+
my $last_parent;
5098
my %commit;
5199
while (my $line = <$git_out>) {
52-
if ($line =~ /^commit\s+(.*)/) {
100+
if ($line =~ /^commit\s+(\S+)/) {
53101
push_commit(\%commit) if %commit;
102+
$last_tag = $rel_tags{$1} if defined $rel_tags{$1};
54103
%commit = (
55104
'branch' => $branch,
56105
'commit' => $1,
106+
'last_tag' => $last_tag,
57107
'message' => '',
58108
);
109+
if ($line =~ /^commit\s+\S+\s+(\S+)/) {
110+
$last_parent = $1;
111+
} else {
112+
$last_parent = undef;
113+
}
59114
}
60115
elsif ($line =~ /^Author:\s+(.*)/) {
61116
$commit{'author'} = $1;
@@ -68,9 +123,43 @@ for my $branch (@BRANCHES) {
68123
}
69124
}
70125
push_commit(\%commit) if %commit;
126+
$sprout_tags{$last_parent} = $branch if defined $last_parent;
71127
waitpid($pid, 0);
72128
my $child_exit_status = $? >> 8;
73-
die "@git origin/$branch failed" if $child_exit_status != 0;
129+
die "@cmd failed" if $child_exit_status != 0;
130+
}
131+
132+
# Run through the master branch and apply tags. We already tagged the other
133+
# branches, but master needs a separate pass after we've acquired the
134+
# sprout_tags data. Also, in post-date mode we need to add phony entries
135+
# for branches that sprouted after a particular master commit was made.
136+
{
137+
my $last_tag = undef;
138+
my %sprouted_branches;
139+
for my $cc (@{$all_commits_by_branch{'master'}}) {
140+
my $commit = $cc->{'commit'};
141+
my $c = $cc->{'commits'}->[0];
142+
$last_tag = $rel_tags{$commit} if defined $rel_tags{$commit};
143+
if (defined $sprout_tags{$commit}) {
144+
$last_tag = $sprout_tags{$commit};
145+
# normalize branch names for making sprout tags
146+
$last_tag =~ s/^(REL\d+_\d+).*/$1_BR/;
147+
}
148+
$c->{'last_tag'} = $last_tag;
149+
if ($post_date) {
150+
if (defined $sprout_tags{$commit}) {
151+
$sprouted_branches{$sprout_tags{$commit}} = 1;
152+
}
153+
# insert new commits between master and any other commits
154+
my @new_commits = ( shift @{$cc->{'commits'}} );
155+
for my $branch (reverse sort keys %sprouted_branches) {
156+
my $ccopy = {%{$c}};
157+
$ccopy->{'branch'} = $branch;
158+
push @new_commits, $ccopy;
159+
}
160+
$cc->{'commits'} = [ @new_commits, @{$cc->{'commits'}} ];
161+
}
162+
}
74163
}
75164

76165
my %position;
@@ -104,7 +193,14 @@ while (1) {
104193
last if !defined $best_branch;
105194
my $winner =
106195
$all_commits_by_branch{$best_branch}->[$position{$best_branch}];
107-
print $winner->{'header'};
196+
printf "Author: %s\n", $winner->{'author'};
197+
foreach my $c (@{$winner->{'commits'}}) {
198+
printf "Branch: %s", $c->{'branch'};
199+
if (defined $c->{'last_tag'}) {
200+
printf " Release: %s", $c->{'last_tag'};
201+
}
202+
printf " [%s] %s\n", substr($c->{'commit'}, 0, 9), $c->{'date'};
203+
}
108204
print "Commit-Order-Inversions: $best_inversions\n"
109205
if $best_inversions != 0;
110206
print "\n";
@@ -143,22 +239,22 @@ sub push_commit {
143239
}
144240
if (!defined $cc) {
145241
$cc = {
146-
'header' => sprintf("Author: %s\n", $c->{'author'}),
242+
'author' => $c->{'author'},
147243
'message' => $c->{'message'},
148244
'commit' => $c->{'commit'},
245+
'commits' => [],
149246
'timestamp' => $ts
150247
};
151248
push @{$all_commits{$ht}}, $cc;
152-
} elsif ($cc->{'commit'} eq $c->{'commit'}) {
153-
# If this is exactly the same commit we saw before on another
154-
# branch, ignore it. Hence, a commit that's reachable from more
155-
# than one branch head will be reported only for the first
156-
# head it's reachable from. This will give the desired results
157-
# so long as @BRANCHES is ordered with master first.
158-
return;
159249
}
160-
$cc->{'header'} .= sprintf "Branch: %s [%s] %s\n",
161-
$c->{'branch'}, substr($c->{'commit'}, 0, 9), $c->{'date'};
250+
# stash only the fields we'll need later
251+
my $smallc = {
252+
'branch' => $c->{'branch'},
253+
'commit' => $c->{'commit'},
254+
'date' => $c->{'date'},
255+
'last_tag' => $c->{'last_tag'}
256+
};
257+
push @{$cc->{'commits'}}, $smallc;
162258
push @{$all_commits_by_branch{$c->{'branch'}}}, $cc;
163259
$cc->{'branch_position'}{$c->{'branch'}} =
164260
-1+@{$all_commits_by_branch{$c->{'branch'}}};
@@ -180,7 +276,9 @@ sub parse_datetime {
180276

181277
sub usage {
182278
print STDERR <<EOM;
183-
Usage: git_changelog [--since=SINCE]
279+
Usage: git_changelog [--post-date/-p] [--since=SINCE]
280+
--post-date Show branches made after a commit occurred
281+
--since Print only commits dated since SINCE
184282
EOM
185283
exit 1;
186284
}

0 commit comments

Comments
 (0)