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

[PATCH] for-each-ref: `:short` format for `refname`

From
Bert Wesarg <bert.wesarg@googlemail.com>
Date
Aug 31, 2008, 12:41 UTC
Message-ID
<1220186467-24623-1-git-send-email-bert.wesarg@googlemail.com>
In-Reply-To
<7vprnpbqmo.fsf@gitster.siamese.dyndns.org>

This strips from the refname the common directory prefix with the matched pattern.

This is particular usefull for bash completion, to get refs without `refs/heads` or `refs/tags`.

Signed-off-by: Bert Wesarg <bert.wesarg@googlemail.com>
---
 Documentation/git-for-each-ref.txt |    5 ++
 builtin-for-each-ref.c             |   74 +++++++++++++++++++++++++++++++----
 t/t6300-for-each-ref.sh            |   61 +++++++++++++++++++++++++++++
 3 files changed, 131 insertions(+), 9 deletions(-)
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index eae6c0e..deeae79 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -74,6 +74,11 @@ For all objects, the following names can be used:
 
 refname::
 	The name of the ref (the part after $GIT_DIR/).
+	For a short name of the ref append `:short`. This will strip
+	the common directory prefix with the pattern which matches this ref.
+	I.e. for a the pattern `refs/heads` you get `master`, or for
+	`refs/tags/v1.5.[01].*` you get `v1.5.[01].*`.
+	This is particular usefull for bash completion.
 
 objecttype::
 	The type of the object (`blob`, `tree`, `commit`, `tag`).
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
index 21e92bb..946f79b 100644
--- a/builtin-for-each-ref.c
+++ b/builtin-for-each-ref.c
@@ -31,6 +31,7 @@ struct ref_sort {
 
 struct refinfo {
 	char *refname;
+	const char *pattern; /* the pattern which matched this ref */
 	unsigned char objectname[20];
 	struct atom_value *value;
 };
@@ -546,6 +547,40 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
 }
 
 /*
+ * Use the matched pattern from ref to shorten the refname
+ */
+static char *get_short_ref(struct refinfo *ref)
+{
+	int rlen, plen, common = 0;
+
+	if (!ref->pattern)
+		return ref->refname;
+
+	rlen = strlen(ref->refname);
+	plen = strlen(ref->pattern);
+
+	if ((plen <= rlen) &&
+	    !strncmp(ref->refname, ref->pattern, plen) &&
+	    (ref->refname[plen] == '\0' ||
+	     ref->refname[plen] == '/' ||
+	     ref->pattern[plen - 1] == '/')) {
+		common = plen + (ref->refname[plen] == '/');
+		/* prevent stripping the whole refname */
+		if (common == rlen)
+			common = 0;
+	} else {
+		/* find the first wildcard and go back to the previous '/' */
+		common = strcspn(ref->pattern, "*?[");
+		while (common >= 0 && ref->pattern[common] != '/')
+			--common;
+		common++;
+	}
+
+	return ref->refname + common;
+}
+
+
+/*
  * Parse the object referred by ref, and grab needed value.
  */
 static void populate_value(struct refinfo *ref)
@@ -570,13 +605,33 @@ static void populate_value(struct refinfo *ref)
 	for (i = 0; i < used_atom_cnt; i++) {
 		const char *name = used_atom[i];
 		struct atom_value *v = &ref->value[i];
-		if (!strcmp(name, "refname"))
-			v->s = ref->refname;
-		else if (!strcmp(name, "*refname")) {
-			int len = strlen(ref->refname);
-			char *s = xmalloc(len + 4);
-			sprintf(s, "%s^{}", ref->refname);
-			v->s = s;
+		int deref = 0;
+		if (*name == '*') {
+			deref = 1;
+			name++;
+		}
+		if (!prefixcmp(name, "refname")) {
+			const char *formatp = strchr(name, ':');
+			const char *refname = ref->refname;
+
+			/* look for "short" refname format */
+			if (formatp) {
+				formatp++;
+				if (!strcmp(formatp, "short"))
+					refname = get_short_ref(ref);
+				else
+					die("unknown refname format %s",
+					    formatp);
+			}
+
+			if (!deref)
+				v->s = refname;
+			else {
+				int len = strlen(refname);
+				char *s = xmalloc(len + 4);
+				sprintf(s, "%s^{}", refname);
+				v->s = s;
+			}
 		}
 	}
 
@@ -641,9 +696,9 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
 	struct grab_ref_cbdata *cb = cb_data;
 	struct refinfo *ref;
 	int cnt;
+	const char **pattern = cb->grab_pattern;
 
-	if (*cb->grab_pattern) {
-		const char **pattern;
+	if (*pattern) {
 		int namelen = strlen(refname);
 		for (pattern = cb->grab_pattern; *pattern; pattern++) {
 			const char *p = *pattern;
@@ -668,6 +723,7 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
 	 */
 	ref = xcalloc(1, sizeof(*ref));
 	ref->refname = xstrdup(refname);
+	ref->pattern = *pattern;
 	hashcpy(ref->objectname, sha1);
 
 	cnt = cb->grab_cnt;
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 8ced593..a4a2fd3 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -262,6 +262,67 @@ for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do
 	"
 done
 
+cat >expected <<\EOF
+master
+testtag
+master
+testtag
+EOF
+
+test_expect_success 'Check short refname format' '
+	(git for-each-ref --format="%(refname:short)" refs/heads &&
+	git for-each-ref --format="%(refname:short)" refs/tags &&
+	git for-each-ref --format="%(refname:short)" refs/heads/ &&
+	git for-each-ref --format="%(refname:short)" refs/tags/) >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<\EOF
+master
+testtag
+EOF
+
+test_expect_success 'Check short refname format with multiple patterns' '
+	(git for-each-ref --format="%(refname:short)" refs/heads refs/tags) >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/heads/master
+refs/tags/testtag
+EOF
+
+test_expect_success 'Check short refname format without patterns' '
+	git for-each-ref --format="%(refname:short)" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Check for invalid refname format' '
+	test_must_fail git for-each-ref --format="%(refname:INVALID)"
+'
+
+cat >expected <<\EOF
+heads/master
+master
+testtag
+EOF
+
+test_expect_success 'Check short refname format with wildcard pattern' '
+	(git for-each-ref --format="%(refname:short)" refs/*/m* &&
+	git for-each-ref --format="%(refname:short)" refs/heads/?aster &&
+	git for-each-ref --format="%(refname:short)" refs/tags/t[aeiou]sttag) >actual &&
+	test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/heads/master
+EOF
+
+test_expect_success 'Check disabled short refname format with exact pattern' '
+	(git for-each-ref --format="%(refname:short)" refs/heads/master) >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'an unusual tag with an incomplete line' '
 
 	git tag -m "bogo" bogo &&
-- 
tg: (445cac1..) t/for-each-ref-refshort (depends on: master)
Next: SZEDER Gábor
Message 1 of 26 in “for-each-ref: `:short` format for `refname`”
  1. for-each-ref: `:short` format for `refname`Bert Wesarg, Aug 31, 2008
  2. SZEDER GáborSep 1, 2008
  3. Bert WesargSep 1, 2008
  4. Bert WesargSep 1, 2008
  5. Shawn O. PearceSep 1, 2008
  6. Bert WesargSep 1, 2008
  7. Junio C HamanoSep 1, 2008
  8. Bert WesargSep 1, 2008
  9. Bert WesargSep 2, 2008
  10. Shawn O. PearceSep 2, 2008
  11. for-each-ref: `:short` format for `refname`Bert Wesarg, Sep 2, 2008
  12. Junio C HamanoSep 2, 2008
  13. Bert WesargSep 3, 2008
  14. for-each-ref: `:short` format for `refname`Bert Wesarg, Sep 3, 2008
  15. Shawn O. PearceSep 3, 2008
  16. Bert WesargSep 3, 2008
  17. Bert WesargSep 3, 2008
  18. Junio C HamanoSep 3, 2008
  19. for-each-ref: `:short` format for `refname`Bert Wesarg, Sep 5, 2008
  20. Junio C HamanoSep 5, 2008
  21. Bert WesargSep 6, 2008
  22. Junio C HamanoSep 8, 2008
  23. Shawn O. PearceSep 8, 2008
  24. Bert WesargSep 9, 2008
  25. Junio C HamanoSep 9, 2008
  26. Bert WesargSep 9, 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.