]> git.ipfire.org Git - thirdparty/git.git/commitdiff
repack: allow `--write-midx=incremental` without `--geometric`
authorTaylor Blau <me@ttaylorr.com>
Tue, 19 May 2026 15:58:25 +0000 (11:58 -0400)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 May 2026 02:31:14 +0000 (11:31 +0900)
Previously, `--write-midx=incremental` required `--geometric` and would
die() without it. Relax this restriction so that incremental MIDX
repacking can be used independently.

Without `--geometric`, the behavior is append-only: a single new MIDX
layer is created containing whatever packs were written by the repack
and appended to the existing chain (or a new chain is started). Existing
layers are preserved as-is with no compaction or merging.

Implement this via a new repack_make_midx_append_plan() that builds a
plan consisting of a WRITE step for the freshly written packs followed
by COPY steps for every existing MIDX layer. The existing compaction
plan (repack_make_midx_compaction_plan) is used only when `--geometric`
is active.

Update the documentation to describe the behavior with and without
`--geometric`, and replace the test that enforced the old restriction
with one exercising append-only incremental MIDX repacking.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-repack.adoc
builtin/repack.c
repack-midx.c
t/t7705-repack-incremental-midx.sh

index 27a99cc46f4adacb6f1263115d9663407afa3cae..72c42015e23f9493c2cd4571be58168022301c90 100644 (file)
@@ -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,
index 5ffa18e085e49af9bb8ccf0c001900294a5f1b4a..1524a9c13ad5b87e97adc7b5b5a710f22cadbe4c 100644 (file)
@@ -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()))
index 4f5deeb97bf0337609bc09cbebd5d013e9926761..b6b1de718058dac998180bac067d424966f11b25 100644 (file)
@@ -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++) {
index 9e317ff6e8f2fb351c955440910d807b62ea6423..25a8c40e8ee5ccfbc87b1677813e5d763c5ff060 100755 (executable)
@@ -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 &&
+
+               git config maintenance.auto false &&
+
+               test_commit first &&
+               git repack -d &&
 
-       test_grep -- "--write-midx=incremental requires --geometric" err
+               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 &&