commit-graph: fix ordering bug in generation numbers

When computing the generation numbers for a commit-graph, we compute the corrected commit dates and then check if their offsets from the actual dates is too large to fit in the 32-bit Generation Data chunk. However, there is a problem with this approach: if we have parsed the generation data from the previous commit-graph, then we continue the loop because the corrected commit date is already computed. This causes an under-count in the number of overflow values. It is incorrect to add an increment to num_generation_data_overflows next to this 'continue' statement, because we might start double-counting commits that are computed because of the depth-first search walk from a commit with an earlier OID. Instead, iterate over the full commit list at the end, checking the offsets to see how many grow beyond the maximum value. Create a new t5328-commit-graph-64-bit-time.sh test script to handle special cases of testing 64-bit timestamps. This helps demonstrate this bug in more cases. It still won't hit all potential cases until the next change, which reenables reading generation numbers. Use the skip_all trick from 0a2bfccb9c8 (t0051: use "skip_all" under !MINGW in single-test file, 2022-02-04) to make the output clean when run on a 32-bit system. Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Derrick Stolee <derrickstolee@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Mar 1, 2022 at 19:48 UTC 75979d94607d92d53b1cad5d65f20594c66d275c
3 files changed +48 -5
commit-graph.c
+7 -3
@@ -1556,12 +1556,16 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
1556 if (current->date && current->date > max_corrected_commit_date)
1557 max_corrected_commit_date = current->date - 1;
1558 commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
1559 -
1560 - if (commit_graph_data_at(current)->generation - current->date > GENERATION_NUMBER_V2_OFFSET_MAX)
1561 - ctx->num_generation_data_overflows++;
1559 }
1560 }
1561 }
1562 +
1563 + for (i = 0; i < ctx->commits.nr; i++) {
1564 + struct commit *c = ctx->commits.list[i];
1565 + timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
1566 + if (offset > GENERATION_NUMBER_V2_OFFSET_MAX)
1567 + ctx->num_generation_data_overflows++;
1568 + }
1569 stop_progress(&ctx->progress);
1570 }
1571
t/t5318-commit-graph.sh
+2 -2
@@ -425,10 +425,10 @@ test_expect_success 'warn on improper hash version' '
425 )
426 '
427
428 -test_expect_success 'lower layers have overflow chunk' '
428 +test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow chunk' '
429 cd "$TRASH_DIRECTORY/full" &&
430 UNIX_EPOCH_ZERO="@0 +0000" &&
431 - FUTURE_DATE="@2147483646 +0000" &&
431 + FUTURE_DATE="@4147483646 +0000" &&
432 rm -f .git/objects/info/commit-graph &&
433 test_commit --date "$FUTURE_DATE" future-1 &&
434 test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
t/t5328-commit-graph-64bit-time.sh new
+39
@@ -0,0 +1,39 @@
1 +#!/bin/sh
2 +
3 +test_description='commit graph with 64-bit timestamps'
4 +. ./test-lib.sh
5 +
6 +if ! test_have_prereq TIME_IS_64BIT || ! test_have_prereq TIME_T_IS_64BIT
7 +then
8 + skip_all='skipping 64-bit timestamp tests'
9 + test_done
10 +fi
11 +
12 +. "$TEST_DIRECTORY"/lib-commit-graph.sh
13 +
14 +UNIX_EPOCH_ZERO="@0 +0000"
15 +FUTURE_DATE="@4147483646 +0000"
16 +
17 +GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
18 +
19 +test_expect_success 'lower layers have overflow chunk' '
20 + rm -f .git/objects/info/commit-graph &&
21 + test_commit --date "$FUTURE_DATE" future-1 &&
22 + test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
23 + git commit-graph write --reachable &&
24 + test_commit --date "$FUTURE_DATE" future-2 &&
25 + test_commit --date "$UNIX_EPOCH_ZERO" old-2 &&
26 + git commit-graph write --reachable --split=no-merge &&
27 + test_commit extra &&
28 + git commit-graph write --reachable --split=no-merge &&
29 + git commit-graph write --reachable &&
30 + graph_read_expect 5 "generation_data generation_data_overflow" &&
31 + mv .git/objects/info/commit-graph commit-graph-upgraded &&
32 + git commit-graph write --reachable &&
33 + graph_read_expect 5 "generation_data generation_data_overflow" &&
34 + test_cmp .git/objects/info/commit-graph commit-graph-upgraded
35 +'
36 +
37 +graph_git_behavior 'overflow' '' HEAD~2 HEAD
38 +
39 +test_done