Skip to content

Commit a4b1a14

Browse files
committed
Merge branch 'rs/merge-base-optim'
The code to walk revision graph to compute merge base has been optimized. * rs/merge-base-optim: commit-reach: avoid commit_list_insert_by_date()
2 parents 249b0d3 + 134ec33 commit a4b1a14

File tree

2 files changed

+110
-5
lines changed

2 files changed

+110
-5
lines changed

commit-reach.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ static int paint_down_to_common(struct repository *r,
6060
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
6161
int i;
6262
timestamp_t last_gen = GENERATION_NUMBER_INFINITY;
63+
struct commit_list **tail = result;
6364

6465
if (!min_generation && !corrected_commit_dates_enabled(r))
6566
queue.compare = compare_commits_by_commit_date;
@@ -95,7 +96,7 @@ static int paint_down_to_common(struct repository *r,
9596
if (flags == (PARENT1 | PARENT2)) {
9697
if (!(commit->object.flags & RESULT)) {
9798
commit->object.flags |= RESULT;
98-
commit_list_insert_by_date(commit, result);
99+
tail = commit_list_append(commit, tail);
99100
}
100101
/* Mark parents of a found merge stale */
101102
flags |= STALE;
@@ -128,6 +129,7 @@ static int paint_down_to_common(struct repository *r,
128129
}
129130

130131
clear_prio_queue(&queue);
132+
commit_list_sort_by_date(result);
131133
return 0;
132134
}
133135

@@ -136,7 +138,7 @@ static int merge_bases_many(struct repository *r,
136138
struct commit **twos,
137139
struct commit_list **result)
138140
{
139-
struct commit_list *list = NULL;
141+
struct commit_list *list = NULL, **tail = result;
140142
int i;
141143

142144
for (i = 0; i < n; i++) {
@@ -171,8 +173,9 @@ static int merge_bases_many(struct repository *r,
171173
while (list) {
172174
struct commit *commit = pop_commit(&list);
173175
if (!(commit->object.flags & STALE))
174-
commit_list_insert_by_date(commit, result);
176+
tail = commit_list_append(commit, tail);
175177
}
178+
commit_list_sort_by_date(result);
176179
return 0;
177180
}
178181

@@ -425,7 +428,7 @@ static int get_merge_bases_many_0(struct repository *r,
425428
int cleanup,
426429
struct commit_list **result)
427430
{
428-
struct commit_list *list;
431+
struct commit_list *list, **tail = result;
429432
struct commit **rslt;
430433
size_t cnt, i;
431434
int ret;
@@ -461,7 +464,8 @@ static int get_merge_bases_many_0(struct repository *r,
461464
return -1;
462465
}
463466
for (i = 0; i < cnt; i++)
464-
commit_list_insert_by_date(rslt[i], result);
467+
tail = commit_list_append(rslt[i], tail);
468+
commit_list_sort_by_date(result);
465469
free(rslt);
466470
return 0;
467471
}

t/perf/p6010-merge-base.sh

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#!/bin/sh
2+
3+
test_description='Test git merge-base'
4+
5+
. ./perf-lib.sh
6+
7+
test_perf_fresh_repo
8+
9+
#
10+
# Creates lots of merges to make history traversal costly. In
11+
# particular it creates 2^($max_level-1)-1 2-way merges on top of
12+
# 2^($max_level-1) root commits. E.g., the commit history looks like
13+
# this for a $max_level of 3:
14+
#
15+
# _1_
16+
# / \
17+
# 2 3
18+
# / \ / \
19+
# 4 5 6 7
20+
#
21+
# The numbers are the fast-import marks, which also are the commit
22+
# messages. 1 is the HEAD commit and a merge, 2 and 3 are also merges,
23+
# 4-7 are the root commits.
24+
#
25+
build_history () {
26+
local max_level="$1" &&
27+
local level="${2:-1}" &&
28+
local mark="${3:-1}" &&
29+
if test $level -eq $max_level
30+
then
31+
echo "reset refs/heads/master" &&
32+
echo "from $ZERO_OID" &&
33+
echo "commit refs/heads/master" &&
34+
echo "mark :$mark" &&
35+
echo "committer C <c@example.com> 1234567890 +0000" &&
36+
echo "data <<EOF" &&
37+
echo "$mark" &&
38+
echo "EOF"
39+
else
40+
local level1=$((level+1)) &&
41+
local mark1=$((2*mark)) &&
42+
local mark2=$((2*mark+1)) &&
43+
build_history $max_level $level1 $mark1 &&
44+
build_history $max_level $level1 $mark2 &&
45+
echo "commit refs/heads/master" &&
46+
echo "mark :$mark" &&
47+
echo "committer C <c@example.com> 1234567890 +0000" &&
48+
echo "data <<EOF" &&
49+
echo "$mark" &&
50+
echo "EOF" &&
51+
echo "from :$mark1" &&
52+
echo "merge :$mark2"
53+
fi
54+
}
55+
56+
#
57+
# Creates a new merge history in the same shape as build_history does,
58+
# while reusing the same root commits. This way the two top commits
59+
# have 2^($max_level-1) merge bases between them.
60+
#
61+
build_history2 () {
62+
local max_level="$1" &&
63+
local level="${2:-1}" &&
64+
local mark="${3:-1}" &&
65+
if test $level -lt $max_level
66+
then
67+
local level1=$((level+1)) &&
68+
local mark1=$((2*mark)) &&
69+
local mark2=$((2*mark+1)) &&
70+
build_history2 $max_level $level1 $mark1 &&
71+
build_history2 $max_level $level1 $mark2 &&
72+
echo "commit refs/heads/master" &&
73+
echo "mark :$mark" &&
74+
echo "committer C <c@example.com> 1234567890 +0000" &&
75+
echo "data <<EOF" &&
76+
echo "$mark II" &&
77+
echo "EOF" &&
78+
echo "from :$mark1" &&
79+
echo "merge :$mark2"
80+
fi
81+
}
82+
83+
test_expect_success 'setup' '
84+
max_level=15 &&
85+
build_history $max_level | git fast-import --export-marks=marks &&
86+
git tag one &&
87+
build_history2 $max_level | git fast-import --import-marks=marks --force &&
88+
git tag two &&
89+
git gc &&
90+
git log --format=%H --no-merges >expect
91+
'
92+
93+
test_perf 'git merge-base' '
94+
git merge-base --all one two >actual
95+
'
96+
97+
test_expect_success 'verify result' '
98+
test_cmp expect actual
99+
'
100+
101+
test_done

0 commit comments

Comments
 (0)