gc: handle & check gc.reflogExpire config

Don't redundantly run "git reflog expire --all" when gc.reflogExpire and gc.reflogExpireUnreachable are set to "never", and die immediately if those configuration valuer are bad. As an earlier "assert lack of early exit" change to the tests for "git reflog expire" shows, an early check of gc.reflogExpire{Unreachable,} isn't wanted in general for "git reflog expire", but it makes sense for "gc" because: 1) Similarly to 8ab5aa4bd8 ("parseopt: handle malformed --expire arguments more nicely", 2018-04-21) we'll now die early if the config variables are set to invalid values. We run "pack-refs" before "reflog expire", which can take a while, only to then die on an invalid gc.reflogExpire{Unreachable,} configuration. 2) Not invoking the command at all means it won't show up in trace output, which makes what's going on more obvious when the two are set to "never". 3) As a later change documents we lock the refs when looping over the refs to expire, even in cases where we end up doing nothing due to this config. For the reasons noted in the earlier "assert lack of early exit" change I don't think it's worth it to bend over backwards in "git reflog expire" itself to carefully detect if we'll really do nothing given the combination of all its possible options and skip that locking, but that's easy to detect here in "gc" where we'll only run "reflog expire" in a relatively simple mode. Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ævar Arnfjörð Bjarmason committed Mar 28, 2019 at 17:14 UTC bf3d70fe93109616eb339e12dc9a7969b0bf025b
2 files changed +36
builtin/gc.c
+17
@@ -116,6 +116,19 @@ static void process_log_file_on_signal(int signo)
116 raise(signo);
117 }
118
119 +static int gc_config_is_timestamp_never(const char *var)
120 +{
121 + const char *value;
122 + timestamp_t expire;
123 +
124 + if (!git_config_get_value(var, &value) && value) {
125 + if (parse_expiry_date(value, &expire))
126 + die(_("failed to parse '%s' value '%s'"), var, value);
127 + return expire == 0;
128 + }
129 + return 0;
130 +}
131 +
132 static void gc_config(void)
133 {
134 const char *value;
@@ -127,6 +140,10 @@ static void gc_config(void)
140 pack_refs = git_config_bool("gc.packrefs", value);
141 }
142
143 + if (gc_config_is_timestamp_never("gc.reflogexpire") &&
144 + gc_config_is_timestamp_never("gc.reflogexpireunreachable"))
145 + prune_reflogs = 0;
146 +
147 git_config_get_int("gc.aggressivewindow", &aggressive_window);
148 git_config_get_int("gc.aggressivedepth", &aggressive_depth);
149 git_config_get_int("gc.auto", &gc_auto_threshold);
t/t6500-gc.sh
+19
@@ -120,6 +120,25 @@ test_expect_success 'gc --quiet' '
120 test_must_be_empty stderr
121 '
122
123 +test_expect_success 'gc.reflogExpire{Unreachable,}=never skips "expire" via "gc"' '
124 + test_config gc.reflogExpire never &&
125 + test_config gc.reflogExpireUnreachable never &&
126 +
127 + GIT_TRACE=$(pwd)/trace.out git gc &&
128 +
129 + # Check that git-pack-refs is run as a sanity check (done via
130 + # gc_before_repack()) but that git-expire is not.
131 + grep -E "^trace: (built-in|exec|run_command): git pack-refs --" trace.out &&
132 + ! grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out
133 +'
134 +
135 +test_expect_success 'one of gc.reflogExpire{Unreachable,}=never does not skip "expire" via "gc"' '
136 + >trace.out &&
137 + test_config gc.reflogExpire never &&
138 + GIT_TRACE=$(pwd)/trace.out git gc &&
139 + grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out
140 +'
141 +
142 run_and_wait_for_auto_gc () {
143 # We read stdout from gc for the side effect of waiting until the
144 # background gc process exits, closing its fd 9. Furthermore, the