diff options
Diffstat (limited to 'builtin/gc.c')
| -rw-r--r-- | builtin/gc.c | 515 |
1 files changed, 294 insertions, 221 deletions
diff --git a/builtin/gc.c b/builtin/gc.c index 7dc94f243d..03ae4926b2 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -114,7 +114,7 @@ static int gc_config_is_timestamp_never(const char *var) const char *value; timestamp_t expire; - if (!git_config_get_value(var, &value) && value) { + if (!repo_config_get_value(the_repository, var, &value) && value) { if (parse_expiry_date(value, &expire)) die(_("failed to parse '%s' value '%s'"), var, value); return expire == 0; @@ -178,7 +178,7 @@ static void gc_config(struct gc_config *cfg) char *owned = NULL; unsigned long ulongval; - if (!git_config_get_value("gc.packrefs", &value)) { + if (!repo_config_get_value(the_repository, "gc.packrefs", &value)) { if (value && !strcmp(value, "notbare")) cfg->pack_refs = -1; else @@ -189,13 +189,13 @@ static void gc_config(struct gc_config *cfg) gc_config_is_timestamp_never("gc.reflogexpireunreachable")) cfg->prune_reflogs = 0; - git_config_get_int("gc.aggressivewindow", &cfg->aggressive_window); - git_config_get_int("gc.aggressivedepth", &cfg->aggressive_depth); - git_config_get_int("gc.auto", &cfg->gc_auto_threshold); - git_config_get_int("gc.autopacklimit", &cfg->gc_auto_pack_limit); - git_config_get_bool("gc.autodetach", &cfg->detach_auto); - git_config_get_bool("gc.cruftpacks", &cfg->cruft_packs); - git_config_get_ulong("gc.maxcruftsize", &cfg->max_cruft_size); + repo_config_get_int(the_repository, "gc.aggressivewindow", &cfg->aggressive_window); + repo_config_get_int(the_repository, "gc.aggressivedepth", &cfg->aggressive_depth); + repo_config_get_int(the_repository, "gc.auto", &cfg->gc_auto_threshold); + repo_config_get_int(the_repository, "gc.autopacklimit", &cfg->gc_auto_pack_limit); + repo_config_get_bool(the_repository, "gc.autodetach", &cfg->detach_auto); + repo_config_get_bool(the_repository, "gc.cruftpacks", &cfg->cruft_packs); + repo_config_get_ulong(the_repository, "gc.maxcruftsize", &cfg->max_cruft_size); if (!repo_config_get_expiry(the_repository, "gc.pruneexpire", &owned)) { free(cfg->prune_expire); @@ -212,23 +212,23 @@ static void gc_config(struct gc_config *cfg) cfg->gc_log_expire = owned; } - git_config_get_ulong("gc.bigpackthreshold", &cfg->big_pack_threshold); - git_config_get_ulong("pack.deltacachesize", &cfg->max_delta_cache_size); + repo_config_get_ulong(the_repository, "gc.bigpackthreshold", &cfg->big_pack_threshold); + repo_config_get_ulong(the_repository, "pack.deltacachesize", &cfg->max_delta_cache_size); - if (!git_config_get_ulong("core.deltabasecachelimit", &ulongval)) + if (!repo_config_get_ulong(the_repository, "core.deltabasecachelimit", &ulongval)) cfg->delta_base_cache_limit = ulongval; - if (!git_config_get_string("gc.repackfilter", &owned)) { + if (!repo_config_get_string(the_repository, "gc.repackfilter", &owned)) { free(cfg->repack_filter); cfg->repack_filter = owned; } - if (!git_config_get_string("gc.repackfilterto", &owned)) { + if (!repo_config_get_string(the_repository, "gc.repackfilterto", &owned)) { free(cfg->repack_filter_to); cfg->repack_filter_to = owned; } - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); } enum schedule_priority { @@ -251,7 +251,24 @@ static enum schedule_priority parse_schedule(const char *value) return SCHEDULE_NONE; } +enum maintenance_task_label { + TASK_PREFETCH, + TASK_LOOSE_OBJECTS, + TASK_INCREMENTAL_REPACK, + TASK_GC, + TASK_COMMIT_GRAPH, + TASK_PACK_REFS, + TASK_REFLOG_EXPIRE, + TASK_WORKTREE_PRUNE, + TASK_RERERE_GC, + + /* Leave as final value */ + TASK__COUNT +}; + struct maintenance_run_opts { + enum maintenance_task_label *tasks; + size_t tasks_nr, tasks_alloc; int auto_flag; int detach; int quiet; @@ -261,6 +278,11 @@ struct maintenance_run_opts { .detach = -1, \ } +static void maintenance_run_opts_release(struct maintenance_run_opts *opts) +{ + free(opts->tasks); +} + static int pack_refs_condition(UNUSED struct gc_config *cfg) { /* @@ -290,7 +312,8 @@ struct count_reflog_entries_data { size_t limit; }; -static int count_reflog_entries(struct object_id *old_oid, struct object_id *new_oid, +static int count_reflog_entries(const char *refname UNUSED, + struct object_id *old_oid, struct object_id *new_oid, const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data) { @@ -310,7 +333,7 @@ static int reflog_expire_condition(struct gc_config *cfg UNUSED) }; int limit = 100; - git_config_get_int("maintenance.reflog-expire.auto", &limit); + repo_config_get_int(the_repository, "maintenance.reflog-expire.auto", &limit); if (!limit) return 0; if (limit < 0) @@ -324,6 +347,7 @@ static int reflog_expire_condition(struct gc_config *cfg UNUSED) count_reflog_entries, &data); reflog_expiry_cleanup(&data.policy); + reflog_clear_expire_config(&data.policy.opts); return data.count >= data.limit; } @@ -356,7 +380,7 @@ static int worktree_prune_condition(struct gc_config *cfg) struct dirent *d; DIR *dir = NULL; - git_config_get_int("maintenance.worktree-prune.auto", &limit); + repo_config_get_int(the_repository, "maintenance.worktree-prune.auto", &limit); if (limit <= 0) { should_prune = limit < 0; goto out; @@ -401,7 +425,7 @@ static int rerere_gc_condition(struct gc_config *cfg UNUSED) int should_gc = 0, limit = 1; DIR *dir = NULL; - git_config_get_int("maintenance.rerere-gc.auto", &limit); + repo_config_get_int(the_repository, "maintenance.rerere-gc.auto", &limit); if (limit <= 0) { should_gc = limit < 0; goto out; @@ -517,7 +541,7 @@ static uint64_t total_ram(void) return total; } #elif defined(HAVE_BSD_SYSCTL) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM) || defined(HW_PHYSMEM64)) - int64_t physical_memory; + uint64_t physical_memory; int mib[2]; size_t length; @@ -529,9 +553,16 @@ static uint64_t total_ram(void) # else mib[1] = HW_PHYSMEM; # endif - length = sizeof(int64_t); - if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0)) + length = sizeof(physical_memory); + if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0)) { + if (length == 4) { + uint32_t mem; + + if (!sysctl(mib, 2, &mem, &length, NULL, 0)) + physical_memory = mem; + } return physical_memory; + } #elif defined(GIT_WINDOWS_NATIVE) MEMORYSTATUSEX memInfo; @@ -796,22 +827,14 @@ done: return ret; } -static void gc_before_repack(struct maintenance_run_opts *opts, - struct gc_config *cfg) +static int gc_foreground_tasks(struct maintenance_run_opts *opts, + struct gc_config *cfg) { - /* - * We may be called twice, as both the pre- and - * post-daemonized phases will call us, but running these - * commands more than once is pointless and wasteful. - */ - static int done = 0; - if (done++) - return; - if (cfg->pack_refs && maintenance_task_pack_refs(opts, cfg)) - die(FAILED_RUN, "pack-refs"); + return error(FAILED_RUN, "pack-refs"); if (cfg->prune_reflogs && maintenance_task_reflog_expire(opts, cfg)) - die(FAILED_RUN, "reflog"); + return error(FAILED_RUN, "reflog"); + return 0; } int cmd_gc(int argc, @@ -820,12 +843,12 @@ int cmd_gc(int argc, struct repository *repo UNUSED) { int aggressive = 0; - int quiet = 0; int force = 0; const char *name; pid_t pid; int daemonized = 0; int keep_largest_pack = -1; + int skip_foreground_tasks = 0; timestamp_t dummy; struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT; struct gc_config cfg = GC_CONFIG_INIT; @@ -833,7 +856,7 @@ int cmd_gc(int argc, const char *prune_expire_arg = prune_expire_sentinel; int ret; struct option builtin_gc_options[] = { - OPT__QUIET(&quiet, N_("suppress progress reporting")), + OPT__QUIET(&opts.quiet, N_("suppress progress reporting")), { .type = OPTION_STRING, .long_name = "prune", @@ -858,6 +881,8 @@ int cmd_gc(int argc, N_("repack all other packs except the largest pack")), OPT_STRING(0, "expire-to", &cfg.repack_expire_to, N_("dir"), N_("pack prefix to store a pack containing pruned objects")), + OPT_HIDDEN_BOOL(0, "skip-foreground-tasks", &skip_foreground_tasks, + N_("skip maintenance tasks typically done in the foreground")), OPT_END() }; @@ -893,7 +918,7 @@ int cmd_gc(int argc, if (cfg.aggressive_window > 0) strvec_pushf(&repack, "--window=%d", cfg.aggressive_window); } - if (quiet) + if (opts.quiet) strvec_push(&repack, "-q"); if (opts.auto_flag) { @@ -908,7 +933,7 @@ int cmd_gc(int argc, goto out; } - if (!quiet) { + if (!opts.quiet) { if (opts.detach > 0) fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n")); else @@ -941,13 +966,16 @@ int cmd_gc(int argc, goto out; } - if (lock_repo_for_gc(force, &pid)) { - ret = 0; - goto out; - } + if (!skip_foreground_tasks) { + if (lock_repo_for_gc(force, &pid)) { + ret = 0; + goto out; + } - gc_before_repack(&opts, &cfg); /* dies on failure */ - delete_tempfile(&pidfile); + if (gc_foreground_tasks(&opts, &cfg) < 0) + die(NULL); + delete_tempfile(&pidfile); + } /* * failure to daemonize is ok, we'll continue @@ -976,9 +1004,10 @@ int cmd_gc(int argc, free(path); } - gc_before_repack(&opts, &cfg); + if (opts.detach <= 0 && !skip_foreground_tasks) + gc_foreground_tasks(&opts, &cfg); - if (!repository_format_precious_objects) { + if (!the_repository->repository_format_precious_objects) { struct child_process repack_cmd = CHILD_PROCESS_INIT; repack_cmd.git_cmd = 1; @@ -993,7 +1022,7 @@ int cmd_gc(int argc, strvec_pushl(&prune_cmd.args, "prune", "--expire", NULL); /* run `git prune` even if using cruft packs */ strvec_push(&prune_cmd.args, cfg.prune_expire); - if (quiet) + if (opts.quiet) strvec_push(&prune_cmd.args, "--no-progress"); if (repo_has_promisor_remote(the_repository)) strvec_push(&prune_cmd.args, @@ -1020,8 +1049,8 @@ int cmd_gc(int argc, } if (the_repository->settings.gc_write_commit_graph == 1) - write_commit_graph_reachable(the_repository->objects->odb, - !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0, + write_commit_graph_reachable(the_repository->objects->sources, + !opts.quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0, NULL); if (opts.auto_flag && too_many_loose_objects(&cfg)) @@ -1035,6 +1064,7 @@ int cmd_gc(int argc, } out: + maintenance_run_opts_release(&opts); gc_config_release(&cfg); return 0; } @@ -1082,7 +1112,7 @@ static int dfs_on_ref(const char *refname UNUSED, if (!peel_iterated_oid(the_repository, oid, &peeled)) oid = &peeled; - if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT) + if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT) return 0; commit = lookup_commit(the_repository, oid); @@ -1133,8 +1163,8 @@ static int should_write_commit_graph(struct gc_config *cfg UNUSED) data.num_not_in_graph = 0; data.limit = 100; - git_config_get_int("maintenance.commit-graph.auto", - &data.limit); + repo_config_get_int(the_repository, "maintenance.commit-graph.auto", + &data.limit); if (!data.limit) return 0; @@ -1211,8 +1241,14 @@ static int maintenance_task_prefetch(struct maintenance_run_opts *opts, return 0; } -static int maintenance_task_gc(struct maintenance_run_opts *opts, - struct gc_config *cfg UNUSED) +static int maintenance_task_gc_foreground(struct maintenance_run_opts *opts, + struct gc_config *cfg) +{ + return gc_foreground_tasks(opts, cfg); +} + +static int maintenance_task_gc_background(struct maintenance_run_opts *opts, + struct gc_config *cfg UNUSED) { struct child_process child = CHILD_PROCESS_INIT; @@ -1226,6 +1262,7 @@ static int maintenance_task_gc(struct maintenance_run_opts *opts, else strvec_push(&child.args, "--no-quiet"); strvec_push(&child.args, "--no-detach"); + strvec_push(&child.args, "--skip-foreground-tasks"); return run_command(&child); } @@ -1265,15 +1302,15 @@ static int loose_object_auto_condition(struct gc_config *cfg UNUSED) { int count = 0; - git_config_get_int("maintenance.loose-objects.auto", - &loose_object_auto_limit); + repo_config_get_int(the_repository, "maintenance.loose-objects.auto", + &loose_object_auto_limit); if (!loose_object_auto_limit) return 0; if (loose_object_auto_limit < 0) return 1; - return for_each_loose_file_in_objdir(the_repository->objects->odb->path, + return for_each_loose_file_in_source(the_repository->objects->sources, loose_object_count, NULL, NULL, &count); } @@ -1308,7 +1345,7 @@ static int pack_loose(struct maintenance_run_opts *opts) * Do not start pack-objects process * if there are no loose objects. */ - if (!for_each_loose_file_in_objdir(r->objects->odb->path, + if (!for_each_loose_file_in_source(r->objects->sources, bail_on_loose, NULL, NULL, NULL)) return 0; @@ -1320,7 +1357,7 @@ static int pack_loose(struct maintenance_run_opts *opts) strvec_push(&pack_proc.args, "--quiet"); else strvec_push(&pack_proc.args, "--no-quiet"); - strvec_pushf(&pack_proc.args, "%s/pack/loose", r->objects->odb->path); + strvec_pushf(&pack_proc.args, "%s/pack/loose", r->objects->sources->path); pack_proc.in = -1; @@ -1348,11 +1385,9 @@ static int pack_loose(struct maintenance_run_opts *opts) else if (data.batch_size > 0) data.batch_size--; /* Decrease for equality on limit. */ - for_each_loose_file_in_objdir(r->objects->odb->path, + for_each_loose_file_in_source(r->objects->sources, write_loose_object_to_stdin, - NULL, - NULL, - &data); + NULL, NULL, &data); fclose(data.in); @@ -1380,8 +1415,8 @@ static int incremental_repack_auto_condition(struct gc_config *cfg UNUSED) if (!the_repository->settings.core_multi_pack_index) return 0; - git_config_get_int("maintenance.incremental-repack.auto", - &incremental_repack_auto_limit); + repo_config_get_int(the_repository, "maintenance.incremental-repack.auto", + &incremental_repack_auto_limit); if (!incremental_repack_auto_limit) return 0; @@ -1513,107 +1548,120 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts return 0; } -typedef int maintenance_task_fn(struct maintenance_run_opts *opts, - struct gc_config *cfg); - -/* - * An auto condition function returns 1 if the task should run - * and 0 if the task should NOT run. See needs_to_gc() for an - * example. - */ -typedef int maintenance_auto_fn(struct gc_config *cfg); +typedef int (*maintenance_task_fn)(struct maintenance_run_opts *opts, + struct gc_config *cfg); +typedef int (*maintenance_auto_fn)(struct gc_config *cfg); struct maintenance_task { const char *name; - maintenance_task_fn *fn; - maintenance_auto_fn *auto_condition; - unsigned enabled:1; - - enum schedule_priority schedule; - /* -1 if not selected. */ - int selected_order; -}; + /* + * Work that will be executed before detaching. This should not include + * tasks that may run for an extended amount of time as it does cause + * auto-maintenance to block until foreground tasks have been run. + */ + maintenance_task_fn foreground; -enum maintenance_task_label { - TASK_PREFETCH, - TASK_LOOSE_OBJECTS, - TASK_INCREMENTAL_REPACK, - TASK_GC, - TASK_COMMIT_GRAPH, - TASK_PACK_REFS, - TASK_REFLOG_EXPIRE, - TASK_WORKTREE_PRUNE, - TASK_RERERE_GC, + /* + * Work that will be executed after detaching. When not detaching the + * work will be run in the foreground, as well. + */ + maintenance_task_fn background; - /* Leave as final value */ - TASK__COUNT + /* + * An auto condition function returns 1 if the task should run and 0 if + * the task should NOT run. See needs_to_gc() for an example. + */ + maintenance_auto_fn auto_condition; }; -static struct maintenance_task tasks[] = { +static const struct maintenance_task tasks[] = { [TASK_PREFETCH] = { - "prefetch", - maintenance_task_prefetch, + .name = "prefetch", + .background = maintenance_task_prefetch, }, [TASK_LOOSE_OBJECTS] = { - "loose-objects", - maintenance_task_loose_objects, - loose_object_auto_condition, + .name = "loose-objects", + .background = maintenance_task_loose_objects, + .auto_condition = loose_object_auto_condition, }, [TASK_INCREMENTAL_REPACK] = { - "incremental-repack", - maintenance_task_incremental_repack, - incremental_repack_auto_condition, + .name = "incremental-repack", + .background = maintenance_task_incremental_repack, + .auto_condition = incremental_repack_auto_condition, }, [TASK_GC] = { - "gc", - maintenance_task_gc, - need_to_gc, - 1, + .name = "gc", + .foreground = maintenance_task_gc_foreground, + .background = maintenance_task_gc_background, + .auto_condition = need_to_gc, }, [TASK_COMMIT_GRAPH] = { - "commit-graph", - maintenance_task_commit_graph, - should_write_commit_graph, + .name = "commit-graph", + .background = maintenance_task_commit_graph, + .auto_condition = should_write_commit_graph, }, [TASK_PACK_REFS] = { - "pack-refs", - maintenance_task_pack_refs, - pack_refs_condition, + .name = "pack-refs", + .foreground = maintenance_task_pack_refs, + .auto_condition = pack_refs_condition, }, [TASK_REFLOG_EXPIRE] = { - "reflog-expire", - maintenance_task_reflog_expire, - reflog_expire_condition, + .name = "reflog-expire", + .foreground = maintenance_task_reflog_expire, + .auto_condition = reflog_expire_condition, }, [TASK_WORKTREE_PRUNE] = { - "worktree-prune", - maintenance_task_worktree_prune, - worktree_prune_condition, + .name = "worktree-prune", + .background = maintenance_task_worktree_prune, + .auto_condition = worktree_prune_condition, }, [TASK_RERERE_GC] = { - "rerere-gc", - maintenance_task_rerere_gc, - rerere_gc_condition, + .name = "rerere-gc", + .background = maintenance_task_rerere_gc, + .auto_condition = rerere_gc_condition, }, }; -static int compare_tasks_by_selection(const void *a_, const void *b_) +enum task_phase { + TASK_PHASE_FOREGROUND, + TASK_PHASE_BACKGROUND, +}; + +static int maybe_run_task(const struct maintenance_task *task, + struct repository *repo, + struct maintenance_run_opts *opts, + struct gc_config *cfg, + enum task_phase phase) { - const struct maintenance_task *a = a_; - const struct maintenance_task *b = b_; + int foreground = (phase == TASK_PHASE_FOREGROUND); + maintenance_task_fn fn = foreground ? task->foreground : task->background; + const char *region = foreground ? "maintenance foreground" : "maintenance"; + int ret = 0; - return b->selected_order - a->selected_order; + if (!fn) + return 0; + if (opts->auto_flag && + (!task->auto_condition || !task->auto_condition(cfg))) + return 0; + + trace2_region_enter(region, task->name, repo); + if (fn(opts, cfg)) { + error(_("task '%s' failed"), task->name); + ret = 1; + } + trace2_region_leave(region, task->name, repo); + + return ret; } static int maintenance_run_tasks(struct maintenance_run_opts *opts, struct gc_config *cfg) { - int i, found_selected = 0; int result = 0; struct lock_file lk; struct repository *r = the_repository; - char *lock_path = xstrfmt("%s/maintenance", r->objects->odb->path); + char *lock_path = xstrfmt("%s/maintenance", r->objects->sources->path); if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) { /* @@ -1631,6 +1679,11 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts, } free(lock_path); + for (size_t i = 0; i < opts->tasks_nr; i++) + if (maybe_run_task(&tasks[opts->tasks[i]], r, opts, cfg, + TASK_PHASE_FOREGROUND)) + result = 1; + /* Failure to daemonize is ok, we'll continue in foreground. */ if (opts->detach > 0) { trace2_region_enter("maintenance", "detach", the_repository); @@ -1638,120 +1691,138 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts, trace2_region_leave("maintenance", "detach", the_repository); } - for (i = 0; !found_selected && i < TASK__COUNT; i++) - found_selected = tasks[i].selected_order >= 0; - - if (found_selected) - QSORT(tasks, TASK__COUNT, compare_tasks_by_selection); - - for (i = 0; i < TASK__COUNT; i++) { - if (found_selected && tasks[i].selected_order < 0) - continue; - - if (!found_selected && !tasks[i].enabled) - continue; - - if (opts->auto_flag && - (!tasks[i].auto_condition || - !tasks[i].auto_condition(cfg))) - continue; - - if (opts->schedule && tasks[i].schedule < opts->schedule) - continue; - - trace2_region_enter("maintenance", tasks[i].name, r); - if (tasks[i].fn(opts, cfg)) { - error(_("task '%s' failed"), tasks[i].name); + for (size_t i = 0; i < opts->tasks_nr; i++) + if (maybe_run_task(&tasks[opts->tasks[i]], r, opts, cfg, + TASK_PHASE_BACKGROUND)) result = 1; - } - trace2_region_leave("maintenance", tasks[i].name, r); - } rollback_lock_file(&lk); return result; } -static void initialize_maintenance_strategy(void) +struct maintenance_strategy { + struct { + int enabled; + enum schedule_priority schedule; + } tasks[TASK__COUNT]; +}; + +static const struct maintenance_strategy none_strategy = { 0 }; +static const struct maintenance_strategy default_strategy = { + .tasks = { + [TASK_GC].enabled = 1, + }, +}; +static const struct maintenance_strategy incremental_strategy = { + .tasks = { + [TASK_COMMIT_GRAPH].enabled = 1, + [TASK_COMMIT_GRAPH].schedule = SCHEDULE_HOURLY, + [TASK_PREFETCH].enabled = 1, + [TASK_PREFETCH].schedule = SCHEDULE_HOURLY, + [TASK_INCREMENTAL_REPACK].enabled = 1, + [TASK_INCREMENTAL_REPACK].schedule = SCHEDULE_DAILY, + [TASK_LOOSE_OBJECTS].enabled = 1, + [TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY, + [TASK_PACK_REFS].enabled = 1, + [TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY, + }, +}; + +static void initialize_task_config(struct maintenance_run_opts *opts, + const struct string_list *selected_tasks) { + struct strbuf config_name = STRBUF_INIT; + struct maintenance_strategy strategy; const char *config_str; - if (git_config_get_string_tmp("maintenance.strategy", &config_str)) - return; + /* + * In case the user has asked us to run tasks explicitly we only use + * those specified tasks. Specifically, we do _not_ want to consult the + * config or maintenance strategy. + */ + if (selected_tasks->nr) { + for (size_t i = 0; i < selected_tasks->nr; i++) { + enum maintenance_task_label label = (intptr_t)selected_tasks->items[i].util;; + ALLOC_GROW(opts->tasks, opts->tasks_nr + 1, opts->tasks_alloc); + opts->tasks[opts->tasks_nr++] = label; + } - if (!strcasecmp(config_str, "incremental")) { - tasks[TASK_GC].schedule = SCHEDULE_NONE; - tasks[TASK_COMMIT_GRAPH].enabled = 1; - tasks[TASK_COMMIT_GRAPH].schedule = SCHEDULE_HOURLY; - tasks[TASK_PREFETCH].enabled = 1; - tasks[TASK_PREFETCH].schedule = SCHEDULE_HOURLY; - tasks[TASK_INCREMENTAL_REPACK].enabled = 1; - tasks[TASK_INCREMENTAL_REPACK].schedule = SCHEDULE_DAILY; - tasks[TASK_LOOSE_OBJECTS].enabled = 1; - tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY; - tasks[TASK_PACK_REFS].enabled = 1; - tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY; + return; } -} -static void initialize_task_config(int schedule) -{ - int i; - struct strbuf config_name = STRBUF_INIT; + /* + * Otherwise, the strategy depends on whether we run as part of a + * scheduled job or not: + * + * - Scheduled maintenance does not perform any housekeeping by + * default, but requires the user to pick a maintenance strategy. + * + * - Unscheduled maintenance uses our default strategy. + * + * Both of these are affected by the gitconfig though, which may + * override specific aspects of our strategy. + */ + if (opts->schedule) { + strategy = none_strategy; - if (schedule) - initialize_maintenance_strategy(); + if (!repo_config_get_string_tmp(the_repository, "maintenance.strategy", &config_str)) { + if (!strcasecmp(config_str, "incremental")) + strategy = incremental_strategy; + } + } else { + strategy = default_strategy; + } - for (i = 0; i < TASK__COUNT; i++) { + for (size_t i = 0; i < TASK__COUNT; i++) { int config_value; - char *config_str; strbuf_reset(&config_name); strbuf_addf(&config_name, "maintenance.%s.enabled", tasks[i].name); + if (!repo_config_get_bool(the_repository, config_name.buf, &config_value)) + strategy.tasks[i].enabled = config_value; + if (!strategy.tasks[i].enabled) + continue; - if (!git_config_get_bool(config_name.buf, &config_value)) - tasks[i].enabled = config_value; - - strbuf_reset(&config_name); - strbuf_addf(&config_name, "maintenance.%s.schedule", - tasks[i].name); - - if (!git_config_get_string(config_name.buf, &config_str)) { - tasks[i].schedule = parse_schedule(config_str); - free(config_str); + if (opts->schedule) { + strbuf_reset(&config_name); + strbuf_addf(&config_name, "maintenance.%s.schedule", + tasks[i].name); + if (!repo_config_get_string_tmp(the_repository, config_name.buf, &config_str)) + strategy.tasks[i].schedule = parse_schedule(config_str); + if (strategy.tasks[i].schedule < opts->schedule) + continue; } + + ALLOC_GROW(opts->tasks, opts->tasks_nr + 1, opts->tasks_alloc); + opts->tasks[opts->tasks_nr++] = i; } strbuf_release(&config_name); } -static int task_option_parse(const struct option *opt UNUSED, +static int task_option_parse(const struct option *opt, const char *arg, int unset) { - int i, num_selected = 0; - struct maintenance_task *task = NULL; + struct string_list *selected_tasks = opt->value; + size_t i; BUG_ON_OPT_NEG(unset); - for (i = 0; i < TASK__COUNT; i++) { - if (tasks[i].selected_order >= 0) - num_selected++; - if (!strcasecmp(tasks[i].name, arg)) { - task = &tasks[i]; - } - } - - if (!task) { + for (i = 0; i < TASK__COUNT; i++) + if (!strcasecmp(tasks[i].name, arg)) + break; + if (i >= TASK__COUNT) { error(_("'%s' is not a valid task"), arg); return 1; } - if (task->selected_order >= 0) { + if (unsorted_string_list_has_string(selected_tasks, arg)) { error(_("task '%s' cannot be selected multiple times"), arg); return 1; } - task->selected_order = num_selected + 1; + string_list_append(selected_tasks, arg)->util = (void *)(intptr_t)i; return 0; } @@ -1759,8 +1830,8 @@ static int task_option_parse(const struct option *opt UNUSED, static int maintenance_run(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { - int i; 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 builtin_maintenance_run_options[] = { OPT_BOOL(0, "auto", &opts.auto_flag, @@ -1772,7 +1843,7 @@ static int maintenance_run(int argc, const char **argv, const char *prefix, maintenance_opt_schedule), OPT_BOOL(0, "quiet", &opts.quiet, N_("do not report progress or other information over stderr")), - OPT_CALLBACK_F(0, "task", NULL, N_("task"), + OPT_CALLBACK_F(0, "task", &selected_tasks, N_("task"), N_("run a specific task"), PARSE_OPT_NONEG, task_option_parse), OPT_END() @@ -1781,25 +1852,27 @@ static int maintenance_run(int argc, const char **argv, const char *prefix, opts.quiet = !isatty(2); - for (i = 0; i < TASK__COUNT; i++) - tasks[i].selected_order = -1; - argc = parse_options(argc, argv, prefix, builtin_maintenance_run_options, builtin_maintenance_run_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (opts.auto_flag && opts.schedule) - die(_("use at most one of --auto and --schedule=<frequency>")); + die_for_incompatible_opt2(opts.auto_flag, "--auto", + opts.schedule, "--schedule="); + die_for_incompatible_opt2(selected_tasks.nr, "--task=", + opts.schedule, "--schedule="); gc_config(&cfg); - initialize_task_config(opts.schedule); + initialize_task_config(&opts, &selected_tasks); if (argc != 0) usage_with_options(builtin_maintenance_run_usage, builtin_maintenance_run_options); ret = maintenance_run_tasks(&opts, &cfg); + + string_list_clear(&selected_tasks, 0); + maintenance_run_opts_release(&opts); gc_config_release(&cfg); return ret; } @@ -1840,13 +1913,13 @@ static int maintenance_register(int argc, const char **argv, const char *prefix, options); /* Disable foreground maintenance */ - git_config_set("maintenance.auto", "false"); + repo_config_set(the_repository, "maintenance.auto", "false"); /* Set maintenance strategy, if unset */ - if (git_config_get("maintenance.strategy")) - git_config_set("maintenance.strategy", "incremental"); + if (repo_config_get(the_repository, "maintenance.strategy")) + repo_config_set(the_repository, "maintenance.strategy", "incremental"); - if (!git_config_get_string_multi(key, &list)) { + if (!repo_config_get_string_multi(the_repository, key, &list)) { for_each_string_list_item(item, list) { if (!strcmp(maintpath, item->string)) { found = 1; @@ -1865,7 +1938,7 @@ static int maintenance_register(int argc, const char **argv, const char *prefix, } if (!config_file) die(_("$HOME not set")); - rc = git_config_set_multivar_in_file_gently( + rc = repo_config_set_multivar_in_file_gently(the_repository, config_file, "maintenance.repo", maintpath, CONFIG_REGEX_NONE, NULL, 0); free(global_config_file); @@ -1915,7 +1988,7 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi } if (!(config_file ? git_configset_get_string_multi(&cs, key, &list) - : git_config_get_string_multi(key, &list))) { + : repo_config_get_string_multi(the_repository, key, &list))) { for_each_string_list_item(item, list) { if (!strcmp(maintpath, item->string)) { found = 1; @@ -1934,7 +2007,7 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi } if (!config_file) die(_("$HOME not set")); - rc = git_config_set_multivar_in_file_gently( + rc = repo_config_set_multivar_in_file_gently(the_repository, config_file, key, NULL, maintpath, NULL, CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE); free(global_config_file); @@ -2271,7 +2344,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit die(_("failed to create directories for '%s'"), filename); if ((long)lock_file_timeout_ms < 0 && - git_config_get_ulong("gc.launchctlplistlocktimeoutms", + repo_config_get_ulong(the_repository, "gc.launchctlplistlocktimeoutms", &lock_file_timeout_ms)) lock_file_timeout_ms = 150; @@ -3085,7 +3158,7 @@ static int update_background_schedule(const struct maintenance_start_opts *opts, unsigned int i; int result = 0; struct lock_file lk; - char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path); + char *lock_path = xstrfmt("%s/schedule", the_repository->objects->sources->path); if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) { if (errno == EEXIST) |
