]> git.ipfire.org Git - thirdparty/git.git/commitdiff
builtin/gc: add a `--detach` flag
authorPatrick Steinhardt <ps@pks.im>
Fri, 16 Aug 2024 10:45:11 +0000 (12:45 +0200)
committerJunio C Hamano <gitster@pobox.com>
Fri, 16 Aug 2024 16:46:25 +0000 (09:46 -0700)
When running `git gc --auto`, the command will by default detach and
continue running in the background. This behaviour can be tweaked via
the `gc.autoDetach` config, but not via a command line switch. We need
that in a subsequent commit though, where git-maintenance(1) will want
to ask its git-gc(1) child process to not detach anymore.

Add a `--[no-]detach` flag that does this for us.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-gc.txt
builtin/gc.c
t/t6500-gc.sh

index b5561c458a101c32a51ccca8599d4898e166b174..370e22faaeb55e95e9996cb8dc92361652a14db6 100644 (file)
@@ -9,7 +9,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
 SYNOPSIS
 --------
 [verse]
-'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] [--force] [--keep-largest-pack]
+'git gc' [--aggressive] [--auto] [--[no-]detach] [--quiet] [--prune=<date> | --no-prune] [--force] [--keep-largest-pack]
 
 DESCRIPTION
 -----------
@@ -53,6 +53,9 @@ configuration options such as `gc.auto` and `gc.autoPackLimit`, all
 other housekeeping tasks (e.g. rerere, working trees, reflog...) will
 be performed as well.
 
+--[no-]detach::
+       Run in the background if the system supports it. This option overrides
+       the `gc.autoDetach` config.
 
 --[no-]cruft::
        When expiring unreachable objects, pack them separately into a
index f81555708183ddf2ad4991d2cd2a95e348d1c0e1..269a77960fba0941cac8cf2d7ef989593db7fe5d 100644 (file)
@@ -242,9 +242,13 @@ static enum schedule_priority parse_schedule(const char *value)
 
 struct maintenance_run_opts {
        int auto_flag;
+       int detach;
        int quiet;
        enum schedule_priority schedule;
 };
+#define MAINTENANCE_RUN_OPTS_INIT { \
+       .detach = -1, \
+}
 
 static int pack_refs_condition(UNUSED struct gc_config *cfg)
 {
@@ -664,7 +668,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        int keep_largest_pack = -1;
        timestamp_t dummy;
        struct child_process rerere_cmd = CHILD_PROCESS_INIT;
-       struct maintenance_run_opts opts = {0};
+       struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
        struct gc_config cfg = GC_CONFIG_INIT;
        const char *prune_expire_sentinel = "sentinel";
        const char *prune_expire_arg = prune_expire_sentinel;
@@ -681,6 +685,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
                OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"),
                           PARSE_OPT_NOCOMPLETE),
+               OPT_BOOL(0, "detach", &opts.detach,
+                        N_("perform garbage collection in the background")),
                OPT_BOOL_F(0, "force", &force,
                           N_("force running gc even if there may be another gc running"),
                           PARSE_OPT_NOCOMPLETE),
@@ -729,6 +735,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                strvec_push(&repack, "-q");
 
        if (opts.auto_flag) {
+               if (cfg.detach_auto && opts.detach < 0)
+                       opts.detach = 1;
+
                /*
                 * Auto-gc should be least intrusive as possible.
                 */
@@ -738,38 +747,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                }
 
                if (!quiet) {
-                       if (cfg.detach_auto)
+                       if (opts.detach > 0)
                                fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n"));
                        else
                                fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
                        fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
                }
-               if (cfg.detach_auto) {
-                       ret = report_last_gc_error();
-                       if (ret == 1) {
-                               /* Last gc --auto failed. Skip this one. */
-                               ret = 0;
-                               goto out;
-
-                       } else if (ret) {
-                               /* an I/O error occurred, already reported */
-                               goto out;
-                       }
-
-                       if (lock_repo_for_gc(force, &pid)) {
-                               ret = 0;
-                               goto out;
-                       }
-
-                       gc_before_repack(&opts, &cfg); /* dies on failure */
-                       delete_tempfile(&pidfile);
-
-                       /*
-                        * failure to daemonize is ok, we'll continue
-                        * in foreground
-                        */
-                       daemonized = !daemonize();
-               }
        } else {
                struct string_list keep_pack = STRING_LIST_INIT_NODUP;
 
@@ -784,6 +767,33 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                string_list_clear(&keep_pack, 0);
        }
 
+       if (opts.detach > 0) {
+               ret = report_last_gc_error();
+               if (ret == 1) {
+                       /* Last gc --auto failed. Skip this one. */
+                       ret = 0;
+                       goto out;
+
+               } else if (ret) {
+                       /* an I/O error occurred, already reported */
+                       goto out;
+               }
+
+               if (lock_repo_for_gc(force, &pid)) {
+                       ret = 0;
+                       goto out;
+               }
+
+               gc_before_repack(&opts, &cfg); /* dies on failure */
+               delete_tempfile(&pidfile);
+
+               /*
+                * failure to daemonize is ok, we'll continue
+                * in foreground
+                */
+               daemonized = !daemonize();
+       }
+
        name = lock_repo_for_gc(force, &pid);
        if (name) {
                if (opts.auto_flag) {
@@ -1537,7 +1547,7 @@ static int task_option_parse(const struct option *opt UNUSED,
 static int maintenance_run(int argc, const char **argv, const char *prefix)
 {
        int i;
-       struct maintenance_run_opts opts;
+       struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
        struct gc_config cfg = GC_CONFIG_INIT;
        struct option builtin_maintenance_run_options[] = {
                OPT_BOOL(0, "auto", &opts.auto_flag,
@@ -1554,8 +1564,6 @@ static int maintenance_run(int argc, const char **argv, const char *prefix)
        };
        int ret;
 
-       memset(&opts, 0, sizeof(opts));
-
        opts.quiet = !isatty(2);
 
        for (i = 0; i < TASK__COUNT; i++)
index 1b5909d1b70bb0f02bdff6f40c778022ede1af25..53784559681cab582ea51dd6c475972001ac01e5 100755 (executable)
@@ -338,14 +338,14 @@ test_expect_success 'gc.maxCruftSize sets appropriate repack options' '
        test_subcommand $cruft_max_size_opts --max-cruft-size=3145728 <trace2.txt
 '
 
-run_and_wait_for_auto_gc () {
+run_and_wait_for_gc () {
        # We read stdout from gc for the side effect of waiting until the
        # background gc process exits, closing its fd 9.  Furthermore, the
        # variable assignment from a command substitution preserves the
        # exit status of the main gc process.
        # Note: this fd trickery doesn't work on Windows, but there is no
        # need to, because on Win the auto gc always runs in the foreground.
-       doesnt_matter=$(git gc --auto 9>&1)
+       doesnt_matter=$(git gc "$@" 9>&1)
 }
 
 test_expect_success 'background auto gc does not run if gc.log is present and recent but does if it is old' '
@@ -361,7 +361,7 @@ test_expect_success 'background auto gc does not run if gc.log is present and re
        test-tool chmtime =-345600 .git/gc.log &&
        git gc --auto &&
        test_config gc.logexpiry 2.days &&
-       run_and_wait_for_auto_gc &&
+       run_and_wait_for_gc --auto &&
        ls .git/objects/pack/pack-*.pack >packs &&
        test_line_count = 1 packs
 '
@@ -391,11 +391,48 @@ test_expect_success 'background auto gc respects lock for all operations' '
        printf "%d %s" "$shell_pid" "$hostname" >.git/gc.pid &&
 
        # our gc should exit zero without doing anything
-       run_and_wait_for_auto_gc &&
+       run_and_wait_for_gc --auto &&
        (ls -1 .git/refs/heads .git/reftable >actual || true) &&
        test_cmp expect actual
 '
 
+test_expect_success '--detach overrides gc.autoDetach=false' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+
+               # Prepare the repository such that git-gc(1) ends up repacking.
+               test_commit "$(test_oid blob17_1)" &&
+               test_commit "$(test_oid blob17_2)" &&
+               git config gc.autodetach false &&
+               git config gc.auto 2 &&
+
+               # Note that we cannot use `test_cmp` here to compare stderr
+               # because it may contain output from `set -x`.
+               run_and_wait_for_gc --auto --detach 2>actual &&
+               test_grep "Auto packing the repository in background for optimum performance." actual
+       )
+'
+
+test_expect_success '--no-detach overrides gc.autoDetach=true' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+
+               # Prepare the repository such that git-gc(1) ends up repacking.
+               test_commit "$(test_oid blob17_1)" &&
+               test_commit "$(test_oid blob17_2)" &&
+               git config gc.autodetach true &&
+               git config gc.auto 2 &&
+
+               GIT_PROGRESS_DELAY=0 git gc --auto --no-detach 2>output &&
+               test_grep "Auto packing the repository for optimum performance." output &&
+               test_grep "Collecting referenced commits: 2, done." output
+       )
+'
+
 # DO NOT leave a detached auto gc process running near the end of the
 # test script: it can run long enough in the background to racily
 # interfere with the cleanup in 'test_done'.