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

[PATCH 5/7] refs.c: Refactor code for shortening full refnames into shorthand names

From
Johan Herland <johan@herland.net>
Date
May 4, 2013, 23:55 UTC
Message-ID
<1367711749-8812-6-git-send-email-johan@herland.net>
In-Reply-To
<1367711749-8812-1-git-send-email-johan@herland.net>

shorten_unambiguous_ref() provides the reverse functionality to the expansion of shorthand names into full refnames, i.e. it takes a full refname (like "refs/heads/master"), and matches it against a pattern (like "refs/heads/%.*s") to produce a shorthand name (like "master").

Being the last remaining user of ref_rev_parse_rules list, this patch converts it to use ref_expand_rules instead. However, as we associated an expansion function with each expansion rule - to allow for alternative expansion behavior in the future - we also need to associate a "shortening" function with each rule, to allow for the reverse operation to be customized as well.

Therefore, we add a "shorten" function to struct ref_expand_rule, to encode the shortening of a full refname into a shorthand name. The relevant rule and the full refname is passed as arguments, and the resulting shorthand name is returned as an allocated string. If the refname could not be shortened according to the given rule, NULL is returned.

The reason for moving the allocation of the shorthand name into the shortening function, is that one assumes the shortening function itself will best know exactly how much memory is needed to hold the shorthand string.

Naturally, we provide a shortening function that encodes the current textual shortening algorithm - called ref_shorten_txtly() - which is merely a slight refactoring of the former shorten_ref() function.

This patch removes the only remaining user of ref_rev_parse_rules. It has now been fully replaced by ref_expand_rules. Hence this patch also removes ref_rev_parse_rules.

Signed-off-by: Johan Herland <johan@herland.net>
---
 refs.c | 110 ++++++++++++++++++++++++++++-------------------------------------
 refs.h |   2 ++
 2 files changed, 50 insertions(+), 62 deletions(-)
diff --git a/refs.c b/refs.c
index 8b02140..a866489 100644
--- a/refs.c
+++ b/refs.c
@@ -1731,24 +1731,41 @@ static void ref_expand_txtly(const struct ref_expand_rule *rule,
 	mksnpath(dst, dst_len, rule->pattern, shortname_len, shortname);
 }
 
-const struct ref_expand_rule ref_expand_rules[] = {
-	{ ref_expand_txtly, "%.*s" },
-	{ ref_expand_txtly, "refs/%.*s" },
-	{ ref_expand_txtly, "refs/tags/%.*s" },
-	{ ref_expand_txtly, "refs/heads/%.*s" },
-	{ ref_expand_txtly, "refs/remotes/%.*s" },
-	{ ref_expand_txtly, "refs/remotes/%.*s/HEAD" },
-	{ NULL, NULL }
-};
+static char *ref_shorten_txtly(const struct ref_expand_rule *rule,
+			       const char *refname)
+{
+	/*
+	 * rule->pattern must be of the form "[pre]%.*s[post]". Check if
+	 * refname starts with "[pre]" and ends with "[post]". If so,
+	 * extract the middle part into a newly-allocated buffer, and
+	 * return it. Else - if refname does not match rule->pattern -
+	 * return NULL.
+	 */
+	size_t pre_len, post_start, post_len, match_len;
+	size_t ref_len = strlen(refname);
+	char *sep = strstr(rule->pattern, "%.*s");
+	if (!sep || strstr(sep + 4, "%.*s"))
+		die("invalid pattern in ref_rev_parse_rules_alt: %s", rule->pattern);
+	pre_len = sep - rule->pattern;
+	post_start = pre_len + 4;
+	post_len = strlen(rule->pattern + post_start);
+	if (pre_len + post_len >= ref_len)
+		return NULL; /* refname too short */
+	match_len = ref_len - (pre_len + post_len);
+	if (strncmp(refname, rule->pattern, pre_len) ||
+	    strncmp(refname + ref_len - post_len, rule->pattern + post_start, post_len))
+		return NULL; /* refname does not match */
+	return xstrndup(refname + pre_len, match_len);
+}
 
-static const char *ref_rev_parse_rules[] = {
-	"%.*s",
-	"refs/%.*s",
-	"refs/tags/%.*s",
-	"refs/heads/%.*s",
-	"refs/remotes/%.*s",
-	"refs/remotes/%.*s/HEAD",
-	NULL
+const struct ref_expand_rule ref_expand_rules[] = {
+	{ ref_expand_txtly, NULL, "%.*s" },
+	{ ref_expand_txtly, ref_shorten_txtly, "refs/%.*s" },
+	{ ref_expand_txtly, ref_shorten_txtly, "refs/tags/%.*s" },
+	{ ref_expand_txtly, ref_shorten_txtly, "refs/heads/%.*s" },
+	{ ref_expand_txtly, ref_shorten_txtly, "refs/remotes/%.*s" },
+	{ ref_expand_txtly, ref_shorten_txtly, "refs/remotes/%.*s/HEAD" },
+	{ NULL, NULL, NULL }
 };
 
 int refname_match(const char *abbrev_name, const char *full_name,
@@ -2965,68 +2982,35 @@ struct ref *find_ref_by_name(const struct ref *list, const char *name)
 	return NULL;
 }
 
-int shorten_ref(const char *refname, const char *pattern, char *short_name)
-{
-	/*
-	 * pattern must be of the form "[pre]%.*s[post]". Check if refname
-	 * starts with "[pre]" and ends with "[post]". If so, write the
-	 * middle part into short_name, and return the number of chars
-	 * written (not counting the added NUL-terminator). Otherwise,
-	 * if refname does not match pattern, return 0.
-	 */
-	size_t pre_len, post_start, post_len, match_len;
-	size_t ref_len = strlen(refname);
-	char *sep = strstr(pattern, "%.*s");
-	if (!sep || strstr(sep + 4, "%.*s"))
-		die("invalid pattern in ref_rev_parse_rules: %s", pattern);
-	pre_len = sep - pattern;
-	post_start = pre_len + 4;
-	post_len = strlen(pattern + post_start);
-	if (pre_len + post_len >= ref_len)
-		return 0; /* refname too short */
-	match_len = ref_len - (pre_len + post_len);
-	if (strncmp(refname, pattern, pre_len) ||
-	    strncmp(refname + ref_len - post_len, pattern + post_start, post_len))
-		return 0; /* refname does not match */
-	memcpy(short_name, refname + pre_len, match_len);
-	short_name[match_len] = '\0';
-	return match_len;
-}
-
 char *shorten_unambiguous_ref(const char *refname, int strict)
 {
 	int i;
 	char *short_name;
 
-	/* buffer for scanf result, at most refname must fit */
-	short_name = xstrdup(refname);
-
-	/* skip first rule, it will always match */
-	for (i = ARRAY_SIZE(ref_rev_parse_rules) - 1; i > 0 ; --i) {
+	for (i = ARRAY_SIZE(ref_expand_rules) - 1; i >= 0 ; --i) {
 		int j;
 		int rules_to_fail = i;
 		int short_name_len;
+		const struct ref_expand_rule *p = ref_expand_rules + i;
 
-		if (!ref_rev_parse_rules[i] ||
-		    !(short_name_len = shorten_ref(refname,
-						   ref_rev_parse_rules[i],
-						   short_name)))
+		if (!p->shorten || !(short_name = p->shorten(p, refname)))
 			continue;
+		short_name_len = strlen(short_name);
 
 		/*
 		 * in strict mode, all (except the matched one) rules
 		 * must fail to resolve to a valid non-ambiguous ref
 		 */
 		if (strict)
-			rules_to_fail = ARRAY_SIZE(ref_rev_parse_rules);
+			rules_to_fail = ARRAY_SIZE(ref_expand_rules);
 
 		/*
 		 * check if the short name resolves to a valid ref,
 		 * but use only rules prior to the matched one
 		 */
 		for (j = 0; j < rules_to_fail; j++) {
-			const char *rule = ref_rev_parse_rules[j];
-			char refname[PATH_MAX];
+			const struct ref_expand_rule *q = ref_expand_rules + j;
+			char resolved[PATH_MAX];
 
 			/* skip matched rule */
 			if (i == j)
@@ -3037,10 +3021,12 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
 			 * (with this previous rule) to a valid ref
 			 * read_ref() returns 0 on success
 			 */
-			mksnpath(refname, sizeof(refname),
-				 rule, short_name_len, short_name);
-			if (ref_exists(refname))
-				break;
+			if (q->expand) {
+				q->expand(q, resolved, sizeof(resolved),
+					  short_name, short_name_len);
+				if (ref_exists(resolved))
+					break;
+			}
 		}
 
 		/*
@@ -3049,9 +3035,9 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
 		 */
 		if (j == rules_to_fail)
 			return short_name;
+		free(short_name);
 	}
 
-	free(short_name);
 	return xstrdup(refname);
 }
 
diff --git a/refs.h b/refs.h
index 85710cb..245af6f 100644
--- a/refs.h
+++ b/refs.h
@@ -169,6 +169,8 @@ struct ref_expand_rule {
 	void (*expand)(const struct ref_expand_rule *rule,
 		       char *dst, size_t dst_len,
 		       const char *shortname, size_t shortname_len);
+	char *(*shorten)(const struct ref_expand_rule *rule,
+			 const char *refname);
 	const char *pattern;
 };
 extern const struct ref_expand_rule ref_expand_rules[];
-- 
1.8.1.3.704.g33f7d4f
Previous: Junio C HamanoNext: Junio C Hamano
Message 22 of 39 in “Make "$remote/$branch" work with unconventional refspecs”
  1. 0/7 Make "$remote/$branch" work with unconventional refspecsJohan Herland, May 4, 2013
  2. 1/7 shorten_unambiguous_ref(): Allow shortening refs/remotes/origin/HEAD to originJohan Herland, May 4, 2013
  3. Bert WesargMay 5, 2013
  4. Junio C HamanoMay 6, 2013
  5. Johan HerlandMay 7, 2013
  6. 1/3 t1514: Add tests of shortening refnames in strict/loose modeJohan Herland, May 7, 2013
  7. 2/3 t1514: Demonstrate failure to correctly shorten "refs/remotes/origin/HEAD"Johan Herland, May 7, 2013
  8. 3/3 shorten_unambiguous_ref(): Fix shortening refs/remotes/origin/HEAD to originJohan Herland, May 7, 2013
  9. Junio C HamanoMay 7, 2013
  10. Junio C HamanoMay 7, 2013
  11. Johan HerlandMay 7, 2013
  12. Junio C HamanoMay 7, 2013
  13. Johan HerlandMay 7, 2013
  14. 2/7 t7900: Start testing usability of namespaced remote refsJohan Herland, May 4, 2013
  15. Junio C HamanoMay 7, 2013
  16. Johan HerlandMay 7, 2013
  17. Junio C HamanoMay 7, 2013
  18. 3/7 t7900: Demonstrate failure to expand "$remote/$branch" according to refspecsJohan Herland, May 4, 2013
  19. Junio C HamanoMay 7, 2013
  20. 4/7 refs.c: Refactor rules for expanding shorthand names into full refnamesJohan Herland, May 4, 2013
  21. Junio C HamanoMay 7, 2013
  22. 5/7 refs.c: Refactor code for shortening full refnames into shorthand namesJohan Herland, May 4, 2013
  23. Junio C HamanoMay 7, 2013
  24. 6/7 refname_match(): Caller must declare if we're matching local or remote refsJohan Herland, May 4, 2013
  25. Junio C HamanoMay 7, 2013
  26. 7/7 refs.c: Add rules for resolving refs using remote refspecsJohan Herland, May 4, 2013
  27. Junio C HamanoMay 5, 2013
  28. Johan HerlandMay 5, 2013
  29. Junio C HamanoMay 5, 2013
  30. Johan HerlandMay 5, 2013
  31. Junio C HamanoMay 5, 2013
  32. Santi BéjarMay 6, 2013
  33. Santi BéjarMay 6, 2013
  34. Junio C HamanoMay 6, 2013
  35. Santi BéjarMay 6, 2013
  36. Junio C HamanoMay 6, 2013
  37. Junio C HamanoMay 6, 2013
  38. Johan HerlandMay 6, 2013
  39. Junio C HamanoMay 7, 2013

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.