gc: save log from daemonized gc --auto and print it next time

While commit 9f673f9 (gc: config option for running --auto in background - 2014-02-08) helps reduce some complaints about 'gc --auto' hogging the terminal, it creates another set of problems. The latest in this set is, as the result of daemonizing, stderr is closed and all warnings are lost. This warning at the end of cmd_gc() is particularly important because it tells the user how to avoid "gc --auto" running repeatedly. Because stderr is closed, the user does not know, naturally they complain about 'gc --auto' wasting CPU. Daemonized gc now saves stderr to $GIT_DIR/gc.log. Following gc --auto will not run and gc.log printed out until the user removes gc.log. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Sep 19, 2015 at 12:13 UTC 329e6e8794c347d3da92144f88ad838945508ac6
1 file changed +55 -1
builtin/gc.c
+55 -1
@@ -41,6 +41,7 @@ static struct argv_array prune = ARGV_ARRAY_INIT;
41 static struct argv_array rerere = ARGV_ARRAY_INIT;
42
43 static char *pidfile;
44 +static struct lock_file log_lock;
45
46 static void remove_pidfile(void)
47 {
@@ -55,6 +56,28 @@ static void remove_pidfile_on_signal(int signo)
56 raise(signo);
57 }
58
59 +static void process_log_file(void)
60 +{
61 + struct stat st;
62 + if (!fstat(log_lock.fd, &st) && st.st_size)
63 + commit_lock_file(&log_lock);
64 + else
65 + rollback_lock_file(&log_lock);
66 +}
67 +
68 +static void process_log_file_at_exit(void)
69 +{
70 + fflush(stderr);
71 + process_log_file();
72 +}
73 +
74 +static void process_log_file_on_signal(int signo)
75 +{
76 + process_log_file();
77 + sigchain_pop(signo);
78 + raise(signo);
79 +}
80 +
81 static void gc_config(void)
82 {
83 const char *value;
@@ -248,6 +271,24 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
271 return NULL;
272 }
273
274 +static int report_last_gc_error(void)
275 +{
276 + struct strbuf sb = STRBUF_INIT;
277 + int ret;
278 +
279 + ret = strbuf_read_file(&sb, git_path("gc.log"), 0);
280 + if (ret > 0)
281 + return error(_("The last gc run reported the following. "
282 + "Please correct the root cause\n"
283 + "and remove %s.\n"
284 + "Automatic cleanup will not be performed "
285 + "until the file is removed.\n\n"
286 + "%s"),
287 + git_path("gc.log"), sb.buf);
288 + strbuf_release(&sb);
289 + return 0;
290 +}
291 +
292 static int gc_before_repack(void)
293 {
294 if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
@@ -269,6 +310,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
310 int force = 0;
311 const char *name;
312 pid_t pid;
313 + int daemonized = 0;
314
315 struct option builtin_gc_options[] = {
316 OPT__QUIET(&quiet, N_("suppress progress reporting")),
@@ -324,13 +366,16 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
366 fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
367 }
368 if (detach_auto) {
369 + if (report_last_gc_error())
370 + return -1;
371 +
372 if (gc_before_repack())
373 return -1;
374 /*
375 * failure to daemonize is ok, we'll continue
376 * in foreground
377 */
333 - daemonize();
378 + daemonized = !daemonize();
379 }
380 } else
381 add_repack_all_option();
@@ -343,6 +388,15 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
388 name, (uintmax_t)pid);
389 }
390
391 + if (daemonized) {
392 + hold_lock_file_for_update(&log_lock,
393 + git_path("gc.log"),
394 + LOCK_DIE_ON_ERROR);
395 + dup2(log_lock.fd, 2);
396 + sigchain_push_common(process_log_file_on_signal);
397 + atexit(process_log_file_at_exit);
398 + }
399 +
400 if (gc_before_repack())
401 return -1;
402