@matheusbsbs / matheus-bagliano / commits / 090511bc0b
Fork 0

maintenance: add --task option

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>

Derrick Stolee committed Sep 17, 2020 at 18:11 UTC 090511bc0b73bf3054f11df6f275ad3a3abbf34f
3 files changed +98 -4
Documentation/git-maintenance.txt
+8 -1
@@ -30,7 +30,9 @@ SUBCOMMANDS
30 -----------
31
32 run::
33 - Run one or more maintenance tasks.
33 + Run one or more maintenance tasks. If one or more `--task=<task>`
34 + options are specified, then those tasks are run in the provided
35 + order. Otherwise, only the `gc` task is run.
36
37 TASKS
38 -----
@@ -63,6 +65,11 @@ OPTIONS
65 --quiet::
66 Do not report progress or other information over `stderr`.
67
68 +--task=<task>::
69 + If this option is specified one or more times, then only run the
70 + specified tasks in the specified order. See the 'TASKS' section
71 + for the list of accepted `<task>` values.
72 +
73 GIT
74 ---
75 Part of the linkgit:git[1] suite
builtin/gc.c
+63 -3
@@ -701,7 +701,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
701 }
702
703 static const char * const builtin_maintenance_run_usage[] = {
704 - N_("git maintenance run [--auto] [--[no-]quiet]"),
704 + N_("git maintenance run [--auto] [--[no-]quiet] [--task=<task>]"),
705 NULL
706 };
707
@@ -759,6 +759,9 @@ struct maintenance_task {
759 const char *name;
760 maintenance_task_fn *fn;
761 unsigned enabled:1;
762 +
763 + /* -1 if not selected. */
764 + int selected_order;
765 };
766
767 enum maintenance_task_label {
@@ -781,13 +784,32 @@ static struct maintenance_task tasks[] = {
784 },
785 };
786
787 +static int compare_tasks_by_selection(const void *a_, const void *b_)
788 +{
789 + const struct maintenance_task *a, *b;
790 +
791 + a = (const struct maintenance_task *)&a_;
792 + b = (const struct maintenance_task *)&b_;
793 +
794 + return b->selected_order - a->selected_order;
795 +}
796 +
797 static int maintenance_run_tasks(struct maintenance_run_opts *opts)
798 {
786 - int i;
799 + int i, found_selected = 0;
800 int result = 0;
801
802 + for (i = 0; !found_selected && i < TASK__COUNT; i++)
803 + found_selected = tasks[i].selected_order >= 0;
804 +
805 + if (found_selected)
806 + QSORT(tasks, TASK__COUNT, compare_tasks_by_selection);
807 +
808 for (i = 0; i < TASK__COUNT; i++) {
790 - if (!tasks[i].enabled)
809 + if (found_selected && tasks[i].selected_order < 0)
810 + continue;
811 +
812 + if (!found_selected && !tasks[i].enabled)
813 continue;
814
815 if (tasks[i].fn(opts)) {
@@ -799,20 +821,58 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts)
821 return result;
822 }
823
824 +static int task_option_parse(const struct option *opt,
825 + const char *arg, int unset)
826 +{
827 + int i, num_selected = 0;
828 + struct maintenance_task *task = NULL;
829 +
830 + BUG_ON_OPT_NEG(unset);
831 +
832 + for (i = 0; i < TASK__COUNT; i++) {
833 + if (tasks[i].selected_order >= 0)
834 + num_selected++;
835 + if (!strcasecmp(tasks[i].name, arg)) {
836 + task = &tasks[i];
837 + }
838 + }
839 +
840 + if (!task) {
841 + error(_("'%s' is not a valid task"), arg);
842 + return 1;
843 + }
844 +
845 + if (task->selected_order >= 0) {
846 + error(_("task '%s' cannot be selected multiple times"), arg);
847 + return 1;
848 + }
849 +
850 + task->selected_order = num_selected + 1;
851 +
852 + return 0;
853 +}
854 +
855 static int maintenance_run(int argc, const char **argv, const char *prefix)
856 {
857 + int i;
858 struct maintenance_run_opts opts;
859 struct option builtin_maintenance_run_options[] = {
860 OPT_BOOL(0, "auto", &opts.auto_flag,
861 N_("run tasks based on the state of the repository")),
862 OPT_BOOL(0, "quiet", &opts.quiet,
863 N_("do not report progress or other information over stderr")),
864 + OPT_CALLBACK_F(0, "task", NULL, N_("task"),
865 + N_("run a specific task"),
866 + PARSE_OPT_NONEG, task_option_parse),
867 OPT_END()
868 };
869 memset(&opts, 0, sizeof(opts));
870
871 opts.quiet = !isatty(2);
872
873 + for (i = 0; i < TASK__COUNT; i++)
874 + tasks[i].selected_order = -1;
875 +
876 argc = parse_options(argc, argv, prefix,
877 builtin_maintenance_run_options,
878 builtin_maintenance_run_usage,
t/t7900-maintenance.sh
+27
@@ -27,4 +27,31 @@ test_expect_success 'run [--auto|--quiet]' '
27 test_subcommand git gc --no-quiet <run-no-quiet.txt
28 '
29
30 +test_expect_success 'run --task=<task>' '
31 + GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
32 + git maintenance run --task=commit-graph 2>/dev/null &&
33 + GIT_TRACE2_EVENT="$(pwd)/run-gc.txt" \
34 + git maintenance run --task=gc 2>/dev/null &&
35 + GIT_TRACE2_EVENT="$(pwd)/run-commit-graph.txt" \
36 + git maintenance run --task=commit-graph 2>/dev/null &&
37 + GIT_TRACE2_EVENT="$(pwd)/run-both.txt" \
38 + git maintenance run --task=commit-graph --task=gc 2>/dev/null &&
39 + test_subcommand ! git gc --quiet <run-commit-graph.txt &&
40 + test_subcommand git gc --quiet <run-gc.txt &&
41 + test_subcommand git gc --quiet <run-both.txt &&
42 + test_subcommand git commit-graph write --split --reachable --no-progress <run-commit-graph.txt &&
43 + test_subcommand ! git commit-graph write --split --reachable --no-progress <run-gc.txt &&
44 + test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt
45 +'
46 +
47 +test_expect_success 'run --task=bogus' '
48 + test_must_fail git maintenance run --task=bogus 2>err &&
49 + test_i18ngrep "is not a valid task" err
50 +'
51 +
52 +test_expect_success 'run --task duplicate' '
53 + test_must_fail git maintenance run --task=gc --task=gc 2>err &&
54 + test_i18ngrep "cannot be selected multiple times" err
55 +'
56 +
57 test_done