Skip to content

Commit 0ea53fb

Browse files
committed
multi-pack-index: implement --max-chain-depth
Signed-off-by: Vicent Marti <vmg@strn.cat>
1 parent 1674d48 commit 0ea53fb

5 files changed

Lines changed: 211 additions & 3 deletions

File tree

Documentation/git-multi-pack-index.adoc

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ SYNOPSIS
1515
[--base=<checksum>]
1616
'git multi-pack-index' [<options>] compact [--[no-]incremental]
1717
[--[no-]bitmap] [--base=<checksum>] [--[no-]write-chain-file]
18-
<from> <to>
18+
[--max-chain-depth=<n> [--split-factor=<n>]] [<from> <to>]
1919
'git multi-pack-index' [<options>] verify
2020
'git multi-pack-index' [<options>] expire
2121
'git multi-pack-index' [<options>] repack [--batch-size=<size>]
@@ -125,6 +125,19 @@ compact::
125125
as the base for the compacted result, instead of using
126126
the immediate parent of `<from>`. The special value
127127
`none` indicates that the result should have no base.
128+
129+
--max-chain-depth=<n>::
130+
Automatically choose a MIDX layer range to compact when the
131+
current incremental MIDX chain has more than `<n>` layers.
132+
If the current chain has at most `<n>` layers, this option
133+
exits without writing a new MIDX. Otherwise, adjacent layers
134+
are selected from the chain tip according to
135+
`--split-factor=<n>` and compacted into a new incremental
136+
layer.
137+
138+
--split-factor=<n>::
139+
With `--max-chain-depth`, control how many adjacent layers
140+
are merged. Defaults to 2.
128141
--
129142
+
130143
Note that the compact command requires writing a version-2 midx that

builtin/multi-pack-index.c

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "config.h"
55
#include "environment.h"
66
#include "gettext.h"
7+
#include "parse.h"
78
#include "parse-options.h"
89
#include "midx.h"
910
#include "strbuf.h"
@@ -22,7 +23,7 @@
2223
#define BUILTIN_MIDX_COMPACT_USAGE \
2324
N_("git multi-pack-index [<options>] compact [--[no-]incremental]\n" \
2425
" [--[no-]bitmap] [--base=<checksum>] [--[no-]write-chain-file]\n" \
25-
" <from> <to>")
26+
" [--max-chain-depth=<n> [--split-factor=<n>]] [<from> <to>]")
2627

2728
#define BUILTIN_MIDX_VERIFY_USAGE \
2829
N_("git multi-pack-index [<options>] verify")
@@ -68,6 +69,8 @@ static struct opts_multi_pack_index {
6869
const char *incremental_base;
6970
char *refs_snapshot;
7071
unsigned long batch_size;
72+
unsigned long max_chain_depth;
73+
unsigned long split_factor;
7174
unsigned flags;
7275
int stdin_packs;
7376
} opts;
@@ -143,6 +146,20 @@ static void read_packs_from_stdin(struct string_list *to)
143146
strbuf_release(&buf);
144147
}
145148

149+
static int parse_positive_ulong(const struct option *opt, const char *arg,
150+
int unset)
151+
{
152+
unsigned long *value = opt->value;
153+
154+
if (unset) {
155+
*value = 0;
156+
return 0;
157+
}
158+
if (!arg || !git_parse_ulong(arg, value) || !*value)
159+
return error(_("%s must be greater than zero"), opt->long_name);
160+
return 0;
161+
}
162+
146163
static int cmd_multi_pack_index_write(int argc, const char **argv,
147164
const char *prefix,
148165
struct repository *repo)
@@ -251,6 +268,13 @@ static int cmd_multi_pack_index_compact(int argc, const char **argv,
251268
OPT_NEGBIT(0, "write-chain-file", &opts.flags,
252269
N_("write the multi-pack-index chain file"),
253270
MIDX_WRITE_NO_CHAIN),
271+
OPT_CALLBACK(0, "max-chain-depth", &opts.max_chain_depth,
272+
N_("n"),
273+
N_("automatically compact when the chain has more than this many layers"),
274+
parse_positive_ulong),
275+
OPT_CALLBACK(0, "split-factor", &opts.split_factor, N_("n"),
276+
N_("with --max-chain-depth, compact adjacent layers according to this split factor"),
277+
parse_positive_ulong),
254278
OPT_END(),
255279
};
256280

@@ -266,9 +290,18 @@ static int cmd_multi_pack_index_compact(int argc, const char **argv,
266290
options, builtin_multi_pack_index_compact_usage,
267291
0);
268292

269-
if (argc != 2)
293+
if (opts.max_chain_depth) {
294+
if (argc)
295+
usage_with_options(builtin_multi_pack_index_compact_usage,
296+
options);
297+
} else if (argc != 2) {
270298
usage_with_options(builtin_multi_pack_index_compact_usage,
271299
options);
300+
} else if (opts.split_factor) {
301+
error(_("cannot use --split-factor without --max-chain-depth"));
302+
usage_with_options(builtin_multi_pack_index_compact_usage,
303+
options);
304+
}
272305

273306
if (opts.flags & MIDX_WRITE_NO_CHAIN &&
274307
!(opts.flags & MIDX_WRITE_INCREMENTAL)) {
@@ -278,10 +311,38 @@ static int cmd_multi_pack_index_compact(int argc, const char **argv,
278311
options);
279312
}
280313

314+
if (opts.max_chain_depth) {
315+
if (opts.incremental_base) {
316+
error(_("cannot use --max-chain-depth with --base"));
317+
usage_with_options(builtin_multi_pack_index_compact_usage,
318+
options);
319+
}
320+
if (opts.flags & MIDX_WRITE_NO_CHAIN) {
321+
error(_("cannot use --max-chain-depth with --no-write-chain-file"));
322+
usage_with_options(builtin_multi_pack_index_compact_usage,
323+
options);
324+
}
325+
if (!opts.split_factor)
326+
opts.split_factor = 2;
327+
if (opts.max_chain_depth > UINT32_MAX ||
328+
opts.split_factor > UINT32_MAX) {
329+
error(_("--max-chain-depth and --split-factor must fit in 32 bits"));
330+
usage_with_options(builtin_multi_pack_index_compact_usage,
331+
options);
332+
}
333+
opts.flags |= MIDX_WRITE_INCREMENTAL;
334+
}
335+
281336
source = handle_object_dir_option(the_repository);
282337

283338
FREE_AND_NULL(options);
284339

340+
if (opts.max_chain_depth)
341+
return compact_midx_chain_auto(source,
342+
(uint32_t)opts.max_chain_depth,
343+
(uint32_t)opts.split_factor,
344+
opts.flags);
345+
285346
m = get_multi_pack_index(source);
286347

287348
for (cur = m; cur && !(from_midx && to_midx); cur = cur->base_midx) {

midx-write.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1898,6 +1898,79 @@ int write_midx_file_compact(struct odb_source *source,
18981898
return write_midx_internal(&opts);
18991899
}
19001900

1901+
int compact_midx_chain_auto(struct odb_source *source,
1902+
uint32_t max_chain_depth,
1903+
uint32_t split_factor,
1904+
unsigned flags)
1905+
{
1906+
struct repository *r = source->odb->repo;
1907+
struct multi_pack_index *m;
1908+
struct multi_pack_index *from_midx, *to_midx, *next;
1909+
uint32_t depth = 0;
1910+
uint32_t compact_objects;
1911+
1912+
if (!max_chain_depth)
1913+
return error(_("--max-chain-depth must be greater than zero"));
1914+
if (!split_factor)
1915+
return error(_("--split-factor must be greater than zero"));
1916+
1917+
trace2_region_enter("midx", "compact_midx_chain_auto", r);
1918+
1919+
odb_reprepare(r->objects);
1920+
m = get_multi_pack_index(source);
1921+
for (struct multi_pack_index *cur = m; cur; cur = cur->base_midx)
1922+
depth++;
1923+
1924+
trace2_data_intmax("midx", r, "auto:chain_depth", depth);
1925+
trace2_data_intmax("midx", r, "auto:max_chain_depth", max_chain_depth);
1926+
trace2_data_intmax("midx", r, "auto:split_factor", split_factor);
1927+
1928+
if (depth <= max_chain_depth) {
1929+
trace2_data_string("midx", r, "auto:skip", "chain-depth");
1930+
trace2_region_leave("midx", "compact_midx_chain_auto", r);
1931+
return 0;
1932+
}
1933+
if (!m) {
1934+
trace2_data_string("midx", r, "auto:skip", "no-midx");
1935+
trace2_region_leave("midx", "compact_midx_chain_auto", r);
1936+
return 0;
1937+
}
1938+
1939+
to_midx = m;
1940+
from_midx = m;
1941+
compact_objects = m->num_objects;
1942+
while ((next = from_midx->base_midx)) {
1943+
if (compact_objects < next->num_objects / split_factor)
1944+
break;
1945+
if (unsigned_add_overflows(compact_objects, next->num_objects)) {
1946+
trace2_region_leave("midx", "compact_midx_chain_auto", r);
1947+
return error(_("too many objects in automatic MIDX compaction"));
1948+
}
1949+
compact_objects += next->num_objects;
1950+
from_midx = next;
1951+
}
1952+
1953+
if (from_midx == to_midx) {
1954+
trace2_data_string("midx", r, "auto:skip", "split-factor");
1955+
trace2_region_leave("midx", "compact_midx_chain_auto", r);
1956+
return 0;
1957+
}
1958+
1959+
trace2_data_string("midx", r, "auto:from", midx_get_checksum_hex(from_midx));
1960+
trace2_data_string("midx", r, "auto:to", midx_get_checksum_hex(to_midx));
1961+
trace2_data_intmax("midx", r, "auto:objects", compact_objects);
1962+
1963+
flags |= MIDX_WRITE_INCREMENTAL;
1964+
/*
1965+
* The automatic range selector only chooses an existing contiguous
1966+
* suffix of the chain, so the compacted layer naturally uses from's
1967+
* immediate base as its base. Custom --base is for explicit callers.
1968+
*/
1969+
int ret = write_midx_file_compact(source, from_midx, to_midx, NULL, flags);
1970+
trace2_region_leave("midx", "compact_midx_chain_auto", r);
1971+
return ret;
1972+
}
1973+
19011974
int expire_midx_packs(struct odb_source *source, unsigned flags)
19021975
{
19031976
uint32_t i, *count, result = 0;

midx.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ int write_midx_file_compact(struct odb_source *source,
143143
struct multi_pack_index *to,
144144
const char *incremental_base,
145145
unsigned flags);
146+
int compact_midx_chain_auto(struct odb_source *source,
147+
uint32_t max_chain_depth,
148+
uint32_t split_factor,
149+
unsigned flags);
146150
void clear_midx_file(struct repository *r);
147151
void clear_incremental_midx_files(struct repository *r,
148152
const struct strvec *keep_hashes);

t/t5335-compact-multi-pack-index.sh

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,4 +403,61 @@ test_expect_success 'MIDX compaction with bogus --base checksum' '
403403
)
404404
'
405405

406+
test_expect_success 'automatic MIDX compaction no-ops below chain depth cap' '
407+
git init midx-compact-auto-noop &&
408+
(
409+
cd midx-compact-auto-noop &&
410+
411+
git config maintenance.auto false &&
412+
413+
write_packs A B C &&
414+
cp "$midx_chain" "$midx_chain.bak" &&
415+
416+
git multi-pack-index compact --max-chain-depth=3 &&
417+
test_cmp "$midx_chain.bak" "$midx_chain"
418+
)
419+
'
420+
421+
test_expect_success 'automatic MIDX compaction picks and compacts a range' '
422+
git init midx-compact-auto &&
423+
(
424+
cd midx-compact-auto &&
425+
426+
git config maintenance.auto false &&
427+
428+
write_packs A B C D E &&
429+
test_line_count = 5 "$midx_chain" &&
430+
431+
git multi-pack-index compact --bitmap \
432+
--max-chain-depth=3 --split-factor=2 &&
433+
test_line_count = 1 "$midx_chain" &&
434+
test_path_is_file "$midxdir/multi-pack-index-$(nth_line 1 "$midx_chain").bitmap" &&
435+
git multi-pack-index verify &&
436+
test_midx_layer_object_uniqueness
437+
)
438+
'
439+
440+
test_expect_success 'automatic MIDX compaction rejects invalid knobs' '
441+
git init midx-compact-auto-invalid &&
442+
(
443+
cd midx-compact-auto-invalid &&
444+
445+
write_packs A B &&
446+
447+
test_must_fail git multi-pack-index compact \
448+
--max-chain-depth=0 2>err &&
449+
test_grep "max-chain-depth must be greater than zero" err &&
450+
451+
test_must_fail git multi-pack-index compact \
452+
--max-chain-depth=1 --split-factor=0 2>err &&
453+
test_grep "split-factor must be greater than zero" err &&
454+
455+
test_must_fail git multi-pack-index compact \
456+
--split-factor=2 \
457+
"$(nth_line 1 "$midx_chain")" \
458+
"$(nth_line 2 "$midx_chain")" 2>err &&
459+
test_grep "cannot use --split-factor without --max-chain-depth" err
460+
)
461+
'
462+
406463
test_done

0 commit comments

Comments
 (0)