Merge branch 'kn/maintenance-is-needed'

"git maintenance" command learned "is-needed" subcommand to tell if
it is necessary to perform various maintenance tasks.

* kn/maintenance-is-needed:
  maintenance: add 'is-needed' subcommand
  maintenance: add checking logic in `pack_refs_condition()`
  refs: add a `optimize_required` field to `struct ref_storage_be`
  reftable/stack: add function to check if optimization is required
  reftable/stack: return stack segments directly
main
Junio C Hamano 2025-11-21 09:14:15 -08:00
commit c62d2d3810
14 changed files with 285 additions and 44 deletions

View File

@ -12,6 +12,7 @@ SYNOPSIS
'git maintenance' run [<options>]
'git maintenance' start [--scheduler=<scheduler>]
'git maintenance' (stop|register|unregister) [<options>]
'git maintenance' is-needed [<options>]


DESCRIPTION
@ -84,6 +85,16 @@ The `unregister` subcommand will report an error if the current repository
is not already registered. Use the `--force` option to return success even
when the current repository is not registered.

is-needed::
Check whether maintenance needs to be run without actually running it.
Exits with a 0 status code if maintenance needs to be run, 1 otherwise.
Ideally used with the '--auto' flag.
+
If one or more `--task` options are specified, then those tasks are checked
in that order. Otherwise, the tasks are determined by which
`maintenance.<task>.enabled` config options are true. By default, only
`maintenance.gc.enabled` is true.

TASKS
-----

@ -183,6 +194,8 @@ OPTIONS
in the `gc.auto` config setting, or when the number of pack-files
exceeds the `gc.autoPackLimit` config setting. Not compatible with
the `--schedule` option.
When combined with the `is-needed` subcommand, check if the required
thresholds are met without actually running maintenance.

--schedule::
When combined with the `run` subcommand, run maintenance tasks

View File

@ -36,6 +36,7 @@
#include "reflog.h"
#include "repack.h"
#include "rerere.h"
#include "revision.h"
#include "blob.h"
#include "tree.h"
#include "promisor-remote.h"
@ -286,12 +287,26 @@ static void maintenance_run_opts_release(struct maintenance_run_opts *opts)

static int pack_refs_condition(UNUSED struct gc_config *cfg)
{
/*
* The auto-repacking logic for refs is handled by the ref backends and
* exposed via `git pack-refs --auto`. We thus always return truish
* here and let the backend decide for us.
*/
return 1;
struct string_list included_refs = STRING_LIST_INIT_NODUP;
struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
struct refs_optimize_opts optimize_opts = {
.exclusions = &excludes,
.includes = &included_refs,
.flags = REFS_OPTIMIZE_PRUNE | REFS_OPTIMIZE_AUTO,
};
bool required;

/* Check for all refs, similar to 'git refs optimize --all'. */
string_list_append(optimize_opts.includes, "*");

if (refs_optimize_required(get_main_ref_store(the_repository),
&optimize_opts, &required))
return 0;

clear_ref_exclusions(&excludes);
string_list_clear(&included_refs, 0);

return required;
}

static int maintenance_task_pack_refs(struct maintenance_run_opts *opts,
@ -1095,9 +1110,6 @@ static int maintenance_opt_schedule(const struct option *opt, const char *arg,
return 0;
}

/* Remember to update object flag allocation in object.h */
#define SEEN (1u<<0)

struct cg_auto_data {
int num_not_in_graph;
int limit;
@ -3444,7 +3456,67 @@ static int maintenance_stop(int argc, const char **argv, const char *prefix,
return update_background_schedule(NULL, 0);
}

static const char * const builtin_maintenance_usage[] = {
static const char *const builtin_maintenance_is_needed_usage[] = {
"git maintenance is-needed [--task=<task>] [--schedule]",
NULL
};

static int maintenance_is_needed(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
{
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
struct string_list selected_tasks = STRING_LIST_INIT_DUP;
struct gc_config cfg = GC_CONFIG_INIT;
struct option options[] = {
OPT_BOOL(0, "auto", &opts.auto_flag,
N_("run tasks based on the state of the repository")),
OPT_CALLBACK_F(0, "task", &selected_tasks, N_("task"),
N_("check a specific task"),
PARSE_OPT_NONEG, task_option_parse),
OPT_END()
};
bool is_needed = false;

argc = parse_options(argc, argv, prefix, options,
builtin_maintenance_is_needed_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc)
usage_with_options(builtin_maintenance_is_needed_usage, options);

gc_config(&cfg);
initialize_task_config(&opts, &selected_tasks);

if (opts.auto_flag) {
for (size_t i = 0; i < opts.tasks_nr; i++) {
if (tasks[opts.tasks[i]].auto_condition &&
tasks[opts.tasks[i]].auto_condition(&cfg)) {
is_needed = true;
break;
}
}
} else {
/*
* When not using --auto we always require maintenance right now.
*
* TODO: this certainly is too eager, as some maintenance tasks may
* decide to not do anything because the data structures are already
* fully optimized. We may eventually want to extend the auto
* condition to also cover non-auto runs so that we can detect such
* cases.
*/
is_needed = true;
}

string_list_clear(&selected_tasks, 0);
maintenance_run_opts_release(&opts);
gc_config_release(&cfg);

if (is_needed)
return 0;
return 1;
}

static const char *const builtin_maintenance_usage[] = {
N_("git maintenance <subcommand> [<options>]"),
NULL,
};
@ -3461,6 +3533,7 @@ int cmd_maintenance(int argc,
OPT_SUBCOMMAND("stop", &fn, maintenance_stop),
OPT_SUBCOMMAND("register", &fn, maintenance_register),
OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister),
OPT_SUBCOMMAND("is-needed", &fn, maintenance_is_needed),
OPT_END(),
};


View File

@ -80,7 +80,6 @@ void object_array_init(struct object_array *array);
* list-objects-filter.c: 21
* bloom.c: 2122
* builtin/fsck.c: 0--3
* builtin/gc.c: 0
* builtin/index-pack.c: 2021
* reflog.c: 10--12
* builtin/show-branch.c: 0-------------------------------------------26

7
refs.c
View File

@ -2318,6 +2318,13 @@ int refs_optimize(struct ref_store *refs, struct refs_optimize_opts *opts)
return refs->be->optimize(refs, opts);
}

int refs_optimize_required(struct ref_store *refs,
struct refs_optimize_opts *opts,
bool *required)
{
return refs->be->optimize_required(refs, opts, required);
}

int reference_get_peeled_oid(struct repository *repo,
const struct reference *ref,
struct object_id *peeled_oid)

7
refs.h
View File

@ -520,6 +520,13 @@ struct refs_optimize_opts {
*/
int refs_optimize(struct ref_store *refs, struct refs_optimize_opts *opts);

/*
* Check if refs backend can be optimized by calling 'refs_optimize'.
*/
int refs_optimize_required(struct ref_store *ref_store,
struct refs_optimize_opts *opts,
bool *required);

/*
* Setup reflog before using. Fill in err and return -1 on failure.
*/

View File

@ -132,6 +132,17 @@ static int debug_optimize(struct ref_store *ref_store, struct refs_optimize_opts
return res;
}

static int debug_optimize_required(struct ref_store *ref_store,
struct refs_optimize_opts *opts,
bool *required)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
int res = drefs->refs->be->optimize_required(drefs->refs, opts, required);
trace_printf_key(&trace_refs, "optimize_required: %s, res: %d\n",
required ? "yes" : "no", res);
return res;
}

static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
const char *newref, const char *logmsg)
{
@ -440,6 +451,8 @@ struct ref_storage_be refs_be_debug = {
.transaction_abort = debug_transaction_abort,

.optimize = debug_optimize,
.optimize_required = debug_optimize_required,

.rename_ref = debug_rename_ref,
.copy_ref = debug_copy_ref,


View File

@ -1512,6 +1512,16 @@ static int files_optimize(struct ref_store *ref_store,
return 0;
}

static int files_optimize_required(struct ref_store *ref_store,
struct refs_optimize_opts *opts,
bool *required)
{
struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_READ,
"optimize_required");
*required = should_pack_refs(refs, opts);
return 0;
}

/*
* People using contrib's git-new-workdir have .git/logs/refs ->
* /some/other/path/.git/logs/refs, and that may live on another device.
@ -3982,6 +3992,7 @@ struct ref_storage_be refs_be_files = {
.transaction_abort = files_transaction_abort,

.optimize = files_optimize,
.optimize_required = files_optimize_required,
.rename_ref = files_rename_ref,
.copy_ref = files_copy_ref,


View File

@ -1784,6 +1784,17 @@ static int packed_optimize(struct ref_store *ref_store UNUSED,
return 0;
}

static int packed_optimize_required(struct ref_store *ref_store UNUSED,
struct refs_optimize_opts *opts UNUSED,
bool *required)
{
/*
* Packed refs are already optimized.
*/
*required = false;
return 0;
}

static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store UNUSED)
{
return empty_ref_iterator_begin();
@ -2130,6 +2141,8 @@ struct ref_storage_be refs_be_packed = {
.transaction_abort = packed_transaction_abort,

.optimize = packed_optimize,
.optimize_required = packed_optimize_required,

.rename_ref = NULL,
.copy_ref = NULL,


View File

@ -424,6 +424,11 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,

typedef int optimize_fn(struct ref_store *ref_store,
struct refs_optimize_opts *opts);

typedef int optimize_required_fn(struct ref_store *ref_store,
struct refs_optimize_opts *opts,
bool *required);

typedef int rename_ref_fn(struct ref_store *ref_store,
const char *oldref, const char *newref,
const char *logmsg);
@ -549,6 +554,7 @@ struct ref_storage_be {
ref_transaction_abort_fn *transaction_abort;

optimize_fn *optimize;
optimize_required_fn *optimize_required;
rename_ref_fn *rename_ref;
copy_ref_fn *copy_ref;


View File

@ -1733,6 +1733,29 @@ out:
return ret;
}

static int reftable_be_optimize_required(struct ref_store *ref_store,
struct refs_optimize_opts *opts,
bool *required)
{
struct reftable_ref_store *refs = reftable_be_downcast(ref_store, REF_STORE_READ,
"optimize_refs_required");
struct reftable_stack *stack;
bool use_heuristics = false;

if (refs->err)
return refs->err;

stack = refs->worktree_backend.stack;
if (!stack)
stack = refs->main_backend.stack;

if (opts->flags & REFS_OPTIMIZE_AUTO)
use_heuristics = true;

return reftable_stack_compaction_required(stack, use_heuristics,
required);
}

struct write_create_symref_arg {
struct reftable_ref_store *refs;
struct reftable_stack *stack;
@ -2756,6 +2779,8 @@ struct ref_storage_be refs_be_reftable = {
.transaction_abort = reftable_be_transaction_abort,

.optimize = reftable_be_optimize,
.optimize_required = reftable_be_optimize_required,

.rename_ref = reftable_be_rename_ref,
.copy_ref = reftable_be_copy_ref,


View File

@ -123,6 +123,17 @@ struct reftable_log_expiry_config {
int reftable_stack_compact_all(struct reftable_stack *st,
struct reftable_log_expiry_config *config);

/*
* Check if compaction is required.
*
* When `use_heuristics` is false, check if all tables can be compacted to a
* single table. If true, use heuristics to determine if the tables need to be
* compacted to maintain geometric progression.
*/
int reftable_stack_compaction_required(struct reftable_stack *st,
bool use_heuristics,
bool *required);

/* heuristically compact unbalanced table stack. */
int reftable_stack_auto_compact(struct reftable_stack *st);


View File

@ -1626,7 +1626,8 @@ struct segment suggest_compaction_segment(uint64_t *sizes, size_t n,
return seg;
}

static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)
static int stack_segments_for_compaction(struct reftable_stack *st,
struct segment *seg)
{
int version = (st->opts.hash_id == REFTABLE_HASH_SHA1) ? 1 : 2;
int overhead = header_size(version) - 1;
@ -1634,31 +1635,63 @@ static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)

REFTABLE_CALLOC_ARRAY(sizes, st->merged->tables_len);
if (!sizes)
return NULL;
return REFTABLE_OUT_OF_MEMORY_ERROR;

for (size_t i = 0; i < st->merged->tables_len; i++)
sizes[i] = st->tables[i]->size - overhead;

return sizes;
*seg = suggest_compaction_segment(sizes, st->merged->tables_len,
st->opts.auto_compaction_factor);
reftable_free(sizes);

return 0;
}

static int update_segment_if_compaction_required(struct reftable_stack *st,
struct segment *seg,
bool use_geometric,
bool *required)
{
int err;

if (st->merged->tables_len < 2) {
*required = false;
return 0;
}

if (!use_geometric) {
*required = true;
return 0;
}

err = stack_segments_for_compaction(st, seg);
if (err)
return err;

*required = segment_size(seg) > 0;
return 0;
}

int reftable_stack_compaction_required(struct reftable_stack *st,
bool use_heuristics,
bool *required)
{
struct segment seg;
return update_segment_if_compaction_required(st, &seg, use_heuristics,
required);
}

int reftable_stack_auto_compact(struct reftable_stack *st)
{
struct segment seg;
uint64_t *sizes;
bool required;
int err;

if (st->merged->tables_len < 2)
return 0;
err = update_segment_if_compaction_required(st, &seg, true, &required);
if (err)
return err;

sizes = stack_table_sizes_for_compaction(st);
if (!sizes)
return REFTABLE_OUT_OF_MEMORY_ERROR;

seg = suggest_compaction_segment(sizes, st->merged->tables_len,
st->opts.auto_compaction_factor);
reftable_free(sizes);

if (segment_size(&seg) > 0)
if (required)
return stack_compact_range(st, seg.start, seg.end - 1,
NULL, STACK_COMPACT_RANGE_BEST_EFFORT);


View File

@ -49,7 +49,9 @@ test_expect_success 'run [--auto|--quiet]' '
git maintenance run --auto 2>/dev/null &&
GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \
git maintenance run --no-quiet 2>/dev/null &&
git maintenance is-needed &&
test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-no-auto.txt &&
! git maintenance is-needed --auto &&
test_subcommand ! git gc --auto --quiet --no-detach --skip-foreground-tasks <run-auto.txt &&
test_subcommand git gc --no-quiet --no-detach --skip-foreground-tasks <run-no-quiet.txt
'
@ -180,6 +182,11 @@ test_expect_success 'commit-graph auto condition' '

test_commit first &&

! git -c maintenance.commit-graph.auto=0 \
maintenance is-needed --auto --task=commit-graph &&
git -c maintenance.commit-graph.auto=1 \
maintenance is-needed --auto --task=commit-graph &&

GIT_TRACE2_EVENT="$(pwd)/cg-zero-means-no.txt" \
git -c maintenance.commit-graph.auto=0 $COMMAND &&
GIT_TRACE2_EVENT="$(pwd)/cg-one-satisfied.txt" \
@ -290,16 +297,23 @@ test_expect_success 'maintenance.loose-objects.auto' '
git -c maintenance.loose-objects.auto=1 maintenance \
run --auto --task=loose-objects 2>/dev/null &&
test_subcommand ! git prune-packed --quiet <trace-lo1.txt &&

printf data-A | git hash-object -t blob --stdin -w &&
! git -c maintenance.loose-objects.auto=2 \
maintenance is-needed --auto --task=loose-objects &&
GIT_TRACE2_EVENT="$(pwd)/trace-loA" \
git -c maintenance.loose-objects.auto=2 \
maintenance run --auto --task=loose-objects 2>/dev/null &&
test_subcommand ! git prune-packed --quiet <trace-loA &&

printf data-B | git hash-object -t blob --stdin -w &&
git -c maintenance.loose-objects.auto=2 \
maintenance is-needed --auto --task=loose-objects &&
GIT_TRACE2_EVENT="$(pwd)/trace-loB" \
git -c maintenance.loose-objects.auto=2 \
maintenance run --auto --task=loose-objects 2>/dev/null &&
test_subcommand git prune-packed --quiet <trace-loB &&

GIT_TRACE2_EVENT="$(pwd)/trace-loC" \
git -c maintenance.loose-objects.auto=2 \
maintenance run --auto --task=loose-objects 2>/dev/null &&
@ -421,10 +435,13 @@ run_incremental_repack_and_verify () {
test_commit A &&
git repack -adk &&
git multi-pack-index write &&
! git -c maintenance.incremental-repack.auto=1 \
maintenance is-needed --auto --task=incremental-repack &&
GIT_TRACE2_EVENT="$(pwd)/midx-init.txt" git \
-c maintenance.incremental-repack.auto=1 \
maintenance run --auto --task=incremental-repack 2>/dev/null &&
test_subcommand ! git multi-pack-index write --no-progress <midx-init.txt &&

test_commit B &&
git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
HEAD
@ -434,11 +451,14 @@ run_incremental_repack_and_verify () {
-c maintenance.incremental-repack.auto=2 \
maintenance run --auto --task=incremental-repack 2>/dev/null &&
test_subcommand ! git multi-pack-index write --no-progress <trace-A &&

test_commit C &&
git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
HEAD
^HEAD~1
EOF
git -c maintenance.incremental-repack.auto=2 \
maintenance is-needed --auto --task=incremental-repack &&
GIT_TRACE2_EVENT=$(pwd)/trace-B git \
-c maintenance.incremental-repack.auto=2 \
maintenance run --auto --task=incremental-repack 2>/dev/null &&
@ -655,9 +675,15 @@ test_expect_success 'reflog-expire task --auto only packs when exceeding limits'
git reflog expire --all --expire=now &&
test_commit reflog-one &&
test_commit reflog-two &&

! git -c maintenance.reflog-expire.auto=3 \
maintenance is-needed --auto --task=reflog-expire &&
GIT_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \
git -c maintenance.reflog-expire.auto=3 maintenance run --auto --task=reflog-expire &&
test_subcommand ! git reflog expire --all <reflog-expire-auto.txt &&

git -c maintenance.reflog-expire.auto=2 \
maintenance is-needed --auto --task=reflog-expire &&
GIT_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \
git -c maintenance.reflog-expire.auto=2 maintenance run --auto --task=reflog-expire &&
test_subcommand git reflog expire --all <reflog-expire-auto.txt
@ -684,6 +710,7 @@ test_expect_success 'worktree-prune task --auto only prunes with prunable worktr
test_expect_worktree_prune ! git maintenance run --auto --task=worktree-prune &&
mkdir .git/worktrees &&
: >.git/worktrees/abc &&
git maintenance is-needed --auto --task=worktree-prune &&
test_expect_worktree_prune git maintenance run --auto --task=worktree-prune
'

@ -700,22 +727,7 @@ test_expect_success 'worktree-prune task with --auto honors maintenance.worktree
test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=0 maintenance run --auto --task=worktree-prune &&
# A positive value should require at least this many prunable worktrees.
test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=4 maintenance run --auto --task=worktree-prune &&
test_expect_worktree_prune git -c maintenance.worktree-prune.auto=3 maintenance run --auto --task=worktree-prune
'

test_expect_success 'worktree-prune task with --auto honors maintenance.worktree-prune.auto' '
# A negative value should always prune.
test_expect_worktree_prune git -c maintenance.worktree-prune.auto=-1 maintenance run --auto --task=worktree-prune &&

mkdir .git/worktrees &&
: >.git/worktrees/first &&
: >.git/worktrees/second &&
: >.git/worktrees/third &&

# Zero should never prune.
test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=0 maintenance run --auto --task=worktree-prune &&
# A positive value should require at least this many prunable worktrees.
test_expect_worktree_prune ! git -c maintenance.worktree-prune.auto=4 maintenance run --auto --task=worktree-prune &&
git -c maintenance.worktree-prune.auto=3 maintenance is-needed --auto --task=worktree-prune &&
test_expect_worktree_prune git -c maintenance.worktree-prune.auto=3 maintenance run --auto --task=worktree-prune
'

@ -724,11 +736,13 @@ test_expect_success 'worktree-prune task honors gc.worktreePruneExpire' '
rm -rf worktree &&

rm -f worktree-prune.txt &&
! git -c gc.worktreePruneExpire=1.week.ago maintenance is-needed --auto --task=worktree-prune &&
GIT_TRACE2_EVENT="$(pwd)/worktree-prune.txt" git -c gc.worktreePruneExpire=1.week.ago maintenance run --auto --task=worktree-prune &&
test_subcommand ! git worktree prune --expire 1.week.ago <worktree-prune.txt &&
test_path_is_dir .git/worktrees/worktree &&

rm -f worktree-prune.txt &&
git -c gc.worktreePruneExpire=now maintenance is-needed --auto --task=worktree-prune &&
GIT_TRACE2_EVENT="$(pwd)/worktree-prune.txt" git -c gc.worktreePruneExpire=now maintenance run --auto --task=worktree-prune &&
test_subcommand git worktree prune --expire now <worktree-prune.txt &&
test_path_is_missing .git/worktrees/worktree
@ -753,10 +767,13 @@ test_expect_success 'rerere-gc task without --auto always collects garbage' '

test_expect_success 'rerere-gc task with --auto only prunes with prunable entries' '
test_when_finished "rm -rf .git/rr-cache" &&
! git maintenance is-needed --auto --task=rerere-gc &&
test_expect_rerere_gc ! git maintenance run --auto --task=rerere-gc &&
mkdir .git/rr-cache &&
! git maintenance is-needed --auto --task=rerere-gc &&
test_expect_rerere_gc ! git maintenance run --auto --task=rerere-gc &&
: >.git/rr-cache/entry &&
git maintenance is-needed --auto --task=rerere-gc &&
test_expect_rerere_gc git maintenance run --auto --task=rerere-gc
'

@ -764,17 +781,22 @@ test_expect_success 'rerere-gc task with --auto honors maintenance.rerere-gc.aut
test_when_finished "rm -rf .git/rr-cache" &&

# A negative value should always prune.
git -c maintenance.rerere-gc.auto=-1 maintenance is-needed --auto --task=rerere-gc &&
test_expect_rerere_gc git -c maintenance.rerere-gc.auto=-1 maintenance run --auto --task=rerere-gc &&

# A positive value prunes when there is at least one entry.
! git -c maintenance.rerere-gc.auto=9000 maintenance is-needed --auto --task=rerere-gc &&
test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc &&
mkdir .git/rr-cache &&
! git -c maintenance.rerere-gc.auto=9000 maintenance is-needed --auto --task=rerere-gc &&
test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc &&
: >.git/rr-cache/entry-1 &&
git -c maintenance.rerere-gc.auto=9000 maintenance is-needed --auto --task=rerere-gc &&
test_expect_rerere_gc git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc &&

# Zero should never prune.
: >.git/rr-cache/entry-1 &&
! git -c maintenance.rerere-gc.auto=0 maintenance is-needed --auto --task=rerere-gc &&
test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=0 maintenance run --auto --task=rerere-gc
'


View File

@ -1067,6 +1067,7 @@ void test_reftable_stack__add_performs_auto_compaction(void)
.value_type = REFTABLE_REF_SYMREF,
.value.symref = (char *) "master",
};
bool required = false;
char buf[128];

/*
@ -1087,10 +1088,17 @@ void test_reftable_stack__add_performs_auto_compaction(void)
* auto compaction is disabled. When enabled, we should merge
* all tables in the stack.
*/
if (i != n)
cl_assert_equal_i(reftable_stack_compaction_required(st, true, &required), 0);
if (i != n) {
cl_assert_equal_i(st->merged->tables_len, i + 1);
else
if (i < 1)
cl_assert_equal_b(required, false);
else
cl_assert_equal_b(required, true);
} else {
cl_assert_equal_i(st->merged->tables_len, 1);
cl_assert_equal_b(required, false);
}
}

reftable_stack_destroy(st);