Subject: [PATCH] gc --auto: warn garbage collection happens soon

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Tue, 27 Dec 2011 20:45:34 +0700

This gives users a chance to run gc explicitly elsewhere if they do not
want gc to run suddenly in current terminal.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 v2 of a patch posted a few months ago. The warning limits are in
 percentage and configurable. I could have set the default limits to
 100% (i.e. no warnings) to keep current behavior. However I think
 warning is better.

 May need rewording inn config.txt, I'm not sure I state it clearly.

 Documentation/config.txt |   12 ++++++++++++
 Documentation/git-gc.txt |    4 ++++
 builtin/gc.c             |   41 +++++++++++++++++++++++++++++++++++++++--
 3 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5a841da..c263496 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -965,12 +965,24 @@ gc.auto::
 	light-weight garbage collection from time to time.  The
 	default value is 6700.  Setting this to 0 disables it.
 
+gc.autowarn::
+	The percentage of loose objects specified in `gc.auto`. If the
+	number of loose objects exceeds this limit, `git gc --auto`
+	will warn users garbage collection will happen soon. Default
+	value is 90. Setting this to 100 disables it.
+
 gc.autopacklimit::
 	When there are more than this many packs that are not
 	marked with `*.keep` file in the repository, `git gc
 	--auto` consolidates them into one larger pack.  The
 	default	value is 50.  Setting this to 0 disables it.
 
+gc.autopackwarn::
+	The percentage of packs specified in `gc.autopacklimit`. If
+	the number of packs exceeds this limit, `git gc --auto` will
+	warn users garbage collection will happen soon. Default value
+	is 90. Setting this to 100 disables it.
+
 gc.packrefs::
 	Running `git pack-refs` in a repository renders it
 	unclonable by Git versions prior to 1.5.1.2 over dumb
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index 815afcb..937b3d6 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -59,6 +59,10 @@ then existing packs (except those marked with a `.keep` file)
 are consolidated into a single pack by using the `-A` option of
 'git repack'. Setting `gc.autopacklimit` to 0 disables
 automatic consolidation of packs.
++
+`git gc --auto` will warn users when the number of loose objects or
+packs is close to the limits. See `gc.autowarn` and `gc.autopackwarn`
+for details.
 
 --prune=<date>::
 	Prune loose objects older than date (default is 2 weeks ago,
diff --git a/builtin/gc.c b/builtin/gc.c
index 0498094..f3fa46d 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -28,6 +28,10 @@ static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
 static const char *prune_expire = "2.weeks.ago";
 
+/* numbers are in percent, to be converted to absolute later */
+static int gc_warn_auto_threshold = 90;
+static int gc_warn_auto_pack_limit = 90;
+
 #define MAX_ADD 10
 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
 static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
@@ -52,10 +56,26 @@ static int gc_config(const char *var, const char *value, void *cb)
 		gc_auto_threshold = git_config_int(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "gc.autowarn")) {
+		int percent = percent = git_config_int(var, value);
+		if (percent <= 0 || percent > 100)
+			die(_("gc.autowarn %d%% does not make sense"),
+			    percent);
+		gc_warn_auto_threshold = percent;
+		return 0;
+	}
 	if (!strcmp(var, "gc.autopacklimit")) {
 		gc_auto_pack_limit = git_config_int(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "gc.autopackwarn")) {
+		int percent = percent = git_config_int(var, value);
+		if (percent <= 0 || percent > 100)
+			die(_("gc.autopackwarn %d%% does not make sense"),
+			    percent);
+		gc_warn_auto_pack_limit = percent;
+		return 0;
+	}
 	if (!strcmp(var, "gc.pruneexpire")) {
 		if (value && strcmp(value, "now")) {
 			unsigned long now = approxidate("now");
@@ -118,7 +138,15 @@ static int too_many_loose_objects(void)
 		}
 	}
 	closedir(dir);
-	return needed;
+	if (needed)
+		return 1;
+
+	auto_threshold = (gc_warn_auto_threshold + 255) / 256;
+	if (num_loose >= auto_threshold)
+		warning(_("Too many loose objects (current approx. %d, limit %d).\n"
+			  "\"git gc\" will soon run automatically"),
+			num_loose * 256, gc_auto_threshold);
+	return 0;
 }
 
 static int too_many_packs(void)
@@ -141,7 +169,14 @@ static int too_many_packs(void)
 		 */
 		cnt++;
 	}
-	return gc_auto_pack_limit <= cnt;
+	if (gc_auto_pack_limit <= cnt)
+		return 1;
+
+	if (gc_warn_auto_pack_limit <= cnt)
+		warning(_("Too many packs (current %d, limit %d)\n"
+			  "\"git gc\" will soon run automatically."),
+			cnt, gc_auto_pack_limit);
+	return 0;
 }
 
 static int need_to_gc(void)
@@ -193,6 +228,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
 		usage_with_options(builtin_gc_usage, builtin_gc_options);
 
 	git_config(gc_config, NULL);
+	gc_warn_auto_threshold = 0.01 * gc_auto_threshold * gc_warn_auto_threshold;
+	gc_warn_auto_pack_limit = 0.01 * gc_auto_pack_limit * gc_auto_pack_limit;
 
 	if (pack_refs < 0)
 		pack_refs = !is_bare_repository();
-- 
1.7.8.36.g69ee2