Skip to content

Commit 8791bf1

Browse files
derrickstoleegitster
authored andcommitted
commit-reach: fix in_merge_bases_many bug
Way back in f9b8908 (commit.c: use generation numbers for in_merge_bases(), 2018-05-01), a heuristic was used to short-circuit the in_merge_bases() walk. This works just fine as long as the caller is checking only two commits, but when there are multiple, there is a possibility that this heuristic is _very wrong_. Some code moves since then has changed this method to repo_in_merge_bases_many() inside commit-reach.c. The heuristic computes the minimum generation number of the "reference" list, then compares this number to the generation number of the "commit". In a recent topic, a test was added that used in_merge_bases_many() to test if a commit was reachable from a number of commits pulled from a reflog. However, this highlighted the problem: if any of the reference commits have a smaller generation number than the given commit, then the walk is skipped _even if there exist some with higher generation number_. This heuristic is wrong! It must check the MAXIMUM generation number of the reference commits, not the MINIMUM. This highlights a testing gap. t6600-test-reach.sh covers many methods in commit-reach.c, including in_merge_bases() and get_merge_bases_many(), but since these methods either restrict to two input commits or actually look for the full list of merge bases, they don't check this heuristic! Add a possible input to "test-tool reach" that tests in_merge_bases_many() and add tests to t6600-test-reach.sh that cover this heuristic. This includes cases for the reference commits having generation above and below the generation of the input commit, but also having maximum generation below the generation of the input commit. The fix itself is to swap min_generation with a max_generation in repo_in_merge_bases_many(). Reported-by: Srinidhi Kaushik <[email protected]> Helped-by: Johannes Schindelin <[email protected]> Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 47ae905 commit 8791bf1

File tree

3 files changed

+36
-4
lines changed

3 files changed

+36
-4
lines changed

commit-reach.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
321321
{
322322
struct commit_list *bases;
323323
int ret = 0, i;
324-
uint32_t generation, min_generation = GENERATION_NUMBER_INFINITY;
324+
uint32_t generation, max_generation = GENERATION_NUMBER_ZERO;
325325

326326
if (repo_parse_commit(r, commit))
327327
return ret;
@@ -330,12 +330,12 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
330330
return ret;
331331

332332
generation = commit_graph_generation(reference[i]);
333-
if (generation < min_generation)
334-
min_generation = generation;
333+
if (generation > max_generation)
334+
max_generation = generation;
335335
}
336336

337337
generation = commit_graph_generation(commit);
338-
if (generation > min_generation)
338+
if (generation > max_generation)
339339
return ret;
340340

341341
bases = paint_down_to_common(r, commit,

t/helper/test-reach.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ int cmd__reach(int ac, const char **av)
107107
printf("%s(A,B):%d\n", av[1], ref_newer(&oid_A, &oid_B));
108108
else if (!strcmp(av[1], "in_merge_bases"))
109109
printf("%s(A,B):%d\n", av[1], in_merge_bases(A, B));
110+
else if (!strcmp(av[1], "in_merge_bases_many"))
111+
printf("%s(A,X):%d\n", av[1], in_merge_bases_many(A, X_nr, X_array));
110112
else if (!strcmp(av[1], "is_descendant_of"))
111113
printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X));
112114
else if (!strcmp(av[1], "get_merge_bases_many")) {

t/t6600-test-reach.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,36 @@ test_expect_success 'in_merge_bases:miss' '
110110
test_three_modes in_merge_bases
111111
'
112112

113+
test_expect_success 'in_merge_bases_many:hit' '
114+
cat >input <<-\EOF &&
115+
A:commit-6-8
116+
X:commit-6-9
117+
X:commit-5-7
118+
EOF
119+
echo "in_merge_bases_many(A,X):1" >expect &&
120+
test_three_modes in_merge_bases_many
121+
'
122+
123+
test_expect_success 'in_merge_bases_many:miss' '
124+
cat >input <<-\EOF &&
125+
A:commit-6-8
126+
X:commit-7-7
127+
X:commit-8-6
128+
EOF
129+
echo "in_merge_bases_many(A,X):0" >expect &&
130+
test_three_modes in_merge_bases_many
131+
'
132+
133+
test_expect_success 'in_merge_bases_many:miss-heuristic' '
134+
cat >input <<-\EOF &&
135+
A:commit-6-8
136+
X:commit-7-5
137+
X:commit-6-6
138+
EOF
139+
echo "in_merge_bases_many(A,X):0" >expect &&
140+
test_three_modes in_merge_bases_many
141+
'
142+
113143
test_expect_success 'is_descendant_of:hit' '
114144
cat >input <<-\EOF &&
115145
A:commit-5-7

0 commit comments

Comments
 (0)