]> git.ipfire.org Git - thirdparty/git.git/commitdiff
maintenance: add --task option
authorDerrick Stolee <dstolee@microsoft.com>
Thu, 17 Sep 2020 18:11:47 +0000 (18:11 +0000)
committerJunio C Hamano <gitster@pobox.com>
Thu, 17 Sep 2020 18:30:05 +0000 (11:30 -0700)
A user may want to only run certain maintenance tasks in a certain
order. Add the --task=<task> option, which allows a user to specify an
ordered list of tasks to run. These cannot be run multiple times,
however.

Here is where our array of maintenance_task pointers becomes critical.
We can sort the array of pointers based on the task order, but we do not
want to move the struct data itself in order to preserve the hashmap
references. We use the hashmap to match the --task=<task> arguments into
the task struct data.

Keep in mind that the 'enabled' member of the maintenance_task struct is
a placeholder for a future 'maintenance.<task>.enabled' config option.
Thus, we use the 'enabled' member to specify which tasks are run when
the user does not specify any --task=<task> arguments. The 'enabled'
member should be ignored if --task=<task> appears.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-maintenance.txt
builtin/gc.c
t/t7900-maintenance.sh

index fc5dbcf0b9efd3ab6630dd2de94f8d1e8fcc16e5..819ca41ab6aa1794fc0caed09a96a30d4cbf8d4a 100644 (file)
@@ -30,7 +30,9 @@ SUBCOMMANDS
 -----------
 
 run::
-       Run one or more maintenance tasks.
+       Run one or more maintenance tasks. If one or more `--task=<task>`
+       options are specified, then those tasks are run in the provided
+       order. Otherwise, only the `gc` task is run.
 
 TASKS
 -----
@@ -63,6 +65,11 @@ OPTIONS
 --quiet::
        Do not report progress or other information over `stderr`.
 
+--task=<task>::
+       If this option is specified one or more times, then only run the
+       specified tasks in the specified order. See the 'TASKS' section
+       for the list of accepted `<task>` values.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 86b807a008e1951b2b83d784c03e5d358d90574e..00fff59bdb2c05a4275d1a654b16cc2c89399476 100644 (file)
@@ -701,7 +701,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
 }
 
 static const char * const builtin_maintenance_run_usage[] = {
-       N_("git maintenance run [--auto] [--[no-]quiet]"),
+       N_("git maintenance run [--auto] [--[no-]quiet] [--task=<task>]"),
        NULL
 };
 
@@ -759,6 +759,9 @@ struct maintenance_task {
        const char *name;
        maintenance_task_fn *fn;
        unsigned enabled:1;
+
+       /* -1 if not selected. */
+       int selected_order;
 };
 
 enum maintenance_task_label {
@@ -781,13 +784,32 @@ static struct maintenance_task tasks[] = {
        },
 };
 
+static int compare_tasks_by_selection(const void *a_, const void *b_)
+{
+       const struct maintenance_task *a, *b;
+
+       a = (const struct maintenance_task *)&a_;
+       b = (const struct maintenance_task *)&b_;
+
+       return b->selected_order - a->selected_order;
+}
+
 static int maintenance_run_tasks(struct maintenance_run_opts *opts)
 {
-       int i;
+       int i, found_selected = 0;
        int result = 0;
 
+       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 (!tasks[i].enabled)
+               if (found_selected && tasks[i].selected_order < 0)
+                       continue;
+
+               if (!found_selected && !tasks[i].enabled)
                        continue;
 
                if (tasks[i].fn(opts)) {
@@ -799,20 +821,58 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts)
        return result;
 }
 
+static int task_option_parse(const struct option *opt,
+                            const char *arg, int unset)
+{
+       int i, num_selected = 0;
+       struct maintenance_task *task = NULL;
+
+       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) {
+               error(_("'%s' is not a valid task"), arg);
+               return 1;
+       }
+
+       if (task->selected_order >= 0) {
+               error(_("task '%s' cannot be selected multiple times"), arg);
+               return 1;
+       }
+
+       task->selected_order = num_selected + 1;
+
+       return 0;
+}
+
 static int maintenance_run(int argc, const char **argv, const char *prefix)
 {
+       int i;
        struct maintenance_run_opts opts;
        struct option builtin_maintenance_run_options[] = {
                OPT_BOOL(0, "auto", &opts.auto_flag,
                         N_("run tasks based on the state of the repository")),
                OPT_BOOL(0, "quiet", &opts.quiet,
                         N_("do not report progress or other information over stderr")),
+               OPT_CALLBACK_F(0, "task", NULL, N_("task"),
+                       N_("run a specific task"),
+                       PARSE_OPT_NONEG, task_option_parse),
                OPT_END()
        };
        memset(&opts, 0, sizeof(opts));
 
        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,
index 505a1b4d60acf8ffc24b205b1698151c72848960..fb4cadd30c12201be145bd2aa5ed1a675fec06c2 100755 (executable)
@@ -27,4 +27,31 @@ test_expect_success 'run [--auto|--quiet]' '
        test_subcommand git gc --no-quiet <run-no-quiet.txt
 '
 
+test_expect_success 'run --task=<task>' '
+       GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
+               git maintenance run --task=commit-graph 2>/dev/null &&
+       GIT_TRACE2_EVENT="$(pwd)/run-gc.txt" \
+               git maintenance run --task=gc 2>/dev/null &&
+       GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
+               git maintenance run --task=commit-graph 2>/dev/null &&
+       GIT_TRACE2_EVENT="$(pwd)/run-both.txt" \
+               git maintenance run --task=commit-graph --task=gc 2>/dev/null &&
+       test_subcommand ! git gc --quiet <run-commit-graph.txt &&
+       test_subcommand git gc --quiet <run-gc.txt &&
+       test_subcommand git gc --quiet <run-both.txt &&
+       test_subcommand git commit-graph write --split --reachable --no-progress <run-commit-graph.txt &&
+       test_subcommand ! git commit-graph write --split --reachable --no-progress <run-gc.txt &&
+       test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt
+'
+
+test_expect_success 'run --task=bogus' '
+       test_must_fail git maintenance run --task=bogus 2>err &&
+       test_i18ngrep "is not a valid task" err
+'
+
+test_expect_success 'run --task duplicate' '
+       test_must_fail git maintenance run --task=gc --task=gc 2>err &&
+       test_i18ngrep "cannot be selected multiple times" err
+'
+
 test_done