aboutsummaryrefslogtreecommitdiff
path: root/commit-graph.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2026-04-07 14:59:25 -0700
committerJunio C Hamano <gitster@pobox.com>2026-04-07 14:59:25 -0700
commitd88c8ba70bbcbc45a31cf05034a8e0c6884fb3d3 (patch)
treedb7f8cc1f8de8bf1e6a915561e072c4bbbec15a7 /commit-graph.c
parent716b9db0d3e1662fe2b84582a8b84ee3ff9f867d (diff)
parent04c9c5e8d2d99050d260149cad9dde1302a02ff4 (diff)
downloadgit-d88c8ba70bbcbc45a31cf05034a8e0c6884fb3d3.tar.xz
Merge branch 'ps/commit-graph-overflow-fix'
Fix a regression in writing the commit-graph where commits with dates exceeding 34 bits (beyond year 2514) could cause an underflow and crash Git during the generation data overflow chunk writing. * ps/commit-graph-overflow-fix: commit-graph: fix writing generations with dates exceeding 34 bits
Diffstat (limited to 'commit-graph.c')
-rw-r--r--commit-graph.c37
1 files changed, 34 insertions, 3 deletions
diff --git a/commit-graph.c b/commit-graph.c
index df4b4a125e..9abe62bd5a 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1319,6 +1319,37 @@ static int write_graph_chunk_data(struct hashfile *f,
return 0;
}
+/*
+ * Compute the generation offset between the commit date and its generation.
+ * This is what's ultimately stored as generation number in the commit graph.
+ *
+ * Note that the computation of the commit date is more involved than you might
+ * think. Instead of using the full commit date, we're in fact masking bits so
+ * that only the 34 lowest bits are considered. This results from the fact that
+ * commit graphs themselves only ever store 34 bits of the commit date
+ * themselves.
+ *
+ * This means that if we have a commit date that exceeds 34 bits we'll end up
+ * in situations where depending on whether the commit has been parsed from the
+ * object database or the commit graph we'll have different dates, where the
+ * ones parsed from the object database would have full 64 bit precision.
+ *
+ * But ultimately, we only ever want the offset to be relative to what we
+ * actually end up storing on disk, and hence we have to mask all the other
+ * bits.
+ */
+static timestamp_t compute_generation_offset(struct commit *c)
+{
+ timestamp_t masked_date;
+
+ if (sizeof(timestamp_t) > 4)
+ masked_date = c->date & (((timestamp_t) 1 << 34) - 1);
+ else
+ masked_date = c->date;
+
+ return commit_graph_data_at(c)->generation - masked_date;
+}
+
static int write_graph_chunk_generation_data(struct hashfile *f,
void *data)
{
@@ -1329,7 +1360,7 @@ static int write_graph_chunk_generation_data(struct hashfile *f,
struct commit *c = ctx->commits.items[i];
timestamp_t offset;
repo_parse_commit(ctx->r, c);
- offset = commit_graph_data_at(c)->generation - c->date;
+ offset = compute_generation_offset(c);
display_progress(ctx->progress, ++ctx->progress_cnt);
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
@@ -1350,7 +1381,7 @@ static int write_graph_chunk_generation_data_overflow(struct hashfile *f,
int i;
for (i = 0; i < ctx->commits.nr; i++) {
struct commit *c = ctx->commits.items[i];
- timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
+ timestamp_t offset = compute_generation_offset(c);
display_progress(ctx->progress, ++ctx->progress_cnt);
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
@@ -1741,7 +1772,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
for (i = 0; i < ctx->commits.nr; i++) {
struct commit *c = ctx->commits.items[i];
- timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
+ timestamp_t offset = compute_generation_offset(c);
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX)
ctx->num_generation_data_overflows++;
}