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"
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"
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
5457static timestamp_t gc_log_expire_time ;
55-
5658static 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-
6159static struct tempfile * pidfile ;
6260static struct lock_file log_lock ;
63-
6461static struct string_list pack_garbage = STRING_LIST_INIT_DUP ;
6562
6663static void clean_pack_garbage (void )
@@ -339,6 +336,94 @@ 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 strbuf buf = STRBUF_INIT ;
354+ int should_prune = 0 , limit = 1 ;
355+ timestamp_t expiry_date ;
356+ struct dirent * d ;
357+ DIR * dir = NULL ;
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+ goto out ;
367+
368+ dir = opendir (repo_git_path_replace (the_repository , & buf , "worktrees" ));
369+ if (!dir )
370+ goto out ;
371+
372+ while (limit && (d = readdir_skip_dot_and_dotdot (dir ))) {
373+ char * wtpath ;
374+ strbuf_reset (& buf );
375+ if (should_prune_worktree (d -> d_name , & buf , & wtpath , expiry_date ))
376+ limit -- ;
377+ free (wtpath );
378+ }
379+
380+ should_prune = !limit ;
381+
382+ out :
383+ if (dir )
384+ closedir (dir );
385+ strbuf_release (& buf );
386+ return should_prune ;
387+ }
388+
389+ static int maintenance_task_rerere_gc (struct maintenance_run_opts * opts UNUSED ,
390+ struct gc_config * cfg UNUSED )
391+ {
392+ struct child_process rerere_cmd = CHILD_PROCESS_INIT ;
393+ rerere_cmd .git_cmd = 1 ;
394+ strvec_pushl (& rerere_cmd .args , "rerere" , "gc" , NULL );
395+ return run_command (& rerere_cmd );
396+ }
397+
398+ static int rerere_gc_condition (struct gc_config * cfg UNUSED )
399+ {
400+ struct strbuf path = STRBUF_INIT ;
401+ int should_gc = 0 , limit = 1 ;
402+ DIR * dir = NULL ;
403+
404+ git_config_get_int ("maintenance.rerere-gc.auto" , & limit );
405+ if (limit <= 0 ) {
406+ should_gc = limit < 0 ;
407+ goto out ;
408+ }
409+
410+ /*
411+ * We skip garbage collection in case we either have no "rr-cache"
412+ * directory or when it doesn't contain at least one entry.
413+ */
414+ repo_git_path_replace (the_repository , & path , "rr-cache" );
415+ dir = opendir (path .buf );
416+ if (!dir )
417+ goto out ;
418+ should_gc = !!readdir_skip_dot_and_dotdot (dir );
419+
420+ out :
421+ strbuf_release (& path );
422+ if (dir )
423+ closedir (dir );
424+ return should_gc ;
425+ }
426+
342427static int too_many_loose_objects (struct gc_config * cfg )
343428{
344429 /*
@@ -728,9 +813,9 @@ static void gc_before_repack(struct maintenance_run_opts *opts,
728813}
729814
730815int cmd_gc (int argc ,
731- const char * * argv ,
732- const char * prefix ,
733- struct repository * repo UNUSED )
816+ const char * * argv ,
817+ const char * prefix ,
818+ struct repository * repo UNUSED )
734819{
735820 int aggressive = 0 ;
736821 int quiet = 0 ;
@@ -740,7 +825,6 @@ struct repository *repo UNUSED)
740825 int daemonized = 0 ;
741826 int keep_largest_pack = -1 ;
742827 timestamp_t dummy ;
743- struct child_process rerere_cmd = CHILD_PROCESS_INIT ;
744828 struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT ;
745829 struct gc_config cfg = GC_CONFIG_INIT ;
746830 const char * prune_expire_sentinel = "sentinel" ;
@@ -779,9 +863,6 @@ struct repository *repo UNUSED)
779863 builtin_gc_usage , builtin_gc_options );
780864
781865 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 );
785866
786867 gc_config (& cfg );
787868
@@ -907,34 +988,27 @@ struct repository *repo UNUSED)
907988 if (cfg .prune_expire ) {
908989 struct child_process prune_cmd = CHILD_PROCESS_INIT ;
909990
991+ strvec_pushl (& prune_cmd .args , "prune" , "--expire" , NULL );
910992 /* run `git prune` even if using cruft packs */
911- strvec_push (& prune , cfg .prune_expire );
993+ strvec_push (& prune_cmd . args , cfg .prune_expire );
912994 if (quiet )
913- strvec_push (& prune , "--no-progress" );
995+ strvec_push (& prune_cmd . args , "--no-progress" );
914996 if (repo_has_promisor_remote (the_repository ))
915- strvec_push (& prune ,
997+ strvec_push (& prune_cmd . args ,
916998 "--exclude-promisor-objects" );
917999 prune_cmd .git_cmd = 1 ;
918- strvec_pushv ( & prune_cmd . args , prune . v );
1000+
9191001 if (run_command (& prune_cmd ))
920- die (FAILED_RUN , prune .v [0 ]);
1002+ die (FAILED_RUN , prune_cmd . args .v [0 ]);
9211003 }
9221004 }
9231005
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- }
1006+ if (cfg .prune_worktrees_expire &&
1007+ maintenance_task_worktree_prune (& opts , & cfg ))
1008+ die (FAILED_RUN , "worktree" );
9331009
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 ]);
1010+ if (maintenance_task_rerere_gc (& opts , & cfg ))
1011+ die (FAILED_RUN , "rerere" );
9381012
9391013 report_garbage = report_pack_garbage ;
9401014 reprepare_packed_git (the_repository );
@@ -1467,6 +1541,8 @@ enum maintenance_task_label {
14671541 TASK_COMMIT_GRAPH ,
14681542 TASK_PACK_REFS ,
14691543 TASK_REFLOG_EXPIRE ,
1544+ TASK_WORKTREE_PRUNE ,
1545+ TASK_RERERE_GC ,
14701546
14711547 /* Leave as final value */
14721548 TASK__COUNT
@@ -1508,6 +1584,16 @@ static struct maintenance_task tasks[] = {
15081584 maintenance_task_reflog_expire ,
15091585 reflog_expire_condition ,
15101586 },
1587+ [TASK_WORKTREE_PRUNE ] = {
1588+ "worktree-prune" ,
1589+ maintenance_task_worktree_prune ,
1590+ worktree_prune_condition ,
1591+ },
1592+ [TASK_RERERE_GC ] = {
1593+ "rerere-gc" ,
1594+ maintenance_task_rerere_gc ,
1595+ rerere_gc_condition ,
1596+ },
15111597};
15121598
15131599static int compare_tasks_by_selection (const void * a_ , const void * b_ )
0 commit comments