git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 2/2] git-gc: skip stashes when expiring reflogs

From
Junio C Hamano <gitster@pobox.com>
Date
Jun 16, 2008, 07:21 UTC
Message-ID
<7vy755c0b2.fsf@gitster.siamese.dyndns.org>
In-Reply-To
<7vabhne15k.fsf@gitster.siamese.dyndns.org>
Junio C Hamano <gitster@pobox.com> writes:
Show 19 quoted lines
> I think the steps from here on would be:
>
>  - Apply the patch in your message I am responding to, so that a stash
>    that is kept forever will not pin the unnecessary history behind it in
>    the repository.  As you said there is no reason to make the base commit
>    (H) actually the same as the commit in the true history --- the only
>    thing we care about it is its tree object;
>
>  - Design and decide the way to tell git to make stash entries unexpirable
>    (or maybe have very long expiration period).  I am leaning toward a
>    configuration option that lets you specify expiration period per ref,
>    rather than marking individual reflog entries as I suggested earlier;
>
>  - Make the default for new repositories' stash reflog expiry period
>    "never", by setting the above configuration upon "git init".
>
> None of the above should obviously be in 1.5.6, but I think even the third
> step to the change the default would be acceptable in the next 1.6.0
> release.

So here is the second step from the above list. I did not write any test nor docs, but if people are so keen to see permanent stashes supported, I am reasonably sure that they will contribute by filling the gap even if I do not do anything further ;-)

Obviously this will _not_ come anywhere near 'master' nor 'next' until 1.5.6 ships.

-- >8 --
From: Junio C Hamano <gitster@pobox.com>
Date: Sun, 15 Jun 2008 23:48:46 -0700
Subject: [PATCH] Per-ref reflog expiry configuration

In addition to gc.reflogexpireunreachable and gc.reflogexpire, this lets you set gc.<pattern>.reflogexpireunreachable and gc.<pattern>.reflogexpire variables.

When "git reflog expire" expires reflog entry for $ref, the expiry timers are taken from the first <pattern> that matches $ref (and if there isn't the global default value is used).

For example, you could:
	[gc "refs/stash"]
		reflogexpire = never
		reflogexpireunreachable = never
	[gc "refs/remotes/*"]
		reflogexpire = 7 days
		reflogexpireunreachable = 3 days
	[gc]
		reflogexpire = 90 days
		reflogexpireunreachable = 30 days
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin-reflog.c |  145 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 126 insertions(+), 19 deletions(-)
diff --git a/builtin-reflog.c b/builtin-reflog.c
index b151e24..eec14c7 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -269,7 +269,9 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
 	int status = 0;
 
 	memset(&cb, 0, sizeof(cb));
-	/* we take the lock for the ref itself to prevent it from
+
+	/*
+	 * we take the lock for the ref itself to prevent it from
 	 * getting updated.
 	 */
 	lock = lock_any_ref_for_update(ref, sha1, 0);
@@ -331,21 +333,119 @@ static int collect_reflog(const char *ref, const unsigned char *sha1, int unused
 	return 0;
 }
 
-static int reflog_expire_config(const char *var, const char *value, void *cb)
+static struct reflog_expire_cfg {
+	struct reflog_expire_cfg *next;
+	unsigned long expire_total;
+	unsigned long expire_unreachable;
+	size_t len;
+	char pattern[FLEX_ARRAY];
+} *reflog_expire_cfg, **reflog_expire_cfg_tail;
+
+static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
 {
-	if (!strcmp(var, "gc.reflogexpire")) {
-		if (!value)
-			config_error_nonbool(var);
-		default_reflog_expire = approxidate(value);
+	struct reflog_expire_cfg *ent;
+
+	if (!reflog_expire_cfg_tail)
+		reflog_expire_cfg_tail = &reflog_expire_cfg;
+
+	for (ent = reflog_expire_cfg; ent; ent = ent->next)
+		if (ent->len == len &&
+		    !memcmp(ent->pattern, pattern, len))
+			return ent;
+
+	ent = xcalloc(1, (sizeof(*ent) + len));
+	memcpy(ent->pattern, pattern, len);
+	ent->len = len;
+	*reflog_expire_cfg_tail = ent;
+	reflog_expire_cfg_tail = &(ent->next);
+	return ent;
+}
+
+static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
+{
+	if (!value)
+		return config_error_nonbool(var);
+	if (!strcmp(value, "never") || !strcmp(value, "false")) {
+		*expire = 0;
 		return 0;
 	}
-	if (!strcmp(var, "gc.reflogexpireunreachable")) {
-		if (!value)
-			config_error_nonbool(var);
-		default_reflog_expire_unreachable = approxidate(value);
+	*expire = approxidate(value);
+	return 0;
+}
+
+/* expiry timer slot */
+#define EXPIRE_TOTAL   01
+#define EXPIRE_UNREACH 02
+
+static int reflog_expire_config(const char *var, const char *value, void *cb)
+{
+	const char *lastdot = strrchr(var, '.');
+	unsigned long expire;
+	int slot;
+	struct reflog_expire_cfg *ent;
+
+	if (!lastdot || prefixcmp(var, "gc."))
+		return git_default_config(var, value, cb);
+
+	if (!strcmp(lastdot, ".reflogexpire")) {
+		slot = EXPIRE_TOTAL;
+		if (parse_expire_cfg_value(var, value, &expire))
+			return -1;
+	} else if (!strcmp(lastdot, ".reflogexpireunreachable")) {
+		slot = EXPIRE_UNREACH;
+		if (parse_expire_cfg_value(var, value, &expire))
+			return -1;
+	} else
+		return git_default_config(var, value, cb);
+
+	if (lastdot == var + 2) {
+		switch (slot) {
+		case EXPIRE_TOTAL:
+			default_reflog_expire = expire;
+			break;
+		case EXPIRE_UNREACH:
+			default_reflog_expire_unreachable = expire;
+			break;
+		}
 		return 0;
 	}
-	return git_default_config(var, value, cb);
+
+	ent = find_cfg_ent(var + 3, lastdot - (var+3));
+	if (!ent)
+		return -1;
+	switch (slot) {
+	case EXPIRE_TOTAL:
+		ent->expire_total = expire;
+		break;
+	case EXPIRE_UNREACH:
+		ent->expire_unreachable = expire;
+		break;
+	}
+	return 0;
+}
+
+static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
+{
+	struct reflog_expire_cfg *ent;
+
+	if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
+		return; /* both given explicitly -- nothing to tweak */
+
+	for (ent = reflog_expire_cfg; ent; ent = ent->next) {
+		if (!fnmatch(ent->pattern, ref, 0)) {
+			if (!(slot & EXPIRE_TOTAL))
+				cb->expire_total = ent->expire_total;
+			if (!(slot & EXPIRE_UNREACH))
+				cb->expire_unreachable = ent->expire_unreachable;
+			return;
+		}
+	}
+
+	/* Nothing matched -- set the default value */
+	if (!(slot & EXPIRE_TOTAL))
+		cb->expire_total = default_reflog_expire;
+	if (!(slot & EXPIRE_UNREACH))
+		cb->expire_unreachable = default_reflog_expire_unreachable;
 }
 
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
@@ -353,6 +453,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 	struct cmd_reflog_expire_cb cb;
 	unsigned long now = time(NULL);
 	int i, status, do_all;
+	int explicit_expiry = 0;
 
 	git_config(reflog_expire_config, NULL);
 
@@ -367,20 +468,18 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 	cb.expire_total = default_reflog_expire;
 	cb.expire_unreachable = default_reflog_expire_unreachable;
 
-	/*
-	 * We can trust the commits and objects reachable from refs
-	 * even in older repository.  We cannot trust what's reachable
-	 * from reflog if the repository was pruned with older git.
-	 */
-
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 		if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
 			cb.dry_run = 1;
-		else if (!prefixcmp(arg, "--expire="))
+		else if (!prefixcmp(arg, "--expire=")) {
 			cb.expire_total = approxidate(arg + 9);
-		else if (!prefixcmp(arg, "--expire-unreachable="))
+			explicit_expiry |= EXPIRE_TOTAL;
+		}
+		else if (!prefixcmp(arg, "--expire-unreachable=")) {
 			cb.expire_unreachable = approxidate(arg + 21);
+			explicit_expiry |= EXPIRE_UNREACH;
+		}
 		else if (!strcmp(arg, "--stale-fix"))
 			cb.stalefix = 1;
 		else if (!strcmp(arg, "--rewrite"))
@@ -400,6 +499,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 		else
 			break;
 	}
+
+	/*
+	 * We can trust the commits and objects reachable from refs
+	 * even in older repository.  We cannot trust what's reachable
+	 * from reflog if the repository was pruned with older git.
+	 */
 	if (cb.stalefix) {
 		init_revisions(&cb.revs, prefix);
 		if (cb.verbose)
@@ -417,6 +522,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 		for_each_reflog(collect_reflog, &collected);
 		for (i = 0; i < collected.nr; i++) {
 			struct collected_reflog *e = collected.e[i];
+			set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
 			status |= expire_reflog(e->reflog, e->sha1, 0, &cb);
 			free(e);
 		}
@@ -430,6 +536,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 			status |= error("%s points nowhere!", ref);
 			continue;
 		}
+		set_reflog_expiry_param(&cb, explicit_expiry, ref);
 		status |= expire_reflog(ref, sha1, 0, &cb);
 	}
 	return status;
-- 
1.5.6.rc3.7.g336d0
Previous: Eric RaibleNext: Johannes Schindelin
Message 22 of 69 in “git-gc: skip stashes when expiring reflogs”
  1. 2/2 git-gc: skip stashes when expiring reflogsBrandon Casey, Jun 11, 2008
  2. Mike HommeyJun 11, 2008
  3. Johannes SchindelinJun 11, 2008
  4. Jeff KingJun 11, 2008
  5. Nicolas PitreJun 11, 2008
  6. Eric RaibleJun 12, 2008
  7. Wincent ColaiutaJun 12, 2008
  8. Nicolas PitreJun 12, 2008
  9. Junio C HamanoJun 12, 2008
  10. Eric RaibleJun 12, 2008
  11. Junio C HamanoJun 12, 2008
  12. Eric RaibleJun 12, 2008
  13. Johannes SchindelinJun 13, 2008
  14. Wincent ColaiutaJun 13, 2008
  15. Jeff KingJun 13, 2008
  16. Johannes SchindelinJun 13, 2008
  17. Christian JaegerJun 13, 2008
  18. Wincent ColaiutaJun 14, 2008
  19. しらいしななこJun 14, 2008
  20. Junio C HamanoJun 15, 2008
  21. Eric RaibleJun 16, 2008
  22. Junio C HamanoJun 16, 2008
  23. Johannes SchindelinJun 17, 2008
  24. Junio C HamanoJun 17, 2008
  25. Johannes SchindelinJun 18, 2008
  26. Junio C HamanoJun 18, 2008
  27. Brandon CaseyJun 16, 2008
  28. Jakub NarebskiJun 16, 2008
  29. Mikael MagnussonJun 13, 2008
  30. Brandon CaseyJun 12, 2008
  31. Junio C HamanoJun 12, 2008
  32. Brandon CaseyJun 12, 2008
  33. しらいしななこJun 13, 2008
  34. Andreas EricssonJun 13, 2008
  35. Jeff KingJun 13, 2008
  36. Andreas EricssonJun 13, 2008
  37. Jeff KingJun 13, 2008
  38. Andreas EricssonJun 13, 2008
  39. Jakub NarebskiJun 13, 2008
  40. Sverre RabbelierJun 13, 2008
  41. Jeff KingJun 13, 2008
  42. Miles BaderJun 13, 2008
  43. Junio C HamanoJun 13, 2008
  44. Jakub NarebskiJun 13, 2008
  45. Sverre RabbelierJun 13, 2008
  46. Olivier MarinJun 13, 2008
  47. Junio C HamanoJun 13, 2008
  48. Wincent ColaiutaJun 13, 2008
  49. Brandon CaseyJun 13, 2008
  50. Olivier MarinJun 13, 2008
  51. しらいしななこJun 14, 2008
  52. Wincent ColaiutaJun 13, 2008
  53. Jeff KingJun 13, 2008
  54. Olivier MarinJun 13, 2008
  55. Jon LoeligerJun 13, 2008
  56. Brandon CaseyJun 13, 2008
  57. Brandon CaseyJun 11, 2008
  58. Jeff KingJun 12, 2008
  59. Brandon CaseyJun 12, 2008
  60. Jeff KingJun 13, 2008
  61. Wincent ColaiutaJun 13, 2008
  62. Sverre RabbelierJun 13, 2008
  63. Teemu LikonenJun 13, 2008
  64. Jeff KingJun 13, 2008
  65. Miles BaderJun 13, 2008
  66. Brandon CaseyJun 13, 2008
  67. Jeff KingJun 13, 2008
  68. Brandon CaseyJun 11, 2008
  69. Johannes SchindelinJun 15, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.