]> git.ipfire.org Git - thirdparty/git.git/commitdiff
commit-graph: fix ordering bug in generation numbers
authorDerrick Stolee <derrickstolee@github.com>
Tue, 1 Mar 2022 19:48:30 +0000 (19:48 +0000)
committerJunio C Hamano <gitster@pobox.com>
Tue, 1 Mar 2022 20:14:57 +0000 (12:14 -0800)
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>
commit-graph.c
t/t5318-commit-graph.sh
t/t5328-commit-graph-64bit-time.sh [new file with mode: 0755]

index 265c010122e8edefc141f1dec8f506078bbbee19..a19bd96c2eee602555d043431ac5b8841f6336d5 100644 (file)
@@ -1556,12 +1556,16 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
                                if (current->date && current->date > max_corrected_commit_date)
                                        max_corrected_commit_date = current->date - 1;
                                commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
-
-                               if (commit_graph_data_at(current)->generation - current->date > GENERATION_NUMBER_V2_OFFSET_MAX)
-                                       ctx->num_generation_data_overflows++;
                        }
                }
        }
+
+       for (i = 0; i < ctx->commits.nr; i++) {
+               struct commit *c = ctx->commits.list[i];
+               timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
+               if (offset > GENERATION_NUMBER_V2_OFFSET_MAX)
+                       ctx->num_generation_data_overflows++;
+       }
        stop_progress(&ctx->progress);
 }
 
index 9e2b5884dae641253e95c3f004bddad9fca7b400..0ed7e9de8e6c17123c5ca8ce28313b601f156199 100755 (executable)
@@ -425,10 +425,10 @@ test_expect_success 'warn on improper hash version' '
        )
 '
 
-test_expect_success 'lower layers have overflow chunk' '
+test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow chunk' '
        cd "$TRASH_DIRECTORY/full" &&
        UNIX_EPOCH_ZERO="@0 +0000" &&
-       FUTURE_DATE="@2147483646 +0000" &&
+       FUTURE_DATE="@4147483646 +0000" &&
        rm -f .git/objects/info/commit-graph &&
        test_commit --date "$FUTURE_DATE" future-1 &&
        test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
diff --git a/t/t5328-commit-graph-64bit-time.sh b/t/t5328-commit-graph-64bit-time.sh
new file mode 100755 (executable)
index 0000000..28114bc
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='commit graph with 64-bit timestamps'
+. ./test-lib.sh
+
+if ! test_have_prereq TIME_IS_64BIT || ! test_have_prereq TIME_T_IS_64BIT
+then
+       skip_all='skipping 64-bit timestamp tests'
+       test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-commit-graph.sh
+
+UNIX_EPOCH_ZERO="@0 +0000"
+FUTURE_DATE="@4147483646 +0000"
+
+GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
+
+test_expect_success 'lower layers have overflow chunk' '
+       rm -f .git/objects/info/commit-graph &&
+       test_commit --date "$FUTURE_DATE" future-1 &&
+       test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
+       git commit-graph write --reachable &&
+       test_commit --date "$FUTURE_DATE" future-2 &&
+       test_commit --date "$UNIX_EPOCH_ZERO" old-2 &&
+       git commit-graph write --reachable --split=no-merge &&
+       test_commit extra &&
+       git commit-graph write --reachable --split=no-merge &&
+       git commit-graph write --reachable &&
+       graph_read_expect 5 "generation_data generation_data_overflow" &&
+       mv .git/objects/info/commit-graph commit-graph-upgraded &&
+       git commit-graph write --reachable &&
+       graph_read_expect 5 "generation_data generation_data_overflow" &&
+       test_cmp .git/objects/info/commit-graph commit-graph-upgraded
+'
+
+graph_git_behavior 'overflow' '' HEAD~2 HEAD
+
+test_done