Skip to content

Commit 0571979

Browse files
peffgitster
authored andcommitted
tag: do not show ambiguous tag names as "tags/foo"
Since b7cc53e (tag.c: use 'ref-filter' APIs, 2015-07-11), git-tag has started showing tags with ambiguous names (i.e., when both "heads/foo" and "tags/foo" exists) as "tags/foo" instead of just "foo". This is both: - pointless; the output of "git tag" includes only refs/tags, so we know that "foo" means the one in "refs/tags". and - ambiguous; in the original output, we know that the line "foo" means that "refs/tags/foo" exists. In the new output, it is unclear whether we mean "refs/tags/foo" or "refs/tags/tags/foo". The reason this happens is that commit b7cc53e switched git-tag to use ref-filter's "%(refname:short)" output formatting, which was adapted from for-each-ref. This more general code does not know that we care only about tags, and uses shorten_unambiguous_ref to get the short-name. We need to tell it that we care only about "refs/tags/", and it should shorten with respect to that value. In theory, the ref-filter code could figure this out by us passing FILTER_REFS_TAGS. But there are two complications there: 1. The handling of refname:short is deep in formatting code that does not even have our ref_filter struct, let alone the arguments to the filter_ref struct. 2. In git v2.7.0, we expose the formatting language to the user. If we follow this path, it will mean that "%(refname:short)" behaves differently for "tag" versus "for-each-ref" (including "for-each-ref refs/tags/"), which can lead to confusion. Instead, let's add a new modifier to the formatting language, "strip", to remove a specific set of prefix components. This fixes "git tag", and lets users invoke the same behavior from their own custom formats (for "tag" or "for-each-ref") while leaving ":short" with its same consistent meaning in all places. We introduce a test in t7004 for "git tag", which fails without this patch. We also add a similar test in t3203 for "git branch", which does not actually fail. But since it is likely that "branch" will eventually use the same formatting code, the test helps defend against future regressions. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1d094db commit 0571979

File tree

7 files changed

+62
-4
lines changed

7 files changed

+62
-4
lines changed

Documentation/git-for-each-ref.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ refname::
9292
The name of the ref (the part after $GIT_DIR/).
9393
For a non-ambiguous short name of the ref append `:short`.
9494
The option core.warnAmbiguousRefs is used to select the strict
95-
abbreviation mode.
95+
abbreviation mode. If `strip=<N>` is appended, strips `<N>`
96+
slash-separated path components from the front of the refname
97+
(e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.
98+
`<N>` must be a positive integer. If a displayed ref has fewer
99+
components than `<N>`, the command aborts with an error.
96100

97101
objecttype::
98102
The type of the object (`blob`, `tree`, `commit`, `tag`).

Documentation/git-tag.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ This option is only applicable when listing tags without annotation lines.
163163
A string that interpolates `%(fieldname)` from the object
164164
pointed at by a ref being shown. The format is the same as
165165
that of linkgit:git-for-each-ref[1]. When unspecified,
166-
defaults to `%(refname:short)`.
166+
defaults to `%(refname:strip=2)`.
167167

168168
--[no-]merged [<commit>]::
169169
Only list tags whose tips are reachable, or not reachable

builtin/tag.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
4444
if (!format) {
4545
if (filter->lines) {
4646
to_free = xstrfmt("%s %%(contents:lines=%d)",
47-
"%(align:15)%(refname:short)%(end)",
47+
"%(align:15)%(refname:strip=2)%(end)",
4848
filter->lines);
4949
format = to_free;
5050
} else
51-
format = "%(refname:short)";
51+
format = "%(refname:strip=2)";
5252
}
5353

5454
verify_ref_format(format);

ref-filter.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,29 @@ static inline char *copy_advance(char *dst, const char *src)
763763
return dst;
764764
}
765765

766+
static const char *strip_ref_components(const char *refname, const char *nr_arg)
767+
{
768+
char *end;
769+
long nr = strtol(nr_arg, &end, 10);
770+
long remaining = nr;
771+
const char *start = refname;
772+
773+
if (nr < 1 || *end != '\0')
774+
die(":strip= requires a positive integer argument");
775+
776+
while (remaining) {
777+
switch (*start++) {
778+
case '\0':
779+
die("ref '%s' does not have %ld components to :strip",
780+
refname, nr);
781+
case '/':
782+
remaining--;
783+
break;
784+
}
785+
}
786+
return start;
787+
}
788+
766789
/*
767790
* Parse the object referred by ref, and grab needed value.
768791
*/
@@ -909,11 +932,14 @@ static void populate_value(struct ref_array_item *ref)
909932
formatp = strchr(name, ':');
910933
if (formatp) {
911934
int num_ours, num_theirs;
935+
const char *arg;
912936

913937
formatp++;
914938
if (!strcmp(formatp, "short"))
915939
refname = shorten_unambiguous_ref(refname,
916940
warn_ambiguous_refs);
941+
else if (skip_prefix(formatp, "strip=", &arg))
942+
refname = strip_ref_components(refname, arg);
917943
else if (!strcmp(formatp, "track") &&
918944
(starts_with(name, "upstream") ||
919945
starts_with(name, "push"))) {

t/t3203-branch-output.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,12 @@ test_expect_success 'git branch --points-at option' '
176176
test_cmp expect actual
177177
'
178178

179+
test_expect_success 'ambiguous branch/tag not marked' '
180+
git tag ambiguous &&
181+
git branch ambiguous &&
182+
echo " ambiguous" >expect &&
183+
git branch --list ambiguous >actual &&
184+
test_cmp expect actual
185+
'
186+
179187
test_done

t/t6300-for-each-ref.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ test_atom() {
5050

5151
test_atom head refname refs/heads/master
5252
test_atom head refname:short master
53+
test_atom head refname:strip=1 heads/master
54+
test_atom head refname:strip=2 master
5355
test_atom head upstream refs/remotes/origin/master
5456
test_atom head upstream:short origin/master
5557
test_atom head push refs/remotes/myfork/master
@@ -132,6 +134,16 @@ test_expect_success 'Check invalid atoms names are errors' '
132134
test_must_fail git for-each-ref --format="%(INVALID)" refs/heads
133135
'
134136

137+
test_expect_success 'arguments to :strip must be positive integers' '
138+
test_must_fail git for-each-ref --format="%(refname:strip=0)" &&
139+
test_must_fail git for-each-ref --format="%(refname:strip=-1)" &&
140+
test_must_fail git for-each-ref --format="%(refname:strip=foo)"
141+
'
142+
143+
test_expect_success 'stripping refnames too far gives an error' '
144+
test_must_fail git for-each-ref --format="%(refname:strip=3)"
145+
'
146+
135147
test_expect_success 'Check format specifiers are ignored in naming date atoms' '
136148
git for-each-ref --format="%(authordate)" refs/heads &&
137149
git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads &&

t/t7004-tag.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,4 +1558,12 @@ test_expect_success '--no-merged show unmerged tags' '
15581558
test_cmp expect actual
15591559
'
15601560

1561+
test_expect_success 'ambiguous branch/tags not marked' '
1562+
git tag ambiguous &&
1563+
git branch ambiguous &&
1564+
echo ambiguous >expect &&
1565+
git tag -l ambiguous >actual &&
1566+
test_cmp expect actual
1567+
'
1568+
15611569
test_done

0 commit comments

Comments
 (0)