diff --git a/Documentation/git-repack.adoc b/Documentation/git-repack.adoc index 27a99cc46f..72c42015e2 100644 --- a/Documentation/git-repack.adoc +++ b/Documentation/git-repack.adoc @@ -263,14 +263,19 @@ linkgit:git-multi-pack-index[1]). `incremental`;; Write an incremental MIDX chain instead of a single - flat MIDX. This mode requires `--geometric`. + flat MIDX. + -The incremental mode maintains a chain of MIDX layers that is compacted -over time using a geometric merging strategy. Each repack creates a new -tip layer containing the newly written pack(s). Adjacent layers are then -merged whenever the newer layer's object count exceeds -`1/repack.midxSplitFactor` of the next deeper layer's count. Layers -that do not meet this condition are retained as-is. +Without `--geometric`, a new MIDX layer is appended to the existing +chain (or a new chain is started) containing whatever packs were written +by the repack. Existing layers are preserved as-is. ++ +When combined with `--geometric`, the incremental mode maintains a chain +of MIDX layers that is compacted over time using a geometric merging +strategy. Each repack creates a new tip layer containing the newly +written pack(s). Adjacent layers are then merged whenever the newer +layer's object count exceeds `1/repack.midxSplitFactor` of the next +deeper layer's count. Layers that do not meet this condition are +retained as-is. + The result is that newer (tip) layers tend to contain many small packs with relatively few objects, while older (deeper) layers contain fewer, diff --git a/builtin/repack.c b/builtin/repack.c index 5ffa18e085..1524a9c13a 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -263,9 +263,6 @@ int cmd_repack(int argc, if (pack_everything & PACK_CRUFT) pack_everything |= ALL_INTO_ONE; - if (write_midx == REPACK_WRITE_MIDX_INCREMENTAL && !geometry.split_factor) - die(_("--write-midx=incremental requires --geometric")); - if (write_bitmaps < 0) { if (write_midx == REPACK_WRITE_MIDX_NONE && (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository())) diff --git a/repack-midx.c b/repack-midx.c index 4f5deeb97b..b6b1de7180 100644 --- a/repack-midx.c +++ b/repack-midx.c @@ -548,6 +548,60 @@ static void midx_compaction_step_release(struct midx_compaction_step *step) free(step->csum); } +/* + * Build an append-only MIDX plan: a single WRITE step for the freshly + * written packs, plus COPY steps for every existing layer. No + * compaction or merging is performed. + */ +static void repack_make_midx_append_plan(struct repack_write_midx_opts *opts, + struct midx_compaction_step **steps_p, + size_t *steps_nr_p) +{ + struct multi_pack_index *m; + struct midx_compaction_step *steps = NULL; + struct midx_compaction_step *step; + size_t steps_nr = 0, steps_alloc = 0; + + odb_reprepare(opts->existing->repo->objects); + m = get_multi_pack_index(opts->existing->source); + + if (opts->names->nr) { + struct strbuf buf = STRBUF_INIT; + uint32_t i; + + ALLOC_GROW(steps, st_add(steps_nr, 1), steps_alloc); + + step = &steps[steps_nr++]; + memset(step, 0, sizeof(*step)); + + step->type = MIDX_COMPACTION_STEP_WRITE; + string_list_init_dup(&step->u.write); + + for (i = 0; i < opts->names->nr; i++) { + strbuf_reset(&buf); + strbuf_addf(&buf, "pack-%s.idx", + opts->names->items[i].string); + string_list_append(&step->u.write, buf.buf); + } + + strbuf_release(&buf); + } + + for (; m; m = m->base_midx) { + ALLOC_GROW(steps, st_add(steps_nr, 1), steps_alloc); + + step = &steps[steps_nr++]; + memset(step, 0, sizeof(*step)); + + step->type = MIDX_COMPACTION_STEP_COPY; + step->u.copy = m; + step->objects_nr = m->num_objects; + } + + *steps_p = steps; + *steps_nr_p = steps_nr; +} + static int repack_make_midx_compaction_plan(struct repack_write_midx_opts *opts, struct midx_compaction_step **steps_p, size_t *steps_nr_p) @@ -904,9 +958,13 @@ static int write_midx_incremental(struct repack_write_midx_opts *opts) goto done; } - if (repack_make_midx_compaction_plan(opts, &steps, &steps_nr) < 0) { - ret = error(_("unable to generate compaction plan")); - goto done; + if (opts->geometry->split_factor) { + if (repack_make_midx_compaction_plan(opts, &steps, &steps_nr) < 0) { + ret = error(_("unable to generate compaction plan")); + goto done; + } + } else { + repack_make_midx_append_plan(opts, &steps, &steps_nr); } for (i = 0; i < steps_nr; i++) { diff --git a/t/t7705-repack-incremental-midx.sh b/t/t7705-repack-incremental-midx.sh index 9e317ff6e8..25a8c40e8e 100755 --- a/t/t7705-repack-incremental-midx.sh +++ b/t/t7705-repack-incremental-midx.sh @@ -63,10 +63,36 @@ create_layers () { done } -test_expect_success '--write-midx=incremental requires --geometric' ' - test_must_fail git repack --write-midx=incremental 2>err && +test_expect_success '--write-midx=incremental without --geometric' ' + git init incremental-without-geometric && + ( + cd incremental-without-geometric && - test_grep -- "--write-midx=incremental requires --geometric" err + git config maintenance.auto false && + + test_commit first && + git repack -d && + + test_commit second && + git repack --write-midx=incremental && + + git multi-pack-index verify && + test_line_count = 1 $midx_chain && + cp $midx_chain $midx_chain.before && + + # A second repack appends a new layer without + # disturbing the existing one. + test_commit third && + git repack --write-midx=incremental && + + git multi-pack-index verify && + test_line_count = 2 $midx_chain && + head -n 1 $midx_chain.before >expect && + head -n 1 $midx_chain >actual && + test_cmp expect actual && + + git fsck + ) ' test_expect_success 'below layer threshold, tip packs excluded' ' @@ -334,8 +360,7 @@ test_expect_success 'kept packs are excluded from repack' ' # entirely, so no rollup occurs as there is only one # non-kept pack. A new MIDX layer is written containing # that pack. - git repack --geometric=2 -d --write-midx=incremental \ - --write-bitmap-index && + git repack --geometric=2 -d --write-midx=incremental && test-tool read-midx $objdir >actual && grep "^pack-.*\.idx$" actual >actual.packs && @@ -433,6 +458,36 @@ test_expect_success 'repack -ad removes stale incremental chain' ' ) ' +test_expect_success 'repack -ad --write-midx=incremental is safe' ' + git init ad-incremental-midx && + ( + cd ad-incremental-midx && + + git config maintenance.auto false && + + # Build a MIDX chain with multiple layers referencing + # distinct packs. + test_commit first && + git repack -d && + + test_commit second && + git repack -d --write-midx=incremental && + + git multi-pack-index verify && + test_line_count = 1 $midx_chain && + + # Now do a full -ad repack. The new pack contains all + # objects, but any retained MIDX layers still reference + # the now-deleted packs. + test_commit third && + git repack -ad --write-midx=incremental && + + git multi-pack-index verify && + git fsck && + git rev-list --all --objects >/dev/null + ) +' + test_expect_success 'repack rejects invalid midxSplitFactor' ' test_when_finished "rm -fr bad-split-factor" && git init bad-split-factor &&