Skip to content

Commit 9f67f10

Browse files
committed
Merge branch 'ps/maintenance-missing-tasks' into seen
Make repository clean-up tasks "gc" can do available to "git maintenance" front-end. Comments? * ps/maintenance-missing-tasks: builtin/maintenance: introduce "rerere-gc" task builtin/gc: move rerere garbage collection into separate function rerere: provide function to collect stale entries builtin/maintenance: introduce "worktree-prune" task worktree: expose function to retrieve worktree names builtin/gc: move pruning of worktrees into a separate function builtin/gc: remove global variables where it trivial to do builtin/gc: fix indentation of `cmd_gc()` parameters
2 parents 09fe180 + 091fc89 commit 9f67f10

File tree

9 files changed

+399
-72
lines changed

9 files changed

+399
-72
lines changed

Documentation/config/maintenance.adoc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,19 @@ maintenance.reflog-expire.auto::
8383
positive value implies the command should run when the number of
8484
expired reflog entries in the "HEAD" reflog is at least the value of
8585
`maintenance.loose-objects.auto`. The default value is 100.
86+
87+
maintenance.rerere-gc.auto::
88+
This integer config option controls how often the `rerere-gc` task
89+
should be run as part of `git maintenance run --auto`. If zero, then
90+
the `rerere-gc` task will not run with the `--auto` option. A negative
91+
value will force the task to run every time. Otherwise, a positive
92+
value implies the command should run when the number of prunable rerere
93+
entries exceeds the value. The default value is 20.
94+
95+
maintenance.worktree-prune.auto::
96+
This integer config option controls how often the `worktree-prune` task
97+
should be run as part of `git maintenance run --auto`. If zero, then
98+
the `worktree-prune` task will not run with the `--auto` option. A
99+
negative value will force the task to run every time. Otherwise, a
100+
positive value implies the command should run when the number of
101+
prunable worktrees exceeds the value. The default value is 1.

Documentation/git-maintenance.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,14 @@ reflog-expire::
166166
The `reflog-expire` task deletes any entries in the reflog older than the
167167
expiry threshold. See linkgit:git-reflog[1] for more information.
168168

169+
rerere-gc::
170+
The `rerere-gc` task invokes garbage collection for stale entries in
171+
the rerere cache. See linkgit:git-rerere[1] for more information.
172+
173+
worktree-prune::
174+
The `worktree-prune` task deletes stale or broken worktrees. See
175+
linkit:git-worktree[1] for more information.
176+
169177
OPTIONS
170178
-------
171179
--auto::

builtin/gc.c

Lines changed: 122 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "builtin.h"
1717
#include "abspath.h"
1818
#include "date.h"
19+
#include "dir.h"
1920
#include "environment.h"
2021
#include "hex.h"
2122
#include "config.h"
@@ -33,6 +34,7 @@
3334
#include "pack-objects.h"
3435
#include "path.h"
3536
#include "reflog.h"
37+
#include "rerere.h"
3638
#include "blob.h"
3739
#include "tree.h"
3840
#include "promisor-remote.h"
@@ -43,6 +45,7 @@
4345
#include "hook.h"
4446
#include "setup.h"
4547
#include "trace2.h"
48+
#include "worktree.h"
4649

4750
#define FAILED_RUN "failed to run %s"
4851

@@ -52,15 +55,9 @@ static const char * const builtin_gc_usage[] = {
5255
};
5356

5457
static timestamp_t gc_log_expire_time;
55-
5658
static struct strvec repack = STRVEC_INIT;
57-
static struct strvec prune = STRVEC_INIT;
58-
static struct strvec prune_worktrees = STRVEC_INIT;
59-
static struct strvec rerere = STRVEC_INIT;
60-
6159
static struct tempfile *pidfile;
6260
static struct lock_file log_lock;
63-
6461
static struct string_list pack_garbage = STRING_LIST_INIT_DUP;
6562

6663
static void clean_pack_garbage(void)
@@ -339,6 +336,99 @@ static int maintenance_task_reflog_expire(struct maintenance_run_opts *opts UNUS
339336
return run_command(&cmd);
340337
}
341338

339+
static int maintenance_task_worktree_prune(struct maintenance_run_opts *opts UNUSED,
340+
struct gc_config *cfg)
341+
{
342+
struct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT;
343+
344+
prune_worktrees_cmd.git_cmd = 1;
345+
strvec_pushl(&prune_worktrees_cmd.args, "worktree", "prune", "--expire", NULL);
346+
strvec_push(&prune_worktrees_cmd.args, cfg->prune_worktrees_expire);
347+
348+
return run_command(&prune_worktrees_cmd);
349+
}
350+
351+
static int worktree_prune_condition(struct gc_config *cfg)
352+
{
353+
struct strvec worktrees = STRVEC_INIT;
354+
struct strbuf reason = STRBUF_INIT;
355+
timestamp_t expiry_date;
356+
int should_prune = 0;
357+
int limit = 1;
358+
359+
git_config_get_int("maintenance.worktree-prune.auto", &limit);
360+
if (limit <= 0) {
361+
should_prune = limit < 0;
362+
goto out;
363+
}
364+
365+
if (parse_expiry_date(cfg->prune_worktrees_expire, &expiry_date) ||
366+
get_worktree_names(the_repository, &worktrees) < 0)
367+
goto out;
368+
369+
for (size_t i = 0; i < worktrees.nr; i++) {
370+
char *wtpath;
371+
372+
strbuf_reset(&reason);
373+
if (should_prune_worktree(worktrees.v[i], &reason, &wtpath, expiry_date)) {
374+
limit--;
375+
376+
if (!limit) {
377+
should_prune = 1;
378+
goto out;
379+
}
380+
}
381+
free(wtpath);
382+
}
383+
384+
out:
385+
strvec_clear(&worktrees);
386+
strbuf_release(&reason);
387+
return should_prune;
388+
}
389+
390+
static int maintenance_task_rerere_gc(struct maintenance_run_opts *opts UNUSED,
391+
struct gc_config *cfg UNUSED)
392+
{
393+
struct child_process rerere_cmd = CHILD_PROCESS_INIT;
394+
rerere_cmd.git_cmd = 1;
395+
strvec_pushl(&rerere_cmd.args, "rerere", "gc", NULL);
396+
return run_command(&rerere_cmd);
397+
}
398+
399+
static int rerere_gc_condition(struct gc_config *cfg UNUSED)
400+
{
401+
struct strbuf path = STRBUF_INIT;
402+
struct string_list prunable_dirs = STRING_LIST_INIT_DUP;
403+
struct rerere_id *prunable_entries = NULL;
404+
size_t prunable_entries_nr;
405+
int should_gc = 0;
406+
int limit = 20;
407+
408+
git_config_get_int("maintenance.rerere-gc.auto", &limit);
409+
if (limit <= 0) {
410+
should_gc = limit < 0;
411+
goto out;
412+
}
413+
414+
/* Skip garbage collecting the rerere cache in case rerere is disabled. */
415+
repo_git_path_replace(the_repository, &path, "rr-cache");
416+
if (!is_directory(path.buf))
417+
goto out;
418+
419+
if (rerere_collect_stale_entries(the_repository, &prunable_dirs,
420+
&prunable_entries, &prunable_entries_nr) < 0)
421+
goto out;
422+
423+
should_gc = prunable_entries_nr >= limit;
424+
425+
out:
426+
string_list_clear(&prunable_dirs, 0);
427+
free(prunable_entries);
428+
strbuf_release(&path);
429+
return should_gc;
430+
}
431+
342432
static int too_many_loose_objects(struct gc_config *cfg)
343433
{
344434
/*
@@ -728,9 +818,9 @@ static void gc_before_repack(struct maintenance_run_opts *opts,
728818
}
729819

730820
int cmd_gc(int argc,
731-
const char **argv,
732-
const char *prefix,
733-
struct repository *repo UNUSED)
821+
const char **argv,
822+
const char *prefix,
823+
struct repository *repo UNUSED)
734824
{
735825
int aggressive = 0;
736826
int quiet = 0;
@@ -740,7 +830,6 @@ struct repository *repo UNUSED)
740830
int daemonized = 0;
741831
int keep_largest_pack = -1;
742832
timestamp_t dummy;
743-
struct child_process rerere_cmd = CHILD_PROCESS_INIT;
744833
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
745834
struct gc_config cfg = GC_CONFIG_INIT;
746835
const char *prune_expire_sentinel = "sentinel";
@@ -779,9 +868,6 @@ struct repository *repo UNUSED)
779868
builtin_gc_usage, builtin_gc_options);
780869

781870
strvec_pushl(&repack, "repack", "-d", "-l", NULL);
782-
strvec_pushl(&prune, "prune", "--expire", NULL);
783-
strvec_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
784-
strvec_pushl(&rerere, "rerere", "gc", NULL);
785871

786872
gc_config(&cfg);
787873

@@ -907,34 +993,27 @@ struct repository *repo UNUSED)
907993
if (cfg.prune_expire) {
908994
struct child_process prune_cmd = CHILD_PROCESS_INIT;
909995

996+
strvec_pushl(&prune_cmd.args, "prune", "--expire", NULL);
910997
/* run `git prune` even if using cruft packs */
911-
strvec_push(&prune, cfg.prune_expire);
998+
strvec_push(&prune_cmd.args, cfg.prune_expire);
912999
if (quiet)
913-
strvec_push(&prune, "--no-progress");
1000+
strvec_push(&prune_cmd.args, "--no-progress");
9141001
if (repo_has_promisor_remote(the_repository))
915-
strvec_push(&prune,
1002+
strvec_push(&prune_cmd.args,
9161003
"--exclude-promisor-objects");
9171004
prune_cmd.git_cmd = 1;
918-
strvec_pushv(&prune_cmd.args, prune.v);
1005+
9191006
if (run_command(&prune_cmd))
920-
die(FAILED_RUN, prune.v[0]);
1007+
die(FAILED_RUN, prune_cmd.args.v[0]);
9211008
}
9221009
}
9231010

924-
if (cfg.prune_worktrees_expire) {
925-
struct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT;
926-
927-
strvec_push(&prune_worktrees, cfg.prune_worktrees_expire);
928-
prune_worktrees_cmd.git_cmd = 1;
929-
strvec_pushv(&prune_worktrees_cmd.args, prune_worktrees.v);
930-
if (run_command(&prune_worktrees_cmd))
931-
die(FAILED_RUN, prune_worktrees.v[0]);
932-
}
1011+
if (cfg.prune_worktrees_expire &&
1012+
maintenance_task_worktree_prune(&opts, &cfg))
1013+
die(FAILED_RUN, "worktree");
9331014

934-
rerere_cmd.git_cmd = 1;
935-
strvec_pushv(&rerere_cmd.args, rerere.v);
936-
if (run_command(&rerere_cmd))
937-
die(FAILED_RUN, rerere.v[0]);
1015+
if (maintenance_task_rerere_gc(&opts, &cfg))
1016+
die(FAILED_RUN, "rerere");
9381017

9391018
report_garbage = report_pack_garbage;
9401019
reprepare_packed_git(the_repository);
@@ -1467,6 +1546,8 @@ enum maintenance_task_label {
14671546
TASK_COMMIT_GRAPH,
14681547
TASK_PACK_REFS,
14691548
TASK_REFLOG_EXPIRE,
1549+
TASK_WORKTREE_PRUNE,
1550+
TASK_RERERE_GC,
14701551

14711552
/* Leave as final value */
14721553
TASK__COUNT
@@ -1508,6 +1589,16 @@ static struct maintenance_task tasks[] = {
15081589
maintenance_task_reflog_expire,
15091590
reflog_expire_condition,
15101591
},
1592+
[TASK_WORKTREE_PRUNE] = {
1593+
"worktree-prune",
1594+
maintenance_task_worktree_prune,
1595+
worktree_prune_condition,
1596+
},
1597+
[TASK_RERERE_GC] = {
1598+
"rerere-gc",
1599+
maintenance_task_rerere_gc,
1600+
rerere_gc_condition,
1601+
},
15111602
};
15121603

15131604
static int compare_tasks_by_selection(const void *a_, const void *b_)

builtin/worktree.c

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -211,27 +211,24 @@ static void prune_dups(struct string_list *l)
211211

212212
static void prune_worktrees(void)
213213
{
214-
struct strbuf reason = STRBUF_INIT;
215214
struct strbuf main_path = STRBUF_INIT;
216215
struct string_list kept = STRING_LIST_INIT_DUP;
217-
char *path;
218-
DIR *dir;
219-
struct dirent *d;
216+
struct strvec worktrees = STRVEC_INIT;
217+
struct strbuf reason = STRBUF_INIT;
220218

221-
path = repo_git_path(the_repository, "worktrees");
222-
dir = opendir(path);
223-
free(path);
224-
if (!dir)
219+
if (get_worktree_names(the_repository, &worktrees) < 0 ||
220+
!worktrees.nr)
225221
return;
226-
while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
222+
223+
for (size_t i = 0; i < worktrees.nr; i++) {
227224
char *path;
225+
228226
strbuf_reset(&reason);
229-
if (should_prune_worktree(d->d_name, &reason, &path, expire))
230-
prune_worktree(d->d_name, reason.buf);
227+
if (should_prune_worktree(worktrees.v[i], &reason, &path, expire))
228+
prune_worktree(worktrees.v[i], reason.buf);
231229
else if (path)
232-
string_list_append_nodup(&kept, path)->util = xstrdup(d->d_name);
230+
string_list_append_nodup(&kept, path)->util = xstrdup(worktrees.v[i]);
233231
}
234-
closedir(dir);
235232

236233
strbuf_add_absolute_path(&main_path, repo_get_common_dir(the_repository));
237234
/* massage main worktree absolute path to match 'gitdir' content */
@@ -242,6 +239,8 @@ static void prune_worktrees(void)
242239

243240
if (!show_only)
244241
delete_worktrees_dir_if_empty();
242+
243+
strvec_clear(&worktrees);
245244
strbuf_release(&reason);
246245
}
247246

0 commit comments

Comments
 (0)