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 <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
committed by
Junio C Hamano
parent
9b6b994f90
commit
c7185df01b
@@ -9,7 +9,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
|
|||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
[verse]
|
[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
|
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
|
other housekeeping tasks (e.g. rerere, working trees, reflog...) will
|
||||||
be performed as well.
|
be performed as well.
|
||||||
|
|
||||||
|
--[no-]detach::
|
||||||
|
Run in the background if the system supports it. This option overrides
|
||||||
|
the `gc.autoDetach` config.
|
||||||
|
|
||||||
--[no-]cruft::
|
--[no-]cruft::
|
||||||
When expiring unreachable objects, pack them separately into a
|
When expiring unreachable objects, pack them separately into a
|
||||||
|
|||||||
70
builtin/gc.c
70
builtin/gc.c
@@ -242,9 +242,13 @@ static enum schedule_priority parse_schedule(const char *value)
|
|||||||
|
|
||||||
struct maintenance_run_opts {
|
struct maintenance_run_opts {
|
||||||
int auto_flag;
|
int auto_flag;
|
||||||
|
int detach;
|
||||||
int quiet;
|
int quiet;
|
||||||
enum schedule_priority schedule;
|
enum schedule_priority schedule;
|
||||||
};
|
};
|
||||||
|
#define MAINTENANCE_RUN_OPTS_INIT { \
|
||||||
|
.detach = -1, \
|
||||||
|
}
|
||||||
|
|
||||||
static int pack_refs_condition(UNUSED struct gc_config *cfg)
|
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;
|
int keep_largest_pack = -1;
|
||||||
timestamp_t dummy;
|
timestamp_t dummy;
|
||||||
struct child_process rerere_cmd = CHILD_PROCESS_INIT;
|
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;
|
struct gc_config cfg = GC_CONFIG_INIT;
|
||||||
const char *prune_expire_sentinel = "sentinel";
|
const char *prune_expire_sentinel = "sentinel";
|
||||||
const char *prune_expire_arg = prune_expire_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(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
|
||||||
OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"),
|
OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"),
|
||||||
PARSE_OPT_NOCOMPLETE),
|
PARSE_OPT_NOCOMPLETE),
|
||||||
|
OPT_BOOL(0, "detach", &opts.detach,
|
||||||
|
N_("perform garbage collection in the background")),
|
||||||
OPT_BOOL_F(0, "force", &force,
|
OPT_BOOL_F(0, "force", &force,
|
||||||
N_("force running gc even if there may be another gc running"),
|
N_("force running gc even if there may be another gc running"),
|
||||||
PARSE_OPT_NOCOMPLETE),
|
PARSE_OPT_NOCOMPLETE),
|
||||||
@@ -729,6 +735,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
|
|||||||
strvec_push(&repack, "-q");
|
strvec_push(&repack, "-q");
|
||||||
|
|
||||||
if (opts.auto_flag) {
|
if (opts.auto_flag) {
|
||||||
|
if (cfg.detach_auto && opts.detach < 0)
|
||||||
|
opts.detach = 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Auto-gc should be least intrusive as possible.
|
* 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 (!quiet) {
|
||||||
if (cfg.detach_auto)
|
if (opts.detach > 0)
|
||||||
fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n"));
|
fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n"));
|
||||||
else
|
else
|
||||||
fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
|
fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
|
||||||
fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\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 {
|
} else {
|
||||||
struct string_list keep_pack = STRING_LIST_INIT_NODUP;
|
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);
|
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);
|
name = lock_repo_for_gc(force, &pid);
|
||||||
if (name) {
|
if (name) {
|
||||||
if (opts.auto_flag) {
|
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)
|
static int maintenance_run(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct maintenance_run_opts opts;
|
struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT;
|
||||||
struct gc_config cfg = GC_CONFIG_INIT;
|
struct gc_config cfg = GC_CONFIG_INIT;
|
||||||
struct option builtin_maintenance_run_options[] = {
|
struct option builtin_maintenance_run_options[] = {
|
||||||
OPT_BOOL(0, "auto", &opts.auto_flag,
|
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;
|
int ret;
|
||||||
|
|
||||||
memset(&opts, 0, sizeof(opts));
|
|
||||||
|
|
||||||
opts.quiet = !isatty(2);
|
opts.quiet = !isatty(2);
|
||||||
|
|
||||||
for (i = 0; i < TASK__COUNT; i++)
|
for (i = 0; i < TASK__COUNT; i++)
|
||||||
|
|||||||
@@ -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
|
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
|
# We read stdout from gc for the side effect of waiting until the
|
||||||
# background gc process exits, closing its fd 9. Furthermore, the
|
# background gc process exits, closing its fd 9. Furthermore, the
|
||||||
# variable assignment from a command substitution preserves the
|
# variable assignment from a command substitution preserves the
|
||||||
# exit status of the main gc process.
|
# exit status of the main gc process.
|
||||||
# Note: this fd trickery doesn't work on Windows, but there is no
|
# 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.
|
# 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' '
|
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 &&
|
test-tool chmtime =-345600 .git/gc.log &&
|
||||||
git gc --auto &&
|
git gc --auto &&
|
||||||
test_config gc.logexpiry 2.days &&
|
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 &&
|
ls .git/objects/pack/pack-*.pack >packs &&
|
||||||
test_line_count = 1 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 &&
|
printf "%d %s" "$shell_pid" "$hostname" >.git/gc.pid &&
|
||||||
|
|
||||||
# our gc should exit zero without doing anything
|
# 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) &&
|
(ls -1 .git/refs/heads .git/reftable >actual || true) &&
|
||||||
test_cmp expect actual
|
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
|
# 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
|
# test script: it can run long enough in the background to racily
|
||||||
# interfere with the cleanup in 'test_done'.
|
# interfere with the cleanup in 'test_done'.
|
||||||
|
|||||||
Reference in New Issue
Block a user