Skip to content

Commit c7185df

Browse files
pks-tgitster
authored andcommitted
builtin/gc: add a --detach flag
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9b6b994 commit c7185df

File tree

3 files changed

+84
-36
lines changed

3 files changed

+84
-36
lines changed

Documentation/git-gc.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] [--force] [--keep-largest-pack]
12+
'git gc' [--aggressive] [--auto] [--[no-]detach] [--quiet] [--prune=<date> | --no-prune] [--force] [--keep-largest-pack]
1313

1414
DESCRIPTION
1515
-----------
@@ -53,6 +53,9 @@ configuration options such as `gc.auto` and `gc.autoPackLimit`, all
5353
other housekeeping tasks (e.g. rerere, working trees, reflog...) will
5454
be performed as well.
5555

56+
--[no-]detach::
57+
Run in the background if the system supports it. This option overrides
58+
the `gc.autoDetach` config.
5659

5760
--[no-]cruft::
5861
When expiring unreachable objects, pack them separately into a

builtin/gc.c

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,13 @@ static enum schedule_priority parse_schedule(const char *value)
242242

243243
struct maintenance_run_opts {
244244
int auto_flag;
245+
int detach;
245246
int quiet;
246247
enum schedule_priority schedule;
247248
};
249+
#define MAINTENANCE_RUN_OPTS_INIT { \
250+
.detach = -1, \
251+
}
248252

249253
static int pack_refs_condition(UNUSED struct gc_config *cfg)
250254
{
@@ -664,7 +668,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
664668
int keep_largest_pack = -1;
665669
timestamp_t dummy;
666670
struct child_process rerere_cmd = CHILD_PROCESS_INIT;
667-
struct maintenance_run_opts opts = {0};
671+
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
668672
struct gc_config cfg = GC_CONFIG_INIT;
669673
const char *prune_expire_sentinel = "sentinel";
670674
const char *prune_expire_arg = prune_expire_sentinel;
@@ -681,6 +685,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
681685
OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
682686
OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"),
683687
PARSE_OPT_NOCOMPLETE),
688+
OPT_BOOL(0, "detach", &opts.detach,
689+
N_("perform garbage collection in the background")),
684690
OPT_BOOL_F(0, "force", &force,
685691
N_("force running gc even if there may be another gc running"),
686692
PARSE_OPT_NOCOMPLETE),
@@ -729,6 +735,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
729735
strvec_push(&repack, "-q");
730736

731737
if (opts.auto_flag) {
738+
if (cfg.detach_auto && opts.detach < 0)
739+
opts.detach = 1;
740+
732741
/*
733742
* Auto-gc should be least intrusive as possible.
734743
*/
@@ -738,38 +747,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
738747
}
739748

740749
if (!quiet) {
741-
if (cfg.detach_auto)
750+
if (opts.detach > 0)
742751
fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n"));
743752
else
744753
fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
745754
fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
746755
}
747-
if (cfg.detach_auto) {
748-
ret = report_last_gc_error();
749-
if (ret == 1) {
750-
/* Last gc --auto failed. Skip this one. */
751-
ret = 0;
752-
goto out;
753-
754-
} else if (ret) {
755-
/* an I/O error occurred, already reported */
756-
goto out;
757-
}
758-
759-
if (lock_repo_for_gc(force, &pid)) {
760-
ret = 0;
761-
goto out;
762-
}
763-
764-
gc_before_repack(&opts, &cfg); /* dies on failure */
765-
delete_tempfile(&pidfile);
766-
767-
/*
768-
* failure to daemonize is ok, we'll continue
769-
* in foreground
770-
*/
771-
daemonized = !daemonize();
772-
}
773756
} else {
774757
struct string_list keep_pack = STRING_LIST_INIT_NODUP;
775758

@@ -784,6 +767,33 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
784767
string_list_clear(&keep_pack, 0);
785768
}
786769

770+
if (opts.detach > 0) {
771+
ret = report_last_gc_error();
772+
if (ret == 1) {
773+
/* Last gc --auto failed. Skip this one. */
774+
ret = 0;
775+
goto out;
776+
777+
} else if (ret) {
778+
/* an I/O error occurred, already reported */
779+
goto out;
780+
}
781+
782+
if (lock_repo_for_gc(force, &pid)) {
783+
ret = 0;
784+
goto out;
785+
}
786+
787+
gc_before_repack(&opts, &cfg); /* dies on failure */
788+
delete_tempfile(&pidfile);
789+
790+
/*
791+
* failure to daemonize is ok, we'll continue
792+
* in foreground
793+
*/
794+
daemonized = !daemonize();
795+
}
796+
787797
name = lock_repo_for_gc(force, &pid);
788798
if (name) {
789799
if (opts.auto_flag) {
@@ -1537,7 +1547,7 @@ static int task_option_parse(const struct option *opt UNUSED,
15371547
static int maintenance_run(int argc, const char **argv, const char *prefix)
15381548
{
15391549
int i;
1540-
struct maintenance_run_opts opts;
1550+
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
15411551
struct gc_config cfg = GC_CONFIG_INIT;
15421552
struct option builtin_maintenance_run_options[] = {
15431553
OPT_BOOL(0, "auto", &opts.auto_flag,
@@ -1554,8 +1564,6 @@ static int maintenance_run(int argc, const char **argv, const char *prefix)
15541564
};
15551565
int ret;
15561566

1557-
memset(&opts, 0, sizeof(opts));
1558-
15591567
opts.quiet = !isatty(2);
15601568

15611569
for (i = 0; i < TASK__COUNT; i++)

t/t6500-gc.sh

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,14 +338,14 @@ test_expect_success 'gc.maxCruftSize sets appropriate repack options' '
338338
test_subcommand $cruft_max_size_opts --max-cruft-size=3145728 <trace2.txt
339339
'
340340

341-
run_and_wait_for_auto_gc () {
341+
run_and_wait_for_gc () {
342342
# We read stdout from gc for the side effect of waiting until the
343343
# background gc process exits, closing its fd 9. Furthermore, the
344344
# variable assignment from a command substitution preserves the
345345
# exit status of the main gc process.
346346
# Note: this fd trickery doesn't work on Windows, but there is no
347347
# need to, because on Win the auto gc always runs in the foreground.
348-
doesnt_matter=$(git gc --auto 9>&1)
348+
doesnt_matter=$(git gc "$@" 9>&1)
349349
}
350350

351351
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
361361
test-tool chmtime =-345600 .git/gc.log &&
362362
git gc --auto &&
363363
test_config gc.logexpiry 2.days &&
364-
run_and_wait_for_auto_gc &&
364+
run_and_wait_for_gc --auto &&
365365
ls .git/objects/pack/pack-*.pack >packs &&
366366
test_line_count = 1 packs
367367
'
@@ -391,11 +391,48 @@ test_expect_success 'background auto gc respects lock for all operations' '
391391
printf "%d %s" "$shell_pid" "$hostname" >.git/gc.pid &&
392392
393393
# our gc should exit zero without doing anything
394-
run_and_wait_for_auto_gc &&
394+
run_and_wait_for_gc --auto &&
395395
(ls -1 .git/refs/heads .git/reftable >actual || true) &&
396396
test_cmp expect actual
397397
'
398398

399+
test_expect_success '--detach overrides gc.autoDetach=false' '
400+
test_when_finished "rm -rf repo" &&
401+
git init repo &&
402+
(
403+
cd repo &&
404+
405+
# Prepare the repository such that git-gc(1) ends up repacking.
406+
test_commit "$(test_oid blob17_1)" &&
407+
test_commit "$(test_oid blob17_2)" &&
408+
git config gc.autodetach false &&
409+
git config gc.auto 2 &&
410+
411+
# Note that we cannot use `test_cmp` here to compare stderr
412+
# because it may contain output from `set -x`.
413+
run_and_wait_for_gc --auto --detach 2>actual &&
414+
test_grep "Auto packing the repository in background for optimum performance." actual
415+
)
416+
'
417+
418+
test_expect_success '--no-detach overrides gc.autoDetach=true' '
419+
test_when_finished "rm -rf repo" &&
420+
git init repo &&
421+
(
422+
cd repo &&
423+
424+
# Prepare the repository such that git-gc(1) ends up repacking.
425+
test_commit "$(test_oid blob17_1)" &&
426+
test_commit "$(test_oid blob17_2)" &&
427+
git config gc.autodetach true &&
428+
git config gc.auto 2 &&
429+
430+
GIT_PROGRESS_DELAY=0 git gc --auto --no-detach 2>output &&
431+
test_grep "Auto packing the repository for optimum performance." output &&
432+
test_grep "Collecting referenced commits: 2, done." output
433+
)
434+
'
435+
399436
# DO NOT leave a detached auto gc process running near the end of the
400437
# test script: it can run long enough in the background to racily
401438
# interfere with the cleanup in 'test_done'.

0 commit comments

Comments
 (0)