mirror of
https://github.com/git/git.git
synced 2025-12-12 20:36:24 +01:00
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
This commit is contained in:
@@ -12,6 +12,7 @@ SYNOPSIS
|
|||||||
'git maintenance' run [<options>]
|
'git maintenance' run [<options>]
|
||||||
'git maintenance' start [--scheduler=<scheduler>]
|
'git maintenance' start [--scheduler=<scheduler>]
|
||||||
'git maintenance' (stop|register|unregister) [<options>]
|
'git maintenance' (stop|register|unregister) [<options>]
|
||||||
|
'git maintenance' is-needed [<options>]
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTION
|
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
|
is not already registered. Use the `--force` option to return success even
|
||||||
when the current repository is not registered.
|
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
|
TASKS
|
||||||
-----
|
-----
|
||||||
|
|
||||||
@@ -183,6 +194,8 @@ OPTIONS
|
|||||||
in the `gc.auto` config setting, or when the number of pack-files
|
in the `gc.auto` config setting, or when the number of pack-files
|
||||||
exceeds the `gc.autoPackLimit` config setting. Not compatible with
|
exceeds the `gc.autoPackLimit` config setting. Not compatible with
|
||||||
the `--schedule` option.
|
the `--schedule` option.
|
||||||
|
When combined with the `is-needed` subcommand, check if the required
|
||||||
|
thresholds are met without actually running maintenance.
|
||||||
|
|
||||||
--schedule::
|
--schedule::
|
||||||
When combined with the `run` subcommand, run maintenance tasks
|
When combined with the `run` subcommand, run maintenance tasks
|
||||||
|
|||||||
91
builtin/gc.c
91
builtin/gc.c
@@ -36,6 +36,7 @@
|
|||||||
#include "reflog.h"
|
#include "reflog.h"
|
||||||
#include "repack.h"
|
#include "repack.h"
|
||||||
#include "rerere.h"
|
#include "rerere.h"
|
||||||
|
#include "revision.h"
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "promisor-remote.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)
|
static int pack_refs_condition(UNUSED struct gc_config *cfg)
|
||||||
{
|
{
|
||||||
/*
|
struct string_list included_refs = STRING_LIST_INIT_NODUP;
|
||||||
* The auto-repacking logic for refs is handled by the ref backends and
|
struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
|
||||||
* exposed via `git pack-refs --auto`. We thus always return truish
|
struct refs_optimize_opts optimize_opts = {
|
||||||
* here and let the backend decide for us.
|
.exclusions = &excludes,
|
||||||
*/
|
.includes = &included_refs,
|
||||||
return 1;
|
.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,
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remember to update object flag allocation in object.h */
|
|
||||||
#define SEEN (1u<<0)
|
|
||||||
|
|
||||||
struct cg_auto_data {
|
struct cg_auto_data {
|
||||||
int num_not_in_graph;
|
int num_not_in_graph;
|
||||||
int limit;
|
int limit;
|
||||||
@@ -3444,6 +3456,66 @@ static int maintenance_stop(int argc, const char **argv, const char *prefix,
|
|||||||
return update_background_schedule(NULL, 0);
|
return update_background_schedule(NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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[] = {
|
static const char *const builtin_maintenance_usage[] = {
|
||||||
N_("git maintenance <subcommand> [<options>]"),
|
N_("git maintenance <subcommand> [<options>]"),
|
||||||
NULL,
|
NULL,
|
||||||
@@ -3461,6 +3533,7 @@ int cmd_maintenance(int argc,
|
|||||||
OPT_SUBCOMMAND("stop", &fn, maintenance_stop),
|
OPT_SUBCOMMAND("stop", &fn, maintenance_stop),
|
||||||
OPT_SUBCOMMAND("register", &fn, maintenance_register),
|
OPT_SUBCOMMAND("register", &fn, maintenance_register),
|
||||||
OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister),
|
OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister),
|
||||||
|
OPT_SUBCOMMAND("is-needed", &fn, maintenance_is_needed),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
1
object.h
1
object.h
@@ -80,7 +80,6 @@ void object_array_init(struct object_array *array);
|
|||||||
* list-objects-filter.c: 21
|
* list-objects-filter.c: 21
|
||||||
* bloom.c: 2122
|
* bloom.c: 2122
|
||||||
* builtin/fsck.c: 0--3
|
* builtin/fsck.c: 0--3
|
||||||
* builtin/gc.c: 0
|
|
||||||
* builtin/index-pack.c: 2021
|
* builtin/index-pack.c: 2021
|
||||||
* reflog.c: 10--12
|
* reflog.c: 10--12
|
||||||
* builtin/show-branch.c: 0-------------------------------------------26
|
* builtin/show-branch.c: 0-------------------------------------------26
|
||||||
|
|||||||
7
refs.c
7
refs.c
@@ -2318,6 +2318,13 @@ int refs_optimize(struct ref_store *refs, struct refs_optimize_opts *opts)
|
|||||||
return refs->be->optimize(refs, 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,
|
int reference_get_peeled_oid(struct repository *repo,
|
||||||
const struct reference *ref,
|
const struct reference *ref,
|
||||||
struct object_id *peeled_oid)
|
struct object_id *peeled_oid)
|
||||||
|
|||||||
7
refs.h
7
refs.h
@@ -520,6 +520,13 @@ struct refs_optimize_opts {
|
|||||||
*/
|
*/
|
||||||
int refs_optimize(struct ref_store *refs, struct refs_optimize_opts *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.
|
* Setup reflog before using. Fill in err and return -1 on failure.
|
||||||
*/
|
*/
|
||||||
|
|||||||
13
refs/debug.c
13
refs/debug.c
@@ -132,6 +132,17 @@ static int debug_optimize(struct ref_store *ref_store, struct refs_optimize_opts
|
|||||||
return res;
|
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,
|
static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
|
||||||
const char *newref, const char *logmsg)
|
const char *newref, const char *logmsg)
|
||||||
{
|
{
|
||||||
@@ -440,6 +451,8 @@ struct ref_storage_be refs_be_debug = {
|
|||||||
.transaction_abort = debug_transaction_abort,
|
.transaction_abort = debug_transaction_abort,
|
||||||
|
|
||||||
.optimize = debug_optimize,
|
.optimize = debug_optimize,
|
||||||
|
.optimize_required = debug_optimize_required,
|
||||||
|
|
||||||
.rename_ref = debug_rename_ref,
|
.rename_ref = debug_rename_ref,
|
||||||
.copy_ref = debug_copy_ref,
|
.copy_ref = debug_copy_ref,
|
||||||
|
|
||||||
|
|||||||
@@ -1512,6 +1512,16 @@ static int files_optimize(struct ref_store *ref_store,
|
|||||||
return 0;
|
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 ->
|
* People using contrib's git-new-workdir have .git/logs/refs ->
|
||||||
* /some/other/path/.git/logs/refs, and that may live on another device.
|
* /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,
|
.transaction_abort = files_transaction_abort,
|
||||||
|
|
||||||
.optimize = files_optimize,
|
.optimize = files_optimize,
|
||||||
|
.optimize_required = files_optimize_required,
|
||||||
.rename_ref = files_rename_ref,
|
.rename_ref = files_rename_ref,
|
||||||
.copy_ref = files_copy_ref,
|
.copy_ref = files_copy_ref,
|
||||||
|
|
||||||
|
|||||||
@@ -1784,6 +1784,17 @@ static int packed_optimize(struct ref_store *ref_store UNUSED,
|
|||||||
return 0;
|
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)
|
static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store UNUSED)
|
||||||
{
|
{
|
||||||
return empty_ref_iterator_begin();
|
return empty_ref_iterator_begin();
|
||||||
@@ -2130,6 +2141,8 @@ struct ref_storage_be refs_be_packed = {
|
|||||||
.transaction_abort = packed_transaction_abort,
|
.transaction_abort = packed_transaction_abort,
|
||||||
|
|
||||||
.optimize = packed_optimize,
|
.optimize = packed_optimize,
|
||||||
|
.optimize_required = packed_optimize_required,
|
||||||
|
|
||||||
.rename_ref = NULL,
|
.rename_ref = NULL,
|
||||||
.copy_ref = NULL,
|
.copy_ref = NULL,
|
||||||
|
|
||||||
|
|||||||
@@ -424,6 +424,11 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
|
|||||||
|
|
||||||
typedef int optimize_fn(struct ref_store *ref_store,
|
typedef int optimize_fn(struct ref_store *ref_store,
|
||||||
struct refs_optimize_opts *opts);
|
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,
|
typedef int rename_ref_fn(struct ref_store *ref_store,
|
||||||
const char *oldref, const char *newref,
|
const char *oldref, const char *newref,
|
||||||
const char *logmsg);
|
const char *logmsg);
|
||||||
@@ -549,6 +554,7 @@ struct ref_storage_be {
|
|||||||
ref_transaction_abort_fn *transaction_abort;
|
ref_transaction_abort_fn *transaction_abort;
|
||||||
|
|
||||||
optimize_fn *optimize;
|
optimize_fn *optimize;
|
||||||
|
optimize_required_fn *optimize_required;
|
||||||
rename_ref_fn *rename_ref;
|
rename_ref_fn *rename_ref;
|
||||||
copy_ref_fn *copy_ref;
|
copy_ref_fn *copy_ref;
|
||||||
|
|
||||||
|
|||||||
@@ -1733,6 +1733,29 @@ out:
|
|||||||
return ret;
|
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 write_create_symref_arg {
|
||||||
struct reftable_ref_store *refs;
|
struct reftable_ref_store *refs;
|
||||||
struct reftable_stack *stack;
|
struct reftable_stack *stack;
|
||||||
@@ -2756,6 +2779,8 @@ struct ref_storage_be refs_be_reftable = {
|
|||||||
.transaction_abort = reftable_be_transaction_abort,
|
.transaction_abort = reftable_be_transaction_abort,
|
||||||
|
|
||||||
.optimize = reftable_be_optimize,
|
.optimize = reftable_be_optimize,
|
||||||
|
.optimize_required = reftable_be_optimize_required,
|
||||||
|
|
||||||
.rename_ref = reftable_be_rename_ref,
|
.rename_ref = reftable_be_rename_ref,
|
||||||
.copy_ref = reftable_be_copy_ref,
|
.copy_ref = reftable_be_copy_ref,
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,17 @@ struct reftable_log_expiry_config {
|
|||||||
int reftable_stack_compact_all(struct reftable_stack *st,
|
int reftable_stack_compact_all(struct reftable_stack *st,
|
||||||
struct reftable_log_expiry_config *config);
|
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. */
|
/* heuristically compact unbalanced table stack. */
|
||||||
int reftable_stack_auto_compact(struct reftable_stack *st);
|
int reftable_stack_auto_compact(struct reftable_stack *st);
|
||||||
|
|
||||||
|
|||||||
@@ -1626,7 +1626,8 @@ struct segment suggest_compaction_segment(uint64_t *sizes, size_t n,
|
|||||||
return seg;
|
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 version = (st->opts.hash_id == REFTABLE_HASH_SHA1) ? 1 : 2;
|
||||||
int overhead = header_size(version) - 1;
|
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);
|
REFTABLE_CALLOC_ARRAY(sizes, st->merged->tables_len);
|
||||||
if (!sizes)
|
if (!sizes)
|
||||||
return NULL;
|
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
|
|
||||||
for (size_t i = 0; i < st->merged->tables_len; i++)
|
for (size_t i = 0; i < st->merged->tables_len; i++)
|
||||||
sizes[i] = st->tables[i]->size - overhead;
|
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)
|
int reftable_stack_auto_compact(struct reftable_stack *st)
|
||||||
{
|
{
|
||||||
struct segment seg;
|
struct segment seg;
|
||||||
uint64_t *sizes;
|
bool required;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (st->merged->tables_len < 2)
|
err = update_segment_if_compaction_required(st, &seg, true, &required);
|
||||||
return 0;
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
sizes = stack_table_sizes_for_compaction(st);
|
if (required)
|
||||||
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)
|
|
||||||
return stack_compact_range(st, seg.start, seg.end - 1,
|
return stack_compact_range(st, seg.start, seg.end - 1,
|
||||||
NULL, STACK_COMPACT_RANGE_BEST_EFFORT);
|
NULL, STACK_COMPACT_RANGE_BEST_EFFORT);
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ test_expect_success 'run [--auto|--quiet]' '
|
|||||||
git maintenance run --auto 2>/dev/null &&
|
git maintenance run --auto 2>/dev/null &&
|
||||||
GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \
|
GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \
|
||||||
git maintenance run --no-quiet 2>/dev/null &&
|
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 &&
|
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 --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
|
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 &&
|
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_TRACE2_EVENT="$(pwd)/cg-zero-means-no.txt" \
|
||||||
git -c maintenance.commit-graph.auto=0 $COMMAND &&
|
git -c maintenance.commit-graph.auto=0 $COMMAND &&
|
||||||
GIT_TRACE2_EVENT="$(pwd)/cg-one-satisfied.txt" \
|
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 \
|
git -c maintenance.loose-objects.auto=1 maintenance \
|
||||||
run --auto --task=loose-objects 2>/dev/null &&
|
run --auto --task=loose-objects 2>/dev/null &&
|
||||||
test_subcommand ! git prune-packed --quiet <trace-lo1.txt &&
|
test_subcommand ! git prune-packed --quiet <trace-lo1.txt &&
|
||||||
|
|
||||||
printf data-A | git hash-object -t blob --stdin -w &&
|
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_TRACE2_EVENT="$(pwd)/trace-loA" \
|
||||||
git -c maintenance.loose-objects.auto=2 \
|
git -c maintenance.loose-objects.auto=2 \
|
||||||
maintenance run --auto --task=loose-objects 2>/dev/null &&
|
maintenance run --auto --task=loose-objects 2>/dev/null &&
|
||||||
test_subcommand ! git prune-packed --quiet <trace-loA &&
|
test_subcommand ! git prune-packed --quiet <trace-loA &&
|
||||||
|
|
||||||
printf data-B | git hash-object -t blob --stdin -w &&
|
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_TRACE2_EVENT="$(pwd)/trace-loB" \
|
||||||
git -c maintenance.loose-objects.auto=2 \
|
git -c maintenance.loose-objects.auto=2 \
|
||||||
maintenance run --auto --task=loose-objects 2>/dev/null &&
|
maintenance run --auto --task=loose-objects 2>/dev/null &&
|
||||||
test_subcommand git prune-packed --quiet <trace-loB &&
|
test_subcommand git prune-packed --quiet <trace-loB &&
|
||||||
|
|
||||||
GIT_TRACE2_EVENT="$(pwd)/trace-loC" \
|
GIT_TRACE2_EVENT="$(pwd)/trace-loC" \
|
||||||
git -c maintenance.loose-objects.auto=2 \
|
git -c maintenance.loose-objects.auto=2 \
|
||||||
maintenance run --auto --task=loose-objects 2>/dev/null &&
|
maintenance run --auto --task=loose-objects 2>/dev/null &&
|
||||||
@@ -421,10 +435,13 @@ run_incremental_repack_and_verify () {
|
|||||||
test_commit A &&
|
test_commit A &&
|
||||||
git repack -adk &&
|
git repack -adk &&
|
||||||
git multi-pack-index write &&
|
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 \
|
GIT_TRACE2_EVENT="$(pwd)/midx-init.txt" git \
|
||||||
-c maintenance.incremental-repack.auto=1 \
|
-c maintenance.incremental-repack.auto=1 \
|
||||||
maintenance run --auto --task=incremental-repack 2>/dev/null &&
|
maintenance run --auto --task=incremental-repack 2>/dev/null &&
|
||||||
test_subcommand ! git multi-pack-index write --no-progress <midx-init.txt &&
|
test_subcommand ! git multi-pack-index write --no-progress <midx-init.txt &&
|
||||||
|
|
||||||
test_commit B &&
|
test_commit B &&
|
||||||
git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
|
git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
|
||||||
HEAD
|
HEAD
|
||||||
@@ -434,11 +451,14 @@ run_incremental_repack_and_verify () {
|
|||||||
-c maintenance.incremental-repack.auto=2 \
|
-c maintenance.incremental-repack.auto=2 \
|
||||||
maintenance run --auto --task=incremental-repack 2>/dev/null &&
|
maintenance run --auto --task=incremental-repack 2>/dev/null &&
|
||||||
test_subcommand ! git multi-pack-index write --no-progress <trace-A &&
|
test_subcommand ! git multi-pack-index write --no-progress <trace-A &&
|
||||||
|
|
||||||
test_commit C &&
|
test_commit C &&
|
||||||
git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
|
git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
|
||||||
HEAD
|
HEAD
|
||||||
^HEAD~1
|
^HEAD~1
|
||||||
EOF
|
EOF
|
||||||
|
git -c maintenance.incremental-repack.auto=2 \
|
||||||
|
maintenance is-needed --auto --task=incremental-repack &&
|
||||||
GIT_TRACE2_EVENT=$(pwd)/trace-B git \
|
GIT_TRACE2_EVENT=$(pwd)/trace-B git \
|
||||||
-c maintenance.incremental-repack.auto=2 \
|
-c maintenance.incremental-repack.auto=2 \
|
||||||
maintenance run --auto --task=incremental-repack 2>/dev/null &&
|
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 &&
|
git reflog expire --all --expire=now &&
|
||||||
test_commit reflog-one &&
|
test_commit reflog-one &&
|
||||||
test_commit reflog-two &&
|
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_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \
|
||||||
git -c maintenance.reflog-expire.auto=3 maintenance run --auto --task=reflog-expire &&
|
git -c maintenance.reflog-expire.auto=3 maintenance run --auto --task=reflog-expire &&
|
||||||
test_subcommand ! git reflog expire --all <reflog-expire-auto.txt &&
|
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_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \
|
||||||
git -c maintenance.reflog-expire.auto=2 maintenance run --auto --task=reflog-expire &&
|
git -c maintenance.reflog-expire.auto=2 maintenance run --auto --task=reflog-expire &&
|
||||||
test_subcommand git reflog expire --all <reflog-expire-auto.txt
|
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 &&
|
test_expect_worktree_prune ! git maintenance run --auto --task=worktree-prune &&
|
||||||
mkdir .git/worktrees &&
|
mkdir .git/worktrees &&
|
||||||
: >.git/worktrees/abc &&
|
: >.git/worktrees/abc &&
|
||||||
|
git maintenance is-needed --auto --task=worktree-prune &&
|
||||||
test_expect_worktree_prune git maintenance run --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 &&
|
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.
|
# 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=4 maintenance run --auto --task=worktree-prune &&
|
||||||
test_expect_worktree_prune git -c maintenance.worktree-prune.auto=3 maintenance run --auto --task=worktree-prune
|
git -c maintenance.worktree-prune.auto=3 maintenance is-needed --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 &&
|
|
||||||
test_expect_worktree_prune git -c maintenance.worktree-prune.auto=3 maintenance run --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 -rf worktree &&
|
||||||
|
|
||||||
rm -f worktree-prune.txt &&
|
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 &&
|
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_subcommand ! git worktree prune --expire 1.week.ago <worktree-prune.txt &&
|
||||||
test_path_is_dir .git/worktrees/worktree &&
|
test_path_is_dir .git/worktrees/worktree &&
|
||||||
|
|
||||||
rm -f worktree-prune.txt &&
|
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 &&
|
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_subcommand git worktree prune --expire now <worktree-prune.txt &&
|
||||||
test_path_is_missing .git/worktrees/worktree
|
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_expect_success 'rerere-gc task with --auto only prunes with prunable entries' '
|
||||||
test_when_finished "rm -rf .git/rr-cache" &&
|
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 &&
|
test_expect_rerere_gc ! git maintenance run --auto --task=rerere-gc &&
|
||||||
mkdir .git/rr-cache &&
|
mkdir .git/rr-cache &&
|
||||||
|
! git maintenance is-needed --auto --task=rerere-gc &&
|
||||||
test_expect_rerere_gc ! git maintenance run --auto --task=rerere-gc &&
|
test_expect_rerere_gc ! git maintenance run --auto --task=rerere-gc &&
|
||||||
: >.git/rr-cache/entry &&
|
: >.git/rr-cache/entry &&
|
||||||
|
git maintenance is-needed --auto --task=rerere-gc &&
|
||||||
test_expect_rerere_gc git maintenance run --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" &&
|
test_when_finished "rm -rf .git/rr-cache" &&
|
||||||
|
|
||||||
# A negative value should always prune.
|
# 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 &&
|
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.
|
# 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 &&
|
test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc &&
|
||||||
mkdir .git/rr-cache &&
|
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 &&
|
test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc &&
|
||||||
: >.git/rr-cache/entry-1 &&
|
: >.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 &&
|
test_expect_rerere_gc git -c maintenance.rerere-gc.auto=9000 maintenance run --auto --task=rerere-gc &&
|
||||||
|
|
||||||
# Zero should never prune.
|
# Zero should never prune.
|
||||||
: >.git/rr-cache/entry-1 &&
|
: >.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
|
test_expect_rerere_gc ! git -c maintenance.rerere-gc.auto=0 maintenance run --auto --task=rerere-gc
|
||||||
'
|
'
|
||||||
|
|
||||||
|
|||||||
@@ -1067,6 +1067,7 @@ void test_reftable_stack__add_performs_auto_compaction(void)
|
|||||||
.value_type = REFTABLE_REF_SYMREF,
|
.value_type = REFTABLE_REF_SYMREF,
|
||||||
.value.symref = (char *) "master",
|
.value.symref = (char *) "master",
|
||||||
};
|
};
|
||||||
|
bool required = false;
|
||||||
char buf[128];
|
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
|
* auto compaction is disabled. When enabled, we should merge
|
||||||
* all tables in the stack.
|
* 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);
|
cl_assert_equal_i(st->merged->tables_len, i + 1);
|
||||||
|
if (i < 1)
|
||||||
|
cl_assert_equal_b(required, false);
|
||||||
else
|
else
|
||||||
|
cl_assert_equal_b(required, true);
|
||||||
|
} else {
|
||||||
cl_assert_equal_i(st->merged->tables_len, 1);
|
cl_assert_equal_i(st->merged->tables_len, 1);
|
||||||
|
cl_assert_equal_b(required, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reftable_stack_destroy(st);
|
reftable_stack_destroy(st);
|
||||||
|
|||||||
Reference in New Issue
Block a user