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 bitsmaint
commit
d88c8ba70b
|
|
@ -1319,6 +1319,37 @@ static int write_graph_chunk_data(struct hashfile *f,
|
||||||
return 0;
|
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,
|
static int write_graph_chunk_generation_data(struct hashfile *f,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
|
|
@ -1329,7 +1360,7 @@ static int write_graph_chunk_generation_data(struct hashfile *f,
|
||||||
struct commit *c = ctx->commits.items[i];
|
struct commit *c = ctx->commits.items[i];
|
||||||
timestamp_t offset;
|
timestamp_t offset;
|
||||||
repo_parse_commit(ctx->r, c);
|
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);
|
display_progress(ctx->progress, ++ctx->progress_cnt);
|
||||||
|
|
||||||
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
|
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
|
||||||
|
|
@ -1350,7 +1381,7 @@ static int write_graph_chunk_generation_data_overflow(struct hashfile *f,
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < ctx->commits.nr; i++) {
|
for (i = 0; i < ctx->commits.nr; i++) {
|
||||||
struct commit *c = ctx->commits.items[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);
|
display_progress(ctx->progress, ++ctx->progress_cnt);
|
||||||
|
|
||||||
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
|
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++) {
|
for (i = 0; i < ctx->commits.nr; i++) {
|
||||||
struct commit *c = ctx->commits.items[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)
|
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX)
|
||||||
ctx->num_generation_data_overflows++;
|
ctx->num_generation_data_overflows++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -417,6 +417,26 @@ test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow ch
|
||||||
test_cmp full/.git/objects/info/commit-graph commit-graph-upgraded
|
test_cmp full/.git/objects/info/commit-graph commit-graph-upgraded
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'overflow chunk when replacing commit-graph' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
cat >commit <<-EOF &&
|
||||||
|
tree $(test_oid empty_tree)
|
||||||
|
author Example <committer@example.com> 9223372036854775 +0000
|
||||||
|
committer Example <committer@example.com> 9223372036854775 +0000
|
||||||
|
|
||||||
|
Weird commit date
|
||||||
|
EOF
|
||||||
|
commit_id=$(git hash-object -t commit -w commit) &&
|
||||||
|
git reset --hard "$commit_id" &&
|
||||||
|
git commit-graph write --reachable &&
|
||||||
|
git commit-graph write --reachable --split=replace &&
|
||||||
|
git log
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
# the verify tests below expect the commit-graph to contain
|
# the verify tests below expect the commit-graph to contain
|
||||||
# exactly the commits reachable from the commits/8 branch.
|
# exactly the commits reachable from the commits/8 branch.
|
||||||
# If the file changes the set of commits in the list, then the
|
# If the file changes the set of commits in the list, then the
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue