Skip to content

Commit 38a8fa5

Browse files
pks-tgitster
authored andcommitted
builtin/maintenance: stop modifying global array of tasks
When configuring maintenance tasks run by git-maintenance(1) we do so by modifying the global array of tasks directly. This is already quite bad on its own, as global state makes for logic that is hard to follow. Even more importantly though we use multiple different fields to track whether or not a task should be run: - "enabled" tracks the "maintenance.*.enabled" config key. This field disables execution of a task, unless the user has explicitly asked for the task. - "selected_order" tracks the order in which jobs have been asked for by the user via the "--task=" command line option. It overrides everything else, but only has an effect if at least one job has been selected. - "schedule" tracks the schedule priority for a job, that is how often it should run. This field only plays a role when the user has passed the "--schedule=" command line option. All of this makes it non-trivial to figure out which job really should be running right now. The logic to configure these fields and the logic that interprets them is distributed across multiple functions, making it even harder to follow it. Refactor the logic so that we stop modifying global state. Instead, we now compute which jobs should be run in `initialize_task_config()`, represented as an array of jobs to run that is stored in the options structure. Like this, all logic becomes self-contained and any users of this array only need to iterate through the tasks and execute them one by one. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a7c86d3 commit 38a8fa5

File tree

1 file changed

+112
-94
lines changed

1 file changed

+112
-94
lines changed

builtin/gc.c

Lines changed: 112 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,24 @@ static enum schedule_priority parse_schedule(const char *value)
251251
return SCHEDULE_NONE;
252252
}
253253

254+
enum maintenance_task_label {
255+
TASK_PREFETCH,
256+
TASK_LOOSE_OBJECTS,
257+
TASK_INCREMENTAL_REPACK,
258+
TASK_GC,
259+
TASK_COMMIT_GRAPH,
260+
TASK_PACK_REFS,
261+
TASK_REFLOG_EXPIRE,
262+
TASK_WORKTREE_PRUNE,
263+
TASK_RERERE_GC,
264+
265+
/* Leave as final value */
266+
TASK__COUNT
267+
};
268+
254269
struct maintenance_run_opts {
270+
enum maintenance_task_label *tasks;
271+
size_t tasks_nr, tasks_alloc;
255272
int auto_flag;
256273
int detach;
257274
int quiet;
@@ -261,6 +278,11 @@ struct maintenance_run_opts {
261278
.detach = -1, \
262279
}
263280

281+
static void maintenance_run_opts_release(struct maintenance_run_opts *opts)
282+
{
283+
free(opts->tasks);
284+
}
285+
264286
static int pack_refs_condition(UNUSED struct gc_config *cfg)
265287
{
266288
/*
@@ -1032,6 +1054,7 @@ int cmd_gc(int argc,
10321054
}
10331055

10341056
out:
1057+
maintenance_run_opts_release(&opts);
10351058
gc_config_release(&cfg);
10361059
return 0;
10371060
}
@@ -1524,30 +1547,9 @@ struct maintenance_task {
15241547
const char *name;
15251548
maintenance_task_fn *fn;
15261549
maintenance_auto_fn *auto_condition;
1527-
unsigned enabled:1;
1528-
1529-
enum schedule_priority schedule;
1530-
1531-
/* -1 if not selected. */
1532-
int selected_order;
1533-
};
1534-
1535-
enum maintenance_task_label {
1536-
TASK_PREFETCH,
1537-
TASK_LOOSE_OBJECTS,
1538-
TASK_INCREMENTAL_REPACK,
1539-
TASK_GC,
1540-
TASK_COMMIT_GRAPH,
1541-
TASK_PACK_REFS,
1542-
TASK_REFLOG_EXPIRE,
1543-
TASK_WORKTREE_PRUNE,
1544-
TASK_RERERE_GC,
1545-
1546-
/* Leave as final value */
1547-
TASK__COUNT
15481550
};
15491551

1550-
static struct maintenance_task tasks[] = {
1552+
static const struct maintenance_task tasks[] = {
15511553
[TASK_PREFETCH] = {
15521554
.name = "prefetch",
15531555
.fn = maintenance_task_prefetch,
@@ -1566,7 +1568,6 @@ static struct maintenance_task tasks[] = {
15661568
.name = "gc",
15671569
.fn = maintenance_task_gc,
15681570
.auto_condition = need_to_gc,
1569-
.enabled = 1,
15701571
},
15711572
[TASK_COMMIT_GRAPH] = {
15721573
.name = "commit-graph",
@@ -1595,18 +1596,9 @@ static struct maintenance_task tasks[] = {
15951596
},
15961597
};
15971598

1598-
static int compare_tasks_by_selection(const void *a_, const void *b_)
1599-
{
1600-
const struct maintenance_task *a = a_;
1601-
const struct maintenance_task *b = b_;
1602-
1603-
return b->selected_order - a->selected_order;
1604-
}
1605-
16061599
static int maintenance_run_tasks(struct maintenance_run_opts *opts,
16071600
struct gc_config *cfg)
16081601
{
1609-
int i, found_selected = 0;
16101602
int result = 0;
16111603
struct lock_file lk;
16121604
struct repository *r = the_repository;
@@ -1635,95 +1627,120 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts,
16351627
trace2_region_leave("maintenance", "detach", the_repository);
16361628
}
16371629

1638-
for (i = 0; !found_selected && i < TASK__COUNT; i++)
1639-
found_selected = tasks[i].selected_order >= 0;
1640-
1641-
if (found_selected)
1642-
QSORT(tasks, TASK__COUNT, compare_tasks_by_selection);
1643-
1644-
for (i = 0; i < TASK__COUNT; i++) {
1645-
if (found_selected && tasks[i].selected_order < 0)
1646-
continue;
1647-
1648-
if (!found_selected && !tasks[i].enabled)
1649-
continue;
1650-
1630+
for (size_t i = 0; i < opts->tasks_nr; i++) {
16511631
if (opts->auto_flag &&
1652-
(!tasks[i].auto_condition ||
1653-
!tasks[i].auto_condition(cfg)))
1654-
continue;
1655-
1656-
if (opts->schedule && tasks[i].schedule < opts->schedule)
1632+
(!tasks[opts->tasks[i]].auto_condition ||
1633+
!tasks[opts->tasks[i]].auto_condition(cfg)))
16571634
continue;
16581635

1659-
trace2_region_enter("maintenance", tasks[i].name, r);
1660-
if (tasks[i].fn(opts, cfg)) {
1661-
error(_("task '%s' failed"), tasks[i].name);
1636+
trace2_region_enter("maintenance", tasks[opts->tasks[i]].name, r);
1637+
if (tasks[opts->tasks[i]].fn(opts, cfg)) {
1638+
error(_("task '%s' failed"), tasks[opts->tasks[i]].name);
16621639
result = 1;
16631640
}
1664-
trace2_region_leave("maintenance", tasks[i].name, r);
1641+
trace2_region_leave("maintenance", tasks[opts->tasks[i]].name, r);
16651642
}
16661643

16671644
rollback_lock_file(&lk);
16681645
return result;
16691646
}
16701647

1671-
static void initialize_maintenance_strategy(void)
1648+
struct maintenance_strategy {
1649+
struct {
1650+
int enabled;
1651+
enum schedule_priority schedule;
1652+
} tasks[TASK__COUNT];
1653+
};
1654+
1655+
static const struct maintenance_strategy none_strategy = { 0 };
1656+
static const struct maintenance_strategy default_strategy = {
1657+
.tasks = {
1658+
[TASK_GC].enabled = 1,
1659+
},
1660+
};
1661+
static const struct maintenance_strategy incremental_strategy = {
1662+
.tasks = {
1663+
[TASK_COMMIT_GRAPH].enabled = 1,
1664+
[TASK_COMMIT_GRAPH].schedule = SCHEDULE_HOURLY,
1665+
[TASK_PREFETCH].enabled = 1,
1666+
[TASK_PREFETCH].schedule = SCHEDULE_HOURLY,
1667+
[TASK_INCREMENTAL_REPACK].enabled = 1,
1668+
[TASK_INCREMENTAL_REPACK].schedule = SCHEDULE_DAILY,
1669+
[TASK_LOOSE_OBJECTS].enabled = 1,
1670+
[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY,
1671+
[TASK_PACK_REFS].enabled = 1,
1672+
[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY,
1673+
},
1674+
};
1675+
1676+
static void initialize_task_config(struct maintenance_run_opts *opts,
1677+
const struct string_list *selected_tasks)
16721678
{
1679+
struct strbuf config_name = STRBUF_INIT;
1680+
struct maintenance_strategy strategy;
16731681
const char *config_str;
16741682

1675-
if (git_config_get_string_tmp("maintenance.strategy", &config_str))
1676-
return;
1683+
/*
1684+
* In case the user has asked us to run tasks explicitly we only use
1685+
* those specified tasks. Specifically, we do _not_ want to consult the
1686+
* config or maintenance strategy.
1687+
*/
1688+
if (selected_tasks->nr) {
1689+
for (size_t i = 0; i < selected_tasks->nr; i++) {
1690+
enum maintenance_task_label label = (intptr_t)selected_tasks->items[i].util;;
1691+
ALLOC_GROW(opts->tasks, opts->tasks_nr + 1, opts->tasks_alloc);
1692+
opts->tasks[opts->tasks_nr++] = label;
1693+
}
16771694

1678-
if (!strcasecmp(config_str, "incremental")) {
1679-
tasks[TASK_GC].schedule = SCHEDULE_NONE;
1680-
tasks[TASK_COMMIT_GRAPH].enabled = 1;
1681-
tasks[TASK_COMMIT_GRAPH].schedule = SCHEDULE_HOURLY;
1682-
tasks[TASK_PREFETCH].enabled = 1;
1683-
tasks[TASK_PREFETCH].schedule = SCHEDULE_HOURLY;
1684-
tasks[TASK_INCREMENTAL_REPACK].enabled = 1;
1685-
tasks[TASK_INCREMENTAL_REPACK].schedule = SCHEDULE_DAILY;
1686-
tasks[TASK_LOOSE_OBJECTS].enabled = 1;
1687-
tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY;
1688-
tasks[TASK_PACK_REFS].enabled = 1;
1689-
tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY;
1695+
return;
16901696
}
1691-
}
16921697

1693-
static void initialize_task_config(const struct string_list *selected_tasks,
1694-
int schedule)
1695-
{
1696-
struct strbuf config_name = STRBUF_INIT;
1698+
/*
1699+
* Otherwise, the strategy depends on whether we run as part of a
1700+
* scheduled job or not:
1701+
*
1702+
* - Scheduled maintenance does not perform any housekeeping by
1703+
* default, but requires the user to pick a maintenance strategy.
1704+
*
1705+
* - Unscheduled maintenance uses our default strategy.
1706+
*
1707+
* Both of these are affected by the gitconfig though, which may
1708+
* override specific aspects of our strategy.
1709+
*/
1710+
if (opts->schedule) {
1711+
strategy = none_strategy;
16971712

1698-
for (size_t i = 0; i < TASK__COUNT; i++)
1699-
tasks[i].selected_order = -1;
1700-
for (size_t i = 0; i < selected_tasks->nr; i++) {
1701-
struct maintenance_task *task = selected_tasks->items[i].util;
1702-
task->selected_order = i;
1713+
if (!git_config_get_string_tmp("maintenance.strategy", &config_str)) {
1714+
if (!strcasecmp(config_str, "incremental"))
1715+
strategy = incremental_strategy;
1716+
}
1717+
} else {
1718+
strategy = default_strategy;
17031719
}
17041720

1705-
if (schedule)
1706-
initialize_maintenance_strategy();
1707-
17081721
for (size_t i = 0; i < TASK__COUNT; i++) {
17091722
int config_value;
1710-
char *config_str;
17111723

17121724
strbuf_reset(&config_name);
17131725
strbuf_addf(&config_name, "maintenance.%s.enabled",
17141726
tasks[i].name);
1715-
17161727
if (!git_config_get_bool(config_name.buf, &config_value))
1717-
tasks[i].enabled = config_value;
1718-
1719-
strbuf_reset(&config_name);
1720-
strbuf_addf(&config_name, "maintenance.%s.schedule",
1721-
tasks[i].name);
1728+
strategy.tasks[i].enabled = config_value;
1729+
if (!strategy.tasks[i].enabled)
1730+
continue;
17221731

1723-
if (!git_config_get_string(config_name.buf, &config_str)) {
1724-
tasks[i].schedule = parse_schedule(config_str);
1725-
free(config_str);
1732+
if (opts->schedule) {
1733+
strbuf_reset(&config_name);
1734+
strbuf_addf(&config_name, "maintenance.%s.schedule",
1735+
tasks[i].name);
1736+
if (!git_config_get_string_tmp(config_name.buf, &config_str))
1737+
strategy.tasks[i].schedule = parse_schedule(config_str);
1738+
if (strategy.tasks[i].schedule < opts->schedule)
1739+
continue;
17261740
}
1741+
1742+
ALLOC_GROW(opts->tasks, opts->tasks_nr + 1, opts->tasks_alloc);
1743+
opts->tasks[opts->tasks_nr++] = i;
17271744
}
17281745

17291746
strbuf_release(&config_name);
@@ -1750,7 +1767,7 @@ static int task_option_parse(const struct option *opt,
17501767
return 1;
17511768
}
17521769

1753-
string_list_append(selected_tasks, arg)->util = &tasks[i];
1770+
string_list_append(selected_tasks, arg)->util = (void *)(intptr_t)i;
17541771

17551772
return 0;
17561773
}
@@ -1791,7 +1808,7 @@ static int maintenance_run(int argc, const char **argv, const char *prefix,
17911808
opts.schedule, "--schedule=");
17921809

17931810
gc_config(&cfg);
1794-
initialize_task_config(&selected_tasks, opts.schedule);
1811+
initialize_task_config(&opts, &selected_tasks);
17951812

17961813
if (argc != 0)
17971814
usage_with_options(builtin_maintenance_run_usage,
@@ -1800,6 +1817,7 @@ static int maintenance_run(int argc, const char **argv, const char *prefix,
18001817
ret = maintenance_run_tasks(&opts, &cfg);
18011818

18021819
string_list_clear(&selected_tasks, 0);
1820+
maintenance_run_opts_release(&opts);
18031821
gc_config_release(&cfg);
18041822
return ret;
18051823
}

0 commit comments

Comments
 (0)