Skip to content

Commit f9fa6b7

Browse files
committed
Merge branch 'kn/maintenance-is-needed' into jch
"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
2 parents 63b48f3 + 3fa5af4 commit f9fa6b7

File tree

14 files changed

+276
-43
lines changed

14 files changed

+276
-43
lines changed

Documentation/git-maintenance.adoc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ SYNOPSIS
1212
'git maintenance' run [<options>]
1313
'git maintenance' start [--scheduler=<scheduler>]
1414
'git maintenance' (stop|register|unregister) [<options>]
15+
'git maintenance' is-needed [<options>]
1516

1617

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

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

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

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

builtin/gc.c

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "reflog.h"
3737
#include "repack.h"
3838
#include "rerere.h"
39+
#include "revision.h"
3940
#include "blob.h"
4041
#include "tree.h"
4142
#include "promisor-remote.h"
@@ -286,12 +287,26 @@ static void maintenance_run_opts_release(struct maintenance_run_opts *opts)
286287

287288
static int pack_refs_condition(UNUSED struct gc_config *cfg)
288289
{
289-
/*
290-
* The auto-repacking logic for refs is handled by the ref backends and
291-
* exposed via `git pack-refs --auto`. We thus always return truish
292-
* here and let the backend decide for us.
293-
*/
294-
return 1;
290+
struct string_list included_refs = STRING_LIST_INIT_NODUP;
291+
struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
292+
struct refs_optimize_opts optimize_opts = {
293+
.exclusions = &excludes,
294+
.includes = &included_refs,
295+
.flags = REFS_OPTIMIZE_PRUNE | REFS_OPTIMIZE_AUTO,
296+
};
297+
bool required;
298+
299+
/* Check for all refs, similar to 'git refs optimize --all'. */
300+
string_list_append(optimize_opts.includes, "*");
301+
302+
if (refs_optimize_required(get_main_ref_store(the_repository),
303+
&optimize_opts, &required))
304+
return 0;
305+
306+
clear_ref_exclusions(&excludes);
307+
string_list_clear(&included_refs, 0);
308+
309+
return required == true;
295310
}
296311

297312
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,
10951110
return 0;
10961111
}
10971112

1098-
/* Remember to update object flag allocation in object.h */
1099-
#define SEEN (1u<<0)
1100-
11011113
struct cg_auto_data {
11021114
int num_not_in_graph;
11031115
int limit;
@@ -3444,7 +3456,59 @@ static int maintenance_stop(int argc, const char **argv, const char *prefix,
34443456
return update_background_schedule(NULL, 0);
34453457
}
34463458

3447-
static const char * const builtin_maintenance_usage[] = {
3459+
static const char *const builtin_maintenance_is_needed_usage[] = {
3460+
"git maintenance is-needed [--task=<task>] [--schedule]",
3461+
NULL
3462+
};
3463+
3464+
static int maintenance_is_needed(int argc, const char **argv, const char *prefix,
3465+
struct repository *repo UNUSED)
3466+
{
3467+
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
3468+
struct string_list selected_tasks = STRING_LIST_INIT_DUP;
3469+
struct gc_config cfg = GC_CONFIG_INIT;
3470+
struct option options[] = {
3471+
OPT_BOOL(0, "auto", &opts.auto_flag,
3472+
N_("run tasks based on the state of the repository")),
3473+
OPT_CALLBACK_F(0, "task", &selected_tasks, N_("task"),
3474+
N_("check a specific task"),
3475+
PARSE_OPT_NONEG, task_option_parse),
3476+
OPT_END()
3477+
};
3478+
bool is_needed = false;
3479+
3480+
argc = parse_options(argc, argv, prefix, options,
3481+
builtin_maintenance_is_needed_usage,
3482+
PARSE_OPT_STOP_AT_NON_OPTION);
3483+
if (argc)
3484+
usage_with_options(builtin_maintenance_is_needed_usage, options);
3485+
3486+
gc_config(&cfg);
3487+
initialize_task_config(&opts, &selected_tasks);
3488+
3489+
if (opts.auto_flag) {
3490+
for (size_t i = 0; i < opts.tasks_nr; i++) {
3491+
if (tasks[opts.tasks[i]].auto_condition &&
3492+
tasks[opts.tasks[i]].auto_condition(&cfg)) {
3493+
is_needed = true;
3494+
break;
3495+
}
3496+
}
3497+
} else {
3498+
/* When not using --auto, we should always require maintenance. */
3499+
is_needed = true;
3500+
}
3501+
3502+
string_list_clear(&selected_tasks, 0);
3503+
maintenance_run_opts_release(&opts);
3504+
gc_config_release(&cfg);
3505+
3506+
if (is_needed)
3507+
return 0;
3508+
return 1;
3509+
}
3510+
3511+
static const char *const builtin_maintenance_usage[] = {
34483512
N_("git maintenance <subcommand> [<options>]"),
34493513
NULL,
34503514
};
@@ -3461,6 +3525,7 @@ int cmd_maintenance(int argc,
34613525
OPT_SUBCOMMAND("stop", &fn, maintenance_stop),
34623526
OPT_SUBCOMMAND("register", &fn, maintenance_register),
34633527
OPT_SUBCOMMAND("unregister", &fn, maintenance_unregister),
3528+
OPT_SUBCOMMAND("is-needed", &fn, maintenance_is_needed),
34643529
OPT_END(),
34653530
};
34663531

object.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ void object_array_init(struct object_array *array);
8080
* list-objects-filter.c: 21
8181
* bloom.c: 2122
8282
* builtin/fsck.c: 0--3
83-
* builtin/gc.c: 0
8483
* builtin/index-pack.c: 2021
8584
* reflog.c: 10--12
8685
* builtin/show-branch.c: 0-------------------------------------------26

refs.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2318,6 +2318,13 @@ int refs_optimize(struct ref_store *refs, struct refs_optimize_opts *opts)
23182318
return refs->be->optimize(refs, opts);
23192319
}
23202320

2321+
int refs_optimize_required(struct ref_store *refs,
2322+
struct refs_optimize_opts *opts,
2323+
bool *required)
2324+
{
2325+
return refs->be->optimize_required(refs, opts, required);
2326+
}
2327+
23212328
int reference_get_peeled_oid(struct repository *repo,
23222329
const struct reference *ref,
23232330
struct object_id *peeled_oid)

refs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,13 @@ struct refs_optimize_opts {
520520
*/
521521
int refs_optimize(struct ref_store *refs, struct refs_optimize_opts *opts);
522522

523+
/*
524+
* Check if refs backend can be optimized by calling 'refs_optimize'.
525+
*/
526+
int refs_optimize_required(struct ref_store *ref_store,
527+
struct refs_optimize_opts *opts,
528+
bool *required);
529+
523530
/*
524531
* Setup reflog before using. Fill in err and return -1 on failure.
525532
*/

refs/debug.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ static int debug_optimize(struct ref_store *ref_store, struct refs_optimize_opts
132132
return res;
133133
}
134134

135+
static int debug_optimize_required(struct ref_store *ref_store,
136+
struct refs_optimize_opts *opts,
137+
bool *required)
138+
{
139+
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
140+
int res = drefs->refs->be->optimize_required(drefs->refs, opts, required);
141+
trace_printf_key(&trace_refs, "optimize_required: %s, res: %d\n",
142+
required ? "yes" : "no", res);
143+
return res;
144+
}
145+
135146
static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
136147
const char *newref, const char *logmsg)
137148
{
@@ -440,6 +451,8 @@ struct ref_storage_be refs_be_debug = {
440451
.transaction_abort = debug_transaction_abort,
441452

442453
.optimize = debug_optimize,
454+
.optimize_required = debug_optimize_required,
455+
443456
.rename_ref = debug_rename_ref,
444457
.copy_ref = debug_copy_ref,
445458

refs/files-backend.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,16 @@ static int files_optimize(struct ref_store *ref_store,
15121512
return 0;
15131513
}
15141514

1515+
static int files_optimize_required(struct ref_store *ref_store,
1516+
struct refs_optimize_opts *opts,
1517+
bool *required)
1518+
{
1519+
struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_READ,
1520+
"optimize_required");
1521+
*required = should_pack_refs(refs, opts);
1522+
return 0;
1523+
}
1524+
15151525
/*
15161526
* People using contrib's git-new-workdir have .git/logs/refs ->
15171527
* /some/other/path/.git/logs/refs, and that may live on another device.
@@ -3982,6 +3992,7 @@ struct ref_storage_be refs_be_files = {
39823992
.transaction_abort = files_transaction_abort,
39833993

39843994
.optimize = files_optimize,
3995+
.optimize_required = files_optimize_required,
39853996
.rename_ref = files_rename_ref,
39863997
.copy_ref = files_copy_ref,
39873998

refs/packed-backend.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,6 +1784,17 @@ static int packed_optimize(struct ref_store *ref_store UNUSED,
17841784
return 0;
17851785
}
17861786

1787+
static int packed_optimize_required(struct ref_store *ref_store UNUSED,
1788+
struct refs_optimize_opts *opts UNUSED,
1789+
bool *required)
1790+
{
1791+
/*
1792+
* Packed refs are already optimized.
1793+
*/
1794+
*required = false;
1795+
return 0;
1796+
}
1797+
17871798
static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store UNUSED)
17881799
{
17891800
return empty_ref_iterator_begin();
@@ -2130,6 +2141,8 @@ struct ref_storage_be refs_be_packed = {
21302141
.transaction_abort = packed_transaction_abort,
21312142

21322143
.optimize = packed_optimize,
2144+
.optimize_required = packed_optimize_required,
2145+
21332146
.rename_ref = NULL,
21342147
.copy_ref = NULL,
21352148

refs/refs-internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,11 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
424424

425425
typedef int optimize_fn(struct ref_store *ref_store,
426426
struct refs_optimize_opts *opts);
427+
428+
typedef int optimize_required_fn(struct ref_store *ref_store,
429+
struct refs_optimize_opts *opts,
430+
bool *required);
431+
427432
typedef int rename_ref_fn(struct ref_store *ref_store,
428433
const char *oldref, const char *newref,
429434
const char *logmsg);
@@ -549,6 +554,7 @@ struct ref_storage_be {
549554
ref_transaction_abort_fn *transaction_abort;
550555

551556
optimize_fn *optimize;
557+
optimize_required_fn *optimize_required;
552558
rename_ref_fn *rename_ref;
553559
copy_ref_fn *copy_ref;
554560

refs/reftable-backend.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,6 +1733,29 @@ static int reftable_be_optimize(struct ref_store *ref_store,
17331733
return ret;
17341734
}
17351735

1736+
static int reftable_be_optimize_required(struct ref_store *ref_store,
1737+
struct refs_optimize_opts *opts,
1738+
bool *required)
1739+
{
1740+
struct reftable_ref_store *refs = reftable_be_downcast(ref_store, REF_STORE_READ,
1741+
"optimize_refs_required");
1742+
struct reftable_stack *stack;
1743+
bool use_heuristics = false;
1744+
1745+
if (refs->err)
1746+
return refs->err;
1747+
1748+
stack = refs->worktree_backend.stack;
1749+
if (!stack)
1750+
stack = refs->main_backend.stack;
1751+
1752+
if (opts->flags & REFS_OPTIMIZE_AUTO)
1753+
use_heuristics = true;
1754+
1755+
return reftable_stack_compaction_required(stack, use_heuristics,
1756+
required);
1757+
}
1758+
17361759
struct write_create_symref_arg {
17371760
struct reftable_ref_store *refs;
17381761
struct reftable_stack *stack;
@@ -2756,6 +2779,8 @@ struct ref_storage_be refs_be_reftable = {
27562779
.transaction_abort = reftable_be_transaction_abort,
27572780

27582781
.optimize = reftable_be_optimize,
2782+
.optimize_required = reftable_be_optimize_required,
2783+
27592784
.rename_ref = reftable_be_rename_ref,
27602785
.copy_ref = reftable_be_copy_ref,
27612786

0 commit comments

Comments
 (0)