* Regression? Ambiguous tags listed as "tags/<foo>" @ 2016-01-23 23:56 Pete Harlan 2016-01-24 7:12 ` [PATCH] tag: do not show ambiguous tag names as "tags/foo" Jeff King 0 siblings, 1 reply; 19+ messages in thread From: Pete Harlan @ 2016-01-23 23:56 UTC (permalink / raw) To: git There was a behavior change in "git tag" in b7cc53e9 (tag.c: use 'ref-filter' APIs, 2015-07-11, v2.5.0-276-gb7cc53e), such that "git tag" now writes "foo" as "tags/foo" if there is also a branch named "foo": % git tag tags/branchortag tagonly Previous behavior: % git tag branchortag tagonly I prefer the previous behavior. Perhaps the change was intentional, but I didn't see it documented. Thanks, Pete Harlan pgit@tento.net ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-23 23:56 Regression? Ambiguous tags listed as "tags/<foo>" Pete Harlan @ 2016-01-24 7:12 ` Jeff King 2016-01-24 7:18 ` Jeff King 0 siblings, 1 reply; 19+ messages in thread From: Jeff King @ 2016-01-24 7:12 UTC (permalink / raw) To: Pete Harlan; +Cc: Karthik Nayak, git On Sat, Jan 23, 2016 at 03:56:12PM -0800, Pete Harlan wrote: > There was a behavior change in "git tag" in b7cc53e9 (tag.c: use > 'ref-filter' APIs, 2015-07-11, v2.5.0-276-gb7cc53e), such that "git > tag" now writes "foo" as "tags/foo" if there is also a branch named > "foo": > > % git tag > tags/branchortag > tagonly > > Previous behavior: > > % git tag > branchortag > tagonly > > I prefer the previous behavior. Perhaps the change was intentional, > but I didn't see it documented. I don't think it was intentional, and I think the new output is actively wrong. My reasoning (and a fix) are below. -- >8 -- Subject: tag: do not show ambiguous tag names as "tags/foo" Since b7cc53e9 (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 b7cc53e9 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 extend the "short" modifier in the formatting language to handle a specific prefix. 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 <peff@peff.net> --- Documentation/git-for-each-ref.txt | 4 +++- Documentation/git-tag.txt | 2 +- builtin/tag.c | 4 ++-- ref-filter.c | 3 +++ t/t3203-branch-output.sh | 8 ++++++++ t/t7004-tag.sh | 8 ++++++++ 6 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 06208c4..faf10c5 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -92,7 +92,9 @@ refname:: The name of the ref (the part after $GIT_DIR/). For a non-ambiguous short name of the ref append `:short`. The option core.warnAmbiguousRefs is used to select the strict - abbreviation mode. + abbreviation mode. For shortening in a specific hierarchy, use + `:short=<prefix>`, which will remove `<prefix>` (if present) + from the front of the ref. E.g., `%(refname:short=refs/tags/)`. objecttype:: The type of the object (`blob`, `tree`, `commit`, `tag`). diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 7220e5e..62ff509 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -163,7 +163,7 @@ This option is only applicable when listing tags without annotation lines. A string that interpolates `%(fieldname)` from the object pointed at by a ref being shown. The format is the same as that of linkgit:git-for-each-ref[1]. When unspecified, - defaults to `%(refname:short)`. + defaults to `%(refname:short=refs/tags/)`. --[no-]merged [<commit>]:: Only list tags whose tips are reachable, or not reachable diff --git a/builtin/tag.c b/builtin/tag.c index 8db8c87..f60feb1 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -44,11 +44,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con if (!format) { if (filter->lines) { to_free = xstrfmt("%s %%(contents:lines=%d)", - "%(align:15)%(refname:short)%(end)", + "%(align:15)%(refname:short=refs/tags/)%(end)", filter->lines); format = to_free; } else - format = "%(refname:short)"; + format = "%(refname:short=refs/tags/)"; } verify_ref_format(format); diff --git a/ref-filter.c b/ref-filter.c index 7bef7f8..b878b77 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -909,11 +909,14 @@ static void populate_value(struct ref_array_item *ref) formatp = strchr(name, ':'); if (formatp) { int num_ours, num_theirs; + const char *prefix; formatp++; if (!strcmp(formatp, "short")) refname = shorten_unambiguous_ref(refname, warn_ambiguous_refs); + else if (skip_prefix(formatp, "short=", &prefix)) + skip_prefix(refname, prefix, &refname); else if (!strcmp(formatp, "track") && (starts_with(name, "upstream") || starts_with(name, "push"))) { diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index d3913f9..4261403 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -176,4 +176,12 @@ test_expect_success 'git branch --points-at option' ' test_cmp expect actual ' +test_expect_success 'ambiguous branch/tag not marked' ' + git tag ambiguous && + git branch ambiguous && + echo " ambiguous" >expect && + git branch --list ambiguous >actual && + test_cmp expect actual +' + test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 2797f22..cf3469b 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -1558,4 +1558,12 @@ test_expect_success '--no-merged show unmerged tags' ' test_cmp expect actual ' +test_expect_success 'ambiguous branch/tags not marked' ' + git tag ambiguous && + git branch ambiguous && + echo ambiguous >expect && + git tag -l ambiguous >actual && + test_cmp expect actual +' + test_done -- 2.7.0.425.g047222e ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-24 7:12 ` [PATCH] tag: do not show ambiguous tag names as "tags/foo" Jeff King @ 2016-01-24 7:18 ` Jeff King 2016-01-24 22:19 ` Junio C Hamano 0 siblings, 1 reply; 19+ messages in thread From: Jeff King @ 2016-01-24 7:18 UTC (permalink / raw) To: Pete Harlan; +Cc: Karthik Nayak, git On Sun, Jan 24, 2016 at 02:12:35AM -0500, Jeff King wrote: > 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 extend the "short" modifier in the formatting > language to handle a specific prefix. 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. I think the patch I posted is a reasonable way to go. But I also don't think that having "%(refname:short)" behave specially for "git-tag" is all that unreasonable, either. But I'm open to argument. Here are a few more considerations I had. - I'm not sure if the "special" behavior works as well for git-branch, which may want to shorten both "refs/heads/" and "refs/remotes/", depending on the type of ref. My solution may not extend there naturally, either, depending on how it is implemented. - To let users get the same behavior out of for-each-ref, we could perhaps auto-infer that looking at "refs/tags/" means shortening and disambiguation should only happen with respect to the "refs/tags/" hierarchy. But I'm uncomfortable changing the meaning of ":short" without at least a new option. And what would it mean for "git for-each-ref refs/heads/foo/"? Would it shorten "refs/heads/foo/bar" to just "bar", or would it still be "foo/bar"? -Peff ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-24 7:18 ` Jeff King @ 2016-01-24 22:19 ` Junio C Hamano 2016-01-24 22:27 ` Jeff King 2016-01-24 23:05 ` Jeff King 0 siblings, 2 replies; 19+ messages in thread From: Junio C Hamano @ 2016-01-24 22:19 UTC (permalink / raw) To: Jeff King; +Cc: Pete Harlan, Karthik Nayak, git Jeff King <peff@peff.net> writes: > On Sun, Jan 24, 2016 at 02:12:35AM -0500, Jeff King wrote: > >> 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 extend the "short" modifier in the formatting >> language to handle a specific prefix. 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. Yeah, I do agree with the analysis. I however wonder if short=$prefix is a good end-user interface, though, as strip=$prefix may be more intuitive to them, I suspect. > I think the patch I posted is a reasonable way to go. But I also don't > think that having "%(refname:short)" behave specially for "git-tag" is > all that unreasonable, either. But I'm open to argument. > > Here are a few more considerations I had. > > - I'm not sure if the "special" behavior works as well for git-branch, > which may want to shorten both "refs/heads/" and "refs/remotes/", > depending on the type of ref. > > My solution may not extend there naturally, either, depending on how > it is implemented. > > - To let users get the same behavior out of for-each-ref, we could > perhaps auto-infer that looking at "refs/tags/" means shortening and > disambiguation should only happen with respect to the "refs/tags/" > hierarchy. > > But I'm uncomfortable changing the meaning of ":short" without at > least a new option. And what would it mean for "git for-each-ref > refs/heads/foo/"? Would it shorten "refs/heads/foo/bar" to just > "bar", or would it still be "foo/bar"? Also there is "what happens if the expected prefix is not there?" question. Perhaps strip=2 can be defined to "strip 2 levels of hierarchy prefix no matter what that is", and strip refs/tags/foo, refs/heads/foo and refs/remotes/origin/foo to foo, foo, origin/foo, respectively? The filtering natively done by the listing mode of "branch" and "tags" would ensure the prefixes are always what we implicitly expect, so the case we need to worry about how we should signal errors becomes "what if there are not enough levels". That may be simpler to handle. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-24 22:19 ` Junio C Hamano @ 2016-01-24 22:27 ` Jeff King 2016-01-24 23:39 ` Eric Sunshine 2016-01-25 2:26 ` Junio C Hamano 2016-01-24 23:05 ` Jeff King 1 sibling, 2 replies; 19+ messages in thread From: Jeff King @ 2016-01-24 22:27 UTC (permalink / raw) To: Junio C Hamano; +Cc: Pete Harlan, Karthik Nayak, git On Sun, Jan 24, 2016 at 02:19:52PM -0800, Junio C Hamano wrote: > >> Instead, let's extend the "short" modifier in the formatting > >> language to handle a specific prefix. 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. > > Yeah, I do agree with the analysis. > > I however wonder if short=$prefix is a good end-user interface, > though, as strip=$prefix may be more intuitive to them, I suspect. Yeah, I picked "short" just because of the existing feature. But we are not bound to use the same name at all, and "strip" is probably more descriptive (I thought "prefix" might also be, but that only communicates what it _is_, not that we are removing it). > Also there is "what happens if the expected prefix is not there?" > question. I think "do not strip anything" (as I have here) is an OK behavior. It would not come up for sane requests (i.e., you would generally be filtering to match your prefix anyway). But... > Perhaps strip=2 can be defined to "strip 2 levels of > hierarchy prefix no matter what that is", and strip refs/tags/foo, > refs/heads/foo and refs/remotes/origin/foo to foo, foo, origin/foo, > respectively? The filtering natively done by the listing mode of > "branch" and "tags" would ensure the prefixes are always what we > implicitly expect, so the case we need to worry about how we should > signal errors becomes "what if there are not enough levels". That > may be simpler to handle. Yeah, "strip=2" would also get the job done, and extends more naturally to the branch case. To be honest, I cannot imagine anybody using anything _but_ strip=2, but maybe there are special cases, like: git for-each-ref --format='%(refname:strip=3)' refs/heads/jk/ to get my list of topics, sans initials. I had originally hoped to avoid exposing any of this to the user, and just make things internal, so that we would not be locked into a particular formatting behavior. But since we now have "tag --format" and advertise "%(refname:short)" as its default, I think it has become user-visible. Let me see what the "strip=X" patch looks like. -Peff ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-24 22:27 ` Jeff King @ 2016-01-24 23:39 ` Eric Sunshine 2016-01-25 9:56 ` Jeff King 2016-01-25 2:26 ` Junio C Hamano 1 sibling, 1 reply; 19+ messages in thread From: Eric Sunshine @ 2016-01-24 23:39 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, Pete Harlan, Karthik Nayak, Git List On Sun, Jan 24, 2016 at 5:27 PM, Jeff King <peff@peff.net> wrote: > On Sun, Jan 24, 2016 at 02:19:52PM -0800, Junio C Hamano wrote: >> Perhaps strip=2 can be defined to "strip 2 levels of >> hierarchy prefix no matter what that is", and strip refs/tags/foo, >> refs/heads/foo and refs/remotes/origin/foo to foo, foo, origin/foo, >> respectively? The filtering natively done by the listing mode of >> "branch" and "tags" would ensure the prefixes are always what we >> implicitly expect, so the case we need to worry about how we should >> signal errors becomes "what if there are not enough levels". That >> may be simpler to handle. > > Yeah, "strip=2" would also get the job done, and extends more naturally > to the branch case. > > To be honest, I cannot imagine anybody using anything _but_ strip=2, but > maybe there are special cases, like: > > git for-each-ref --format='%(refname:strip=3)' refs/heads/jk/ > > to get my list of topics, sans initials. What if the option was named ":stripprefix=" in its most general form: %(refname:stripprefix=refs/tags/) with plain: %(refname:stripprefix) shorthand for ":stripprefix=refs/*/" or something? ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-24 23:39 ` Eric Sunshine @ 2016-01-25 9:56 ` Jeff King 0 siblings, 0 replies; 19+ messages in thread From: Jeff King @ 2016-01-25 9:56 UTC (permalink / raw) To: Eric Sunshine; +Cc: Junio C Hamano, Pete Harlan, Karthik Nayak, Git List On Sun, Jan 24, 2016 at 06:39:05PM -0500, Eric Sunshine wrote: > > Yeah, "strip=2" would also get the job done, and extends more naturally > > to the branch case. > > > > To be honest, I cannot imagine anybody using anything _but_ strip=2, but > > maybe there are special cases, like: > > > > git for-each-ref --format='%(refname:strip=3)' refs/heads/jk/ > > > > to get my list of topics, sans initials. > > What if the option was named ":stripprefix=" in its most general form: > > %(refname:stripprefix=refs/tags/) > > with plain: > > %(refname:stripprefix) > > shorthand for ":stripprefix=refs/*/" or something? That would work, though I was really hoping not to get into something as complicated as globbing. I'm not sure it really buys us that much here. -Peff ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-24 22:27 ` Jeff King 2016-01-24 23:39 ` Eric Sunshine @ 2016-01-25 2:26 ` Junio C Hamano 2016-01-25 10:01 ` Jeff King 1 sibling, 1 reply; 19+ messages in thread From: Junio C Hamano @ 2016-01-25 2:26 UTC (permalink / raw) To: Jeff King; +Cc: Pete Harlan, Karthik Nayak, git Jeff King <peff@peff.net> writes: > Yeah, "strip=2" would also get the job done, and extends more naturally > to the branch case. > > To be honest, I cannot imagine anybody using anything _but_ strip=2... I 100% agree, and I do consider this to be internal implementation detail for the listing modes of "tag" (and "branch"), which may be exposed to the user (by documenting that %(refname:X) is used by default), so perhaps even the flexibility of strip=2 is unwanted. I know what "remove-standard-prefix" is way too long for the value of X above, but then we can say "the command will error out if you allow your for-each-ref invocation to step outside of the area that has standard prefix to be removed." without having to worry about "what is the sensible thing to do when the prefixes are not what we expect (too short for strip=2 or no match for short=refs/tags/)". ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-25 2:26 ` Junio C Hamano @ 2016-01-25 10:01 ` Jeff King 2016-01-25 19:29 ` Karthik Nayak 2016-01-25 20:21 ` Junio C Hamano 0 siblings, 2 replies; 19+ messages in thread From: Jeff King @ 2016-01-25 10:01 UTC (permalink / raw) To: Junio C Hamano; +Cc: Pete Harlan, Karthik Nayak, git On Sun, Jan 24, 2016 at 06:26:50PM -0800, Junio C Hamano wrote: > Jeff King <peff@peff.net> writes: > > > Yeah, "strip=2" would also get the job done, and extends more naturally > > to the branch case. > > > > To be honest, I cannot imagine anybody using anything _but_ strip=2... > > I 100% agree, and I do consider this to be internal implementation > detail for the listing modes of "tag" (and "branch"), which may be > exposed to the user (by documenting that %(refname:X) is used by > default), so perhaps even the flexibility of strip=2 is unwanted. > > I know what "remove-standard-prefix" is way too long for the value > of X above, but then we can say "the command will error out if you > allow your for-each-ref invocation to step outside of the area that > has standard prefix to be removed." without having to worry about > "what is the sensible thing to do when the prefixes are not what we > expect (too short for strip=2 or no match for short=refs/tags/)". I'm not sure "remove-standard-prefix" doesn't open its own questions. Like "what are the standard prefixes?". If we are going to go with "remove a prefix", I really don't think "remove if present" is too complicated a set of semantics (as opposed to "error out" you mentioned above). I do like "strip=<n>" for its simplicity (it's easy to explain), and the fact that it will probably handle the git-branch case for us. The only open question is what to do if there are fewer components, but I really think any of the 4 behaviors I gave earlier would be fine. Eric' globbing suggestion is simpler for the error case (as a prefix, it can be "remove if present"), but I think introducing globbing in general opens up too many corner cases (e.g., does "*" match "/", is "**" supported, etc). -Peff ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-25 10:01 ` Jeff King @ 2016-01-25 19:29 ` Karthik Nayak 2016-01-25 20:21 ` Junio C Hamano 1 sibling, 0 replies; 19+ messages in thread From: Karthik Nayak @ 2016-01-25 19:29 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, Pete Harlan, Git On Mon, Jan 25, 2016 at 3:31 PM, Jeff King <peff@peff.net> wrote: > On Sun, Jan 24, 2016 at 06:26:50PM -0800, Junio C Hamano wrote: > >> Jeff King <peff@peff.net> writes: >> >> > Yeah, "strip=2" would also get the job done, and extends more naturally >> > to the branch case. >> > >> > To be honest, I cannot imagine anybody using anything _but_ strip=2... >> >> I 100% agree, and I do consider this to be internal implementation >> detail for the listing modes of "tag" (and "branch"), which may be >> exposed to the user (by documenting that %(refname:X) is used by >> default), so perhaps even the flexibility of strip=2 is unwanted. >> >> I know what "remove-standard-prefix" is way too long for the value >> of X above, but then we can say "the command will error out if you >> allow your for-each-ref invocation to step outside of the area that >> has standard prefix to be removed." without having to worry about >> "what is the sensible thing to do when the prefixes are not what we >> expect (too short for strip=2 or no match for short=refs/tags/)". > > I'm not sure "remove-standard-prefix" doesn't open its own questions. > Like "what are the standard prefixes?". > > If we are going to go with "remove a prefix", I really don't think > "remove if present" is too complicated a set of semantics (as opposed to > "error out" you mentioned above). > > I do like "strip=<n>" for its simplicity (it's easy to explain), and the > fact that it will probably handle the git-branch case for us. The only > open question is what to do if there are fewer components, but I really > think any of the 4 behaviors I gave earlier would be fine. This seems ideal, it's also quite useful like your mentioned example. And would provide common ground for the upcoming git-branch patches as you said. What about a scenario wherein we have refs/branches/abc/foo refs/branches/xyz should --format="%(refname:strip=3)" give us foo xyz (here stripping till 2 '/', which is the maximum) or foo refs/branches/xyz (no stripping done at all) I prefer the former, cause that would allow us to give a maximum value for stripping and have everything below that maximum value stripped as well. -- Regards, Karthik Nayak ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-25 10:01 ` Jeff King 2016-01-25 19:29 ` Karthik Nayak @ 2016-01-25 20:21 ` Junio C Hamano 2016-01-26 2:37 ` Jeff King 1 sibling, 1 reply; 19+ messages in thread From: Junio C Hamano @ 2016-01-25 20:21 UTC (permalink / raw) To: Jeff King; +Cc: Pete Harlan, Karthik Nayak, git Jeff King <peff@peff.net> writes: > I'm not sure "remove-standard-prefix" doesn't open its own questions. > Like "what are the standard prefixes?". It is easy to define, no? This is invented for the internal use of the listing modes of "tag" and "branch", so the users are welcome to use it if they see fit, but how the prefixes are stripped is defined by the convenience of these commands--the behaviour might even change when these commands are enhanced. > If we are going to go with "remove a prefix", I really don't think > "remove if present" is too complicated a set of semantics (as opposed to > "error out" you mentioned above). > > I do like "strip=<n>" for its simplicity (it's easy to explain), and the > fact that it will probably handle the git-branch case for us. The only > open question is what to do if there are fewer components, but I really > think any of the 4 behaviors I gave earlier would be fine. > > Eric' globbing suggestion is simpler for the error case (as a prefix, it > can be "remove if present"), but I think introducing globbing in general > opens up too many corner cases (e.g., does "*" match "/", is "**" > supported, etc). Yeah, I really do not like any of the "do not error out but assume that the user would not care about the ambiguities" solutions for something we primarily intend to use for internal purposes. I agree that "strip=<n>", "remove-prefix=<glob>", and the friends are good for end-user scripting, but they can come later, outside of the scope of this regression fix, and that is the proper time to debate and decide if it is really ok to assume that the user does not care about ambiguity, or it is prudent to error out. A separate "remove-standard-prefix" that is meant for internal use would allow us to push the fix out without having to decide now. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-25 20:21 ` Junio C Hamano @ 2016-01-26 2:37 ` Jeff King 2016-01-26 3:00 ` Jeff King 2016-01-26 3:25 ` Junio C Hamano 0 siblings, 2 replies; 19+ messages in thread From: Jeff King @ 2016-01-26 2:37 UTC (permalink / raw) To: Junio C Hamano; +Cc: Pete Harlan, Karthik Nayak, git On Mon, Jan 25, 2016 at 12:21:21PM -0800, Junio C Hamano wrote: > Jeff King <peff@peff.net> writes: > > > I'm not sure "remove-standard-prefix" doesn't open its own questions. > > Like "what are the standard prefixes?". > > It is easy to define, no? This is invented for the internal use of > the listing modes of "tag" and "branch", so the users are welcome to > use it if they see fit, but how the prefixes are stripped is defined > by the convenience of these commands--the behaviour might even change > when these commands are enhanced. What does this do: git for-each-ref --format='%(refname:remove-standard-prefix)' ? Is there no standard prefix, because we are not in branch/tag? Does it remove well-known prefixes like "refs/heads/", "refs/tags/", etc? Does it remove the first two components (e.g., what happens to "refs/foo/bar")? If so, what happens to "refs/stash"? We can say "it is all internally defined and subject to change". But that is not very helpful to a user, and this "it's magic, don't rely on it" wart will be part of the user-facing interface. We have to write _something_ in the "default format" documentation for git-tag. > Yeah, I really do not like any of the "do not error out but assume > that the user would not care about the ambiguities" solutions for > something we primarily intend to use for internal purposes. > > I agree that "strip=<n>", "remove-prefix=<glob>", and the friends > are good for end-user scripting, but they can come later, outside of > the scope of this regression fix, and that is the proper time to > debate and decide if it is really ok to assume that the user does > not care about ambiguity, or it is prudent to error out. A separate > "remove-standard-prefix" that is meant for internal use would allow > us to push the fix out without having to decide now. Yeah, I am definitely on board with trying to do the most minimal thing for the regression fix and worry about more advanced concerns later (if at all). It seems to me that "error out" is a pretty minimal behavior, though, and one that doesn't lock us into any behaviors (i.e., it is generally OK to take something that did not work at all before, and give it new useful behavior; it is not OK to change something that did something useful before). So what about this: 1. Implement ":strip=<n>" and use it from git-tag. 2. Have it error out on a ref with fewer than <n> components. This should be impossible to trigger via "git-tag" with the default format. 3. Explicitly document that the behavior for values of <n> that are not positive integers is undefined and subject to change (or alternatively, we can simply error out). That _is_ user-visible, but it seems like "strip" is a fairly flexible mechanism by itself (enough that I do not mind living with it forever), and we haven't made any promises about the ambiguous parts. If we are going to do something _really_ internal and undocumented to fix the regression, I think I would rather not introduce a new formatter at all, but simply teach "%(refname:short)" to do the magic internal shortening in the context of git-tag. That does not let people emulate "tag" with "for-each-ref", but that is not part of the regression or its fix. -Peff ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-26 2:37 ` Jeff King @ 2016-01-26 3:00 ` Jeff King 2016-01-26 3:25 ` Junio C Hamano 1 sibling, 0 replies; 19+ messages in thread From: Jeff King @ 2016-01-26 3:00 UTC (permalink / raw) To: Junio C Hamano; +Cc: Pete Harlan, Karthik Nayak, git On Mon, Jan 25, 2016 at 09:37:53PM -0500, Jeff King wrote: > So what about this: > > 1. Implement ":strip=<n>" and use it from git-tag. > > 2. Have it error out on a ref with fewer than <n> components. This > should be impossible to trigger via "git-tag" with the default > format. > > 3. Explicitly document that the behavior for values of <n> that are > not positive integers is undefined and subject to change (or > alternatively, we can simply error out). > > That _is_ user-visible, but it seems like "strip" is a fairly flexible > mechanism by itself (enough that I do not mind living with it forever), > and we haven't made any promises about the ambiguous parts. Here's the matching patch. It's "v3 2/2", where "v3 1/2" is the same test-cleanup patch I posted earlier. -- >8 -- Subject: [PATCH v2 2/2] tag: do not show ambiguous tag names as "tags/foo" Since b7cc53e9 (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 b7cc53e9 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 <peff@peff.net> --- Documentation/git-for-each-ref.txt | 6 +++++- Documentation/git-tag.txt | 2 +- builtin/tag.c | 4 ++-- ref-filter.c | 26 ++++++++++++++++++++++++++ t/t3203-branch-output.sh | 8 ++++++++ t/t6300-for-each-ref.sh | 12 ++++++++++++ t/t7004-tag.sh | 8 ++++++++ 7 files changed, 62 insertions(+), 4 deletions(-) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 06208c4..2e3e96f 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -92,7 +92,11 @@ refname:: The name of the ref (the part after $GIT_DIR/). For a non-ambiguous short name of the ref append `:short`. The option core.warnAmbiguousRefs is used to select the strict - abbreviation mode. + abbreviation mode. If `strip=<N>` is appended, strips `<N>` + slash-separated path components from the front of the refname + (e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`. + `<N>` must be a positive integer. If a displayed ref has fewer + components than `<N>`, the command aborts with an error. objecttype:: The type of the object (`blob`, `tree`, `commit`, `tag`). diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 7220e5e..abab481 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -163,7 +163,7 @@ This option is only applicable when listing tags without annotation lines. A string that interpolates `%(fieldname)` from the object pointed at by a ref being shown. The format is the same as that of linkgit:git-for-each-ref[1]. When unspecified, - defaults to `%(refname:short)`. + defaults to `%(refname:strip=2)`. --[no-]merged [<commit>]:: Only list tags whose tips are reachable, or not reachable diff --git a/builtin/tag.c b/builtin/tag.c index 8db8c87..1705c94 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -44,11 +44,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con if (!format) { if (filter->lines) { to_free = xstrfmt("%s %%(contents:lines=%d)", - "%(align:15)%(refname:short)%(end)", + "%(align:15)%(refname:strip=2)%(end)", filter->lines); format = to_free; } else - format = "%(refname:short)"; + format = "%(refname:strip=2)"; } verify_ref_format(format); diff --git a/ref-filter.c b/ref-filter.c index 7bef7f8..f097176 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -763,6 +763,29 @@ static inline char *copy_advance(char *dst, const char *src) return dst; } +static const char *strip_ref_components(const char *refname, const char *nr_arg) +{ + char *end; + long nr = strtol(nr_arg, &end, 10); + long remaining = nr; + const char *start = refname; + + if (nr < 1 || *end != '\0') + die(":strip= requires a positive integer argument"); + + while (remaining) { + switch (*start++) { + case '\0': + die("ref '%s' does not have %ld components to :strip", + refname, nr); + case '/': + remaining--; + break; + } + } + return start; +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -909,11 +932,14 @@ static void populate_value(struct ref_array_item *ref) formatp = strchr(name, ':'); if (formatp) { int num_ours, num_theirs; + const char *arg; formatp++; if (!strcmp(formatp, "short")) refname = shorten_unambiguous_ref(refname, warn_ambiguous_refs); + else if (skip_prefix(formatp, "strip=", &arg)) + refname = strip_ref_components(refname, arg); else if (!strcmp(formatp, "track") && (starts_with(name, "upstream") || starts_with(name, "push"))) { diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index d3913f9..4261403 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -176,4 +176,12 @@ test_expect_success 'git branch --points-at option' ' test_cmp expect actual ' +test_expect_success 'ambiguous branch/tag not marked' ' + git tag ambiguous && + git branch ambiguous && + echo " ambiguous" >expect && + git branch --list ambiguous >actual && + test_cmp expect actual +' + test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 859b237..19a2823 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -50,6 +50,8 @@ test_atom() { test_atom head refname refs/heads/master test_atom head refname:short master +test_atom head refname:strip=1 heads/master +test_atom head refname:strip=2 master test_atom head upstream refs/remotes/origin/master test_atom head upstream:short origin/master test_atom head push refs/remotes/myfork/master @@ -132,6 +134,16 @@ test_expect_success 'Check invalid atoms names are errors' ' test_must_fail git for-each-ref --format="%(INVALID)" refs/heads ' +test_expect_success 'arguments to :strip must be positive integers' ' + test_must_fail git for-each-ref --format="%(refname:strip=0)" && + test_must_fail git for-each-ref --format="%(refname:strip=-1)" && + test_must_fail git for-each-ref --format="%(refname:strip=foo)" +' + +test_expect_success 'stripping refnames too far gives an error' ' + test_must_fail git for-each-ref --format="%(refname:strip=3)" +' + test_expect_success 'Check format specifiers are ignored in naming date atoms' ' git for-each-ref --format="%(authordate)" refs/heads && git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads && diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 2797f22..cf3469b 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -1558,4 +1558,12 @@ test_expect_success '--no-merged show unmerged tags' ' test_cmp expect actual ' +test_expect_success 'ambiguous branch/tags not marked' ' + git tag ambiguous && + git branch ambiguous && + echo ambiguous >expect && + git tag -l ambiguous >actual && + test_cmp expect actual +' + test_done -- 2.7.0.427.g4c6e021 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-26 2:37 ` Jeff King 2016-01-26 3:00 ` Jeff King @ 2016-01-26 3:25 ` Junio C Hamano 1 sibling, 0 replies; 19+ messages in thread From: Junio C Hamano @ 2016-01-26 3:25 UTC (permalink / raw) To: Jeff King; +Cc: Pete Harlan, Karthik Nayak, git Jeff King <peff@peff.net> writes: > What does this do: > > git for-each-ref --format='%(refname:remove-standard-prefix)' > > ? > > Is there no standard prefix, because we are not in branch/tag? Does it > remove well-known prefixes like "refs/heads/", "refs/tags/", etc? Does > it remove the first two components (e.g., what happens to > "refs/foo/bar")? If so, what happens to "refs/stash"? I think our mails crossed. I envisioned that the documentation would go something like this: remove-standard-prefix:: When the refname begins with one of the well-known prefixes, the prefix is stripped from it (and when it does not start with any of the well-known prefixes, the refname is left as-is). "refs/heads/", "refs/remotes/" and "refs/tags/" are the well-known prefixes that are removed (as this modifier is primarily meant to allow users emulate the listing modes of "git tag" and "git branch") but this set may change over time (we may teach it to remove "refs/changes/" in later versions of Git, for example). > Yeah, I am definitely on board with trying to do the most minimal thing > for the regression fix and worry about more advanced concerns later (if > at all). It seems to me that "error out" is a pretty minimal behavior, > though, and one that doesn't lock us into any behaviors (i.e., it is > generally OK to take something that did not work at all before, and give > it new useful behavior; it is not OK to change something that did > something useful before). The thing that worried me the most is that "strip=<n>" is a very intuitive and nice notation that end users would want to use it beyond emulating "git tag" literally, and one behaviour we happen to pick, be it "error out" or "leave it intact", would have a high chance of being not the most useful one. If we define it to error out, somebody somewhere will abuse it to "ensure that all branch names are at least two levels deep" or something we do not anticipate now and start relying on the "erroring out" behaviour, and then complain when we later "allow it to do something more useful" that it no longer errors out. Having said all that, I think I can live with "strip=<n>" that happens to pick a single behaviour that we pick with the best knowledge we have right now. If we fear the compatiblity wart so seriously, we can always add a separate "strip-nofail=<n>" variant that gives a different behaviour from "strip=<n>" that errors out. Another useful variant might be "strip <n> levels if we can, otherwise pretend that the ref did not even pass the filtering criteria and do not show anything about the ref on the output", by the way. > So what about this: > > 1. Implement ":strip=<n>" and use it from git-tag. > > 2. Have it error out on a ref with fewer than <n> components. This > should be impossible to trigger via "git-tag" with the default > format. > > 3. Explicitly document that the behavior for values of <n> that are > not positive integers is undefined and subject to change (or > alternatively, we can simply error out). > > > That _is_ user-visible, but it seems like "strip" is a fairly flexible > mechanism by itself (enough that I do not mind living with it forever), > and we haven't made any promises about the ambiguous parts. OK. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] tag: do not show ambiguous tag names as "tags/foo" 2016-01-24 22:19 ` Junio C Hamano 2016-01-24 22:27 ` Jeff King @ 2016-01-24 23:05 ` Jeff King 2016-01-24 23:08 ` [PATCH 1/2] t6300: use test_atom for some un-modern tests Jeff King 2016-01-24 23:08 ` [PATCH 2/2] tag: do not show ambiguous tag names as "tags/foo" Jeff King 1 sibling, 2 replies; 19+ messages in thread From: Jeff King @ 2016-01-24 23:05 UTC (permalink / raw) To: Junio C Hamano; +Cc: Pete Harlan, Karthik Nayak, git On Sun, Jan 24, 2016 at 02:19:52PM -0800, Junio C Hamano wrote: > Also there is "what happens if the expected prefix is not there?" > question. Perhaps strip=2 can be defined to "strip 2 levels of > hierarchy prefix no matter what that is", and strip refs/tags/foo, > refs/heads/foo and refs/remotes/origin/foo to foo, foo, origin/foo, > respectively? The filtering natively done by the listing mode of > "branch" and "tags" would ensure the prefixes are always what we > implicitly expect, so the case we need to worry about how we should > signal errors becomes "what if there are not enough levels". That > may be simpler to handle. The "not enough levels" question is interesting. Here are the options I can think of: 1. Signal an error and die. Safest, but potentially annoying. 2. Strip everything and print a blank. Logically consistent, but the output is not particularly useful, and may introduce parsing confusion. 3. Strip nothing (i.e., "%(refname:strip=4)" on "refs/heads/master" remains "refs/heads/master"). Easy to explain, and provides useful-ish output. The output is technically ambiguous, though (was it "refs/heads/foo/refs/heads/master", or just "refs/heads/master"?). 4. Strip all but the final entry. Kind-of also useful, but like (3), ambiguous. I implemented (3) semi-arbitrarily (mostly because it was only slightly less easy than (2), and (2) seems kind of crazy). There is also a question of what "strip=-1" or "strip=foobar" should do. They are both equivalent to strip=0 in my implementation, but we could also report an error. I ended up doing a preparatory patch for t6300; I wanted to add new tests there, and the existing content was so messy I couldn't bear it. [1/2]: t6300: use test_atom for some un-modern tests [2/2]: tag: do not show ambiguous tag names as "tags/foo" -Peff ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 1/2] t6300: use test_atom for some un-modern tests 2016-01-24 23:05 ` Jeff King @ 2016-01-24 23:08 ` Jeff King 2016-01-24 23:08 ` [PATCH 2/2] tag: do not show ambiguous tag names as "tags/foo" Jeff King 1 sibling, 0 replies; 19+ messages in thread From: Jeff King @ 2016-01-24 23:08 UTC (permalink / raw) To: Junio C Hamano; +Cc: Pete Harlan, Karthik Nayak, git Because this script has to test so many formatters, we have the nice "test_atom" helper, but we don't use it consistently. Let's do so. This is shorter, gets rid of some tests that have their "expected" setup outside of a test_expect_success block, and lets us organize the changes better (e.g., putting "refname:short" near "refname"). We also expand the "%(push)" tests a little to match the "%(upstream)" ones. Signed-off-by: Jeff King <peff@peff.net> --- I suspect one could do even more cleanup in this script, but this looked like all of the low-hanging fruit (some of the others are order-dependent, for example). One of the blocks that goes away must be setting a new record for "pointless use of cat". See if you can spot it. :) t/t6300-for-each-ref.sh | 62 ++++++++----------------------------------------- 1 file changed, 10 insertions(+), 52 deletions(-) diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 03873b0..859b237 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -49,11 +49,15 @@ test_atom() { } test_atom head refname refs/heads/master +test_atom head refname:short master test_atom head upstream refs/remotes/origin/master +test_atom head upstream:short origin/master test_atom head push refs/remotes/myfork/master +test_atom head push:short myfork/master test_atom head objecttype commit test_atom head objectsize 171 test_atom head objectname $(git rev-parse refs/heads/master) +test_atom head objectname:short $(git rev-parse --short refs/heads/master) test_atom head tree $(git rev-parse refs/heads/master^{tree}) test_atom head parent '' test_atom head numparent 0 @@ -86,11 +90,13 @@ test_atom head contents 'Initial test_atom head HEAD '*' test_atom tag refname refs/tags/testtag +test_atom tag refname:short testtag test_atom tag upstream '' test_atom tag push '' test_atom tag objecttype tag test_atom tag objectsize 154 test_atom tag objectname $(git rev-parse refs/tags/testtag) +test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag) test_atom tag tree '' test_atom tag parent '' test_atom tag numparent '' @@ -338,47 +344,14 @@ for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do " done -cat >expected <<\EOF -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) >actual && - test_cmp expected actual -' - -cat >expected <<EOF -origin/master -EOF - -test_expect_success 'Check short upstream format' ' - git for-each-ref --format="%(upstream:short)" refs/heads >actual && - test_cmp expected actual -' - test_expect_success 'setup for upstream:track[short]' ' test_commit two ' -cat >expected <<EOF -[ahead 1] -EOF - -test_expect_success 'Check upstream:track format' ' - git for-each-ref --format="%(upstream:track)" refs/heads >actual && - test_cmp expected actual -' - -cat >expected <<EOF -> -EOF - -test_expect_success 'Check upstream:trackshort format' ' - git for-each-ref --format="%(upstream:trackshort)" refs/heads >actual && - test_cmp expected actual -' +test_atom head upstream:track '[ahead 1]' +test_atom head upstream:trackshort '>' +test_atom head push:track '[ahead 1]' +test_atom head push:trackshort '>' test_expect_success 'Check that :track[short] cannot be used with other atoms' ' test_must_fail git for-each-ref --format="%(refname:track)" 2>/dev/null && @@ -398,21 +371,6 @@ test_expect_success 'Check that :track[short] works when upstream is invalid' ' test_cmp expected actual ' -test_expect_success '%(push) supports tracking specifiers, too' ' - echo "[ahead 1]" >expected && - git for-each-ref --format="%(push:track)" refs/heads >actual && - test_cmp expected actual -' - -cat >expected <<EOF -$(git rev-parse --short HEAD) -EOF - -test_expect_success 'Check short objectname format' ' - git for-each-ref --format="%(objectname:short)" refs/heads >actual && - test_cmp expected actual -' - test_expect_success 'Check for invalid refname format' ' test_must_fail git for-each-ref --format="%(refname:INVALID)" ' -- 2.7.0.427.g4c6e021 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 2/2] tag: do not show ambiguous tag names as "tags/foo" 2016-01-24 23:05 ` Jeff King 2016-01-24 23:08 ` [PATCH 1/2] t6300: use test_atom for some un-modern tests Jeff King @ 2016-01-24 23:08 ` Jeff King 2016-01-26 0:04 ` Junio C Hamano 1 sibling, 1 reply; 19+ messages in thread From: Jeff King @ 2016-01-24 23:08 UTC (permalink / raw) To: Junio C Hamano; +Cc: Pete Harlan, Karthik Nayak, git Since b7cc53e9 (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 b7cc53e9 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 <peff@peff.net> --- Documentation/git-for-each-ref.txt | 6 +++++- Documentation/git-tag.txt | 2 +- builtin/tag.c | 4 ++-- ref-filter.c | 13 ++++++++++++- t/t3203-branch-output.sh | 8 ++++++++ t/t6300-for-each-ref.sh | 4 ++++ t/t7004-tag.sh | 8 ++++++++ 7 files changed, 40 insertions(+), 5 deletions(-) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 06208c4..f15c817 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -92,7 +92,11 @@ refname:: The name of the ref (the part after $GIT_DIR/). For a non-ambiguous short name of the ref append `:short`. The option core.warnAmbiguousRefs is used to select the strict - abbreviation mode. + abbreviation mode. If `strip=<N>` is appended, strips `<N>` + slash-separated path components from the front of the refname + (e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`. + If the ref has fewer components than `<N>`, the whole, + unstripped `%(refname)` is printed. objecttype:: The type of the object (`blob`, `tree`, `commit`, `tag`). diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 7220e5e..abab481 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -163,7 +163,7 @@ This option is only applicable when listing tags without annotation lines. A string that interpolates `%(fieldname)` from the object pointed at by a ref being shown. The format is the same as that of linkgit:git-for-each-ref[1]. When unspecified, - defaults to `%(refname:short)`. + defaults to `%(refname:strip=2)`. --[no-]merged [<commit>]:: Only list tags whose tips are reachable, or not reachable diff --git a/builtin/tag.c b/builtin/tag.c index 8db8c87..1705c94 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -44,11 +44,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con if (!format) { if (filter->lines) { to_free = xstrfmt("%s %%(contents:lines=%d)", - "%(align:15)%(refname:short)%(end)", + "%(align:15)%(refname:strip=2)%(end)", filter->lines); format = to_free; } else - format = "%(refname:short)"; + format = "%(refname:strip=2)"; } verify_ref_format(format); diff --git a/ref-filter.c b/ref-filter.c index 7bef7f8..9f54adc 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -909,12 +909,23 @@ static void populate_value(struct ref_array_item *ref) formatp = strchr(name, ':'); if (formatp) { int num_ours, num_theirs; + const char *arg; formatp++; if (!strcmp(formatp, "short")) refname = shorten_unambiguous_ref(refname, warn_ambiguous_refs); - else if (!strcmp(formatp, "track") && + else if (skip_prefix(formatp, "strip=", &arg)) { + int strip = atoi(arg); + const char *start = refname; + while (strip && *start) { + if (*start == '/') + strip--; + start++; + } + if (!strip) + refname = start; + } else if (!strcmp(formatp, "track") && (starts_with(name, "upstream") || starts_with(name, "push"))) { diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index d3913f9..4261403 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -176,4 +176,12 @@ test_expect_success 'git branch --points-at option' ' test_cmp expect actual ' +test_expect_success 'ambiguous branch/tag not marked' ' + git tag ambiguous && + git branch ambiguous && + echo " ambiguous" >expect && + git branch --list ambiguous >actual && + test_cmp expect actual +' + test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 859b237..1f3abeb 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -50,6 +50,10 @@ test_atom() { test_atom head refname refs/heads/master test_atom head refname:short master +test_atom head refname:strip=0 refs/heads/master +test_atom head refname:strip=1 heads/master +test_atom head refname:strip=2 master +test_atom head refname:strip=3 refs/heads/master test_atom head upstream refs/remotes/origin/master test_atom head upstream:short origin/master test_atom head push refs/remotes/myfork/master diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 2797f22..cf3469b 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -1558,4 +1558,12 @@ test_expect_success '--no-merged show unmerged tags' ' test_cmp expect actual ' +test_expect_success 'ambiguous branch/tags not marked' ' + git tag ambiguous && + git branch ambiguous && + echo ambiguous >expect && + git tag -l ambiguous >actual && + test_cmp expect actual +' + test_done -- 2.7.0.427.g4c6e021 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 2/2] tag: do not show ambiguous tag names as "tags/foo" 2016-01-24 23:08 ` [PATCH 2/2] tag: do not show ambiguous tag names as "tags/foo" Jeff King @ 2016-01-26 0:04 ` Junio C Hamano 2016-01-26 4:25 ` Karthik Nayak 0 siblings, 1 reply; 19+ messages in thread From: Junio C Hamano @ 2016-01-26 0:04 UTC (permalink / raw) To: Jeff King, Karthik Nayak; +Cc: Pete Harlan, git Jeff King <peff@peff.net> writes: > Since b7cc53e9 (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 b7cc53e9 switched > git-tag to use ref-filter's "%(refname:short)" output > formatting, which was adapted from for-each-ref. > ... Karthik, getting a fix in for "git tag" regression is more important than the topics parked in 'pu', so I'll queue this patch in the early part of 'pu'. I personally feel that "refname:strip=<n>" would be a good mechanism for end users to specify a custom format, and it is unclear to me what should happen when there are not enough elements to be stripped, so I do not think we want to cast the "we will show the whole thing" decision in stone prematurely only because we want to push out the regression fix soon. So I may ask Jeff to rework this one (or I may end up trying to do so myself) not to squat on the nice strip=<n> notation. refname:strip-standard-prefix that removes the known prefix ("refs/heads", "refs/remotes" and "refs/tags") if present and does not touch the refname otherwise would leave us more time to decide what strip=<n> should do in the error case. Unfortunately, this means kn/ref-filter-atom-parsing topic from you that were parked on 'pu' must be ejected for now, as any change in this area overlaps with it, and the atom parsing code would need to be updated to learn about the new attribute of the 'refname' atom (be it 'remove-prefix=<glob>', 'strip=<n>', or something else) that we would decide to use for the regression fix anyway. Thanks. > Documentation/git-for-each-ref.txt | 6 +++++- > Documentation/git-tag.txt | 2 +- > builtin/tag.c | 4 ++-- > ref-filter.c | 13 ++++++++++++- > t/t3203-branch-output.sh | 8 ++++++++ > t/t6300-for-each-ref.sh | 4 ++++ > t/t7004-tag.sh | 8 ++++++++ > 7 files changed, 40 insertions(+), 5 deletions(-) > > diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt > index 06208c4..f15c817 100644 > --- a/Documentation/git-for-each-ref.txt > +++ b/Documentation/git-for-each-ref.txt > @@ -92,7 +92,11 @@ refname:: > The name of the ref (the part after $GIT_DIR/). > For a non-ambiguous short name of the ref append `:short`. > The option core.warnAmbiguousRefs is used to select the strict > - abbreviation mode. > + abbreviation mode. If `strip=<N>` is appended, strips `<N>` > + slash-separated path components from the front of the refname > + (e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`. > + If the ref has fewer components than `<N>`, the whole, > + unstripped `%(refname)` is printed. > > objecttype:: > The type of the object (`blob`, `tree`, `commit`, `tag`). > diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt > index 7220e5e..abab481 100644 > --- a/Documentation/git-tag.txt > +++ b/Documentation/git-tag.txt > @@ -163,7 +163,7 @@ This option is only applicable when listing tags without annotation lines. > A string that interpolates `%(fieldname)` from the object > pointed at by a ref being shown. The format is the same as > that of linkgit:git-for-each-ref[1]. When unspecified, > - defaults to `%(refname:short)`. > + defaults to `%(refname:strip=2)`. > > --[no-]merged [<commit>]:: > Only list tags whose tips are reachable, or not reachable > diff --git a/builtin/tag.c b/builtin/tag.c > index 8db8c87..1705c94 100644 > --- a/builtin/tag.c > +++ b/builtin/tag.c > @@ -44,11 +44,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con > if (!format) { > if (filter->lines) { > to_free = xstrfmt("%s %%(contents:lines=%d)", > - "%(align:15)%(refname:short)%(end)", > + "%(align:15)%(refname:strip=2)%(end)", > filter->lines); > format = to_free; > } else > - format = "%(refname:short)"; > + format = "%(refname:strip=2)"; > } > > verify_ref_format(format); > diff --git a/ref-filter.c b/ref-filter.c > index 7bef7f8..9f54adc 100644 > --- a/ref-filter.c > +++ b/ref-filter.c > @@ -909,12 +909,23 @@ static void populate_value(struct ref_array_item *ref) > formatp = strchr(name, ':'); > if (formatp) { > int num_ours, num_theirs; > + const char *arg; > > formatp++; > if (!strcmp(formatp, "short")) > refname = shorten_unambiguous_ref(refname, > warn_ambiguous_refs); > - else if (!strcmp(formatp, "track") && > + else if (skip_prefix(formatp, "strip=", &arg)) { > + int strip = atoi(arg); > + const char *start = refname; > + while (strip && *start) { > + if (*start == '/') > + strip--; > + start++; > + } > + if (!strip) > + refname = start; > + } else if (!strcmp(formatp, "track") && > (starts_with(name, "upstream") || > starts_with(name, "push"))) { > > diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh > index d3913f9..4261403 100755 > --- a/t/t3203-branch-output.sh > +++ b/t/t3203-branch-output.sh > @@ -176,4 +176,12 @@ test_expect_success 'git branch --points-at option' ' > test_cmp expect actual > ' > > +test_expect_success 'ambiguous branch/tag not marked' ' > + git tag ambiguous && > + git branch ambiguous && > + echo " ambiguous" >expect && > + git branch --list ambiguous >actual && > + test_cmp expect actual > +' > + > test_done > diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh > index 859b237..1f3abeb 100755 > --- a/t/t6300-for-each-ref.sh > +++ b/t/t6300-for-each-ref.sh > @@ -50,6 +50,10 @@ test_atom() { > > test_atom head refname refs/heads/master > test_atom head refname:short master > +test_atom head refname:strip=0 refs/heads/master > +test_atom head refname:strip=1 heads/master > +test_atom head refname:strip=2 master > +test_atom head refname:strip=3 refs/heads/master > test_atom head upstream refs/remotes/origin/master > test_atom head upstream:short origin/master > test_atom head push refs/remotes/myfork/master > diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh > index 2797f22..cf3469b 100755 > --- a/t/t7004-tag.sh > +++ b/t/t7004-tag.sh > @@ -1558,4 +1558,12 @@ test_expect_success '--no-merged show unmerged tags' ' > test_cmp expect actual > ' > > +test_expect_success 'ambiguous branch/tags not marked' ' > + git tag ambiguous && > + git branch ambiguous && > + echo ambiguous >expect && > + git tag -l ambiguous >actual && > + test_cmp expect actual > +' > + > test_done ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 2/2] tag: do not show ambiguous tag names as "tags/foo" 2016-01-26 0:04 ` Junio C Hamano @ 2016-01-26 4:25 ` Karthik Nayak 0 siblings, 0 replies; 19+ messages in thread From: Karthik Nayak @ 2016-01-26 4:25 UTC (permalink / raw) To: Junio C Hamano; +Cc: Jeff King, Pete Harlan, Git On Tue, Jan 26, 2016 at 5:34 AM, Junio C Hamano <gitster@pobox.com> wrote: > Jeff King <peff@peff.net> writes: > >> Since b7cc53e9 (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 b7cc53e9 switched >> git-tag to use ref-filter's "%(refname:short)" output >> formatting, which was adapted from for-each-ref. >> ... > > Karthik, getting a fix in for "git tag" regression is more important > than the topics parked in 'pu', so I'll queue this patch in the > early part of 'pu'. > > I personally feel that "refname:strip=<n>" would be a good mechanism > for end users to specify a custom format, and it is unclear to me > what should happen when there are not enough elements to be > stripped, so I do not think we want to cast the "we will show the > whole thing" decision in stone prematurely only because we want to > push out the regression fix soon. So I may ask Jeff to rework this > one (or I may end up trying to do so myself) not to squat on the > nice strip=<n> notation. refname:strip-standard-prefix that removes > the known prefix ("refs/heads", "refs/remotes" and "refs/tags") if > present and does not touch the refname otherwise would leave us more > time to decide what strip=<n> should do in the error case. > > Unfortunately, this means kn/ref-filter-atom-parsing topic from you > that were parked on 'pu' must be ejected for now, as any change in > this area overlaps with it, and the atom parsing code would need to > be updated to learn about the new attribute of the 'refname' atom > (be it 'remove-prefix=<glob>', 'strip=<n>', or something else) that > we would decide to use for the regression fix anyway. That should be fine, there are still changes to be done there so I'll rebase on this and send that series. -- Regards, Karthik Nayak ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2016-01-26 4:25 UTC | newest] Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2016-01-23 23:56 Regression? Ambiguous tags listed as "tags/<foo>" Pete Harlan 2016-01-24 7:12 ` [PATCH] tag: do not show ambiguous tag names as "tags/foo" Jeff King 2016-01-24 7:18 ` Jeff King 2016-01-24 22:19 ` Junio C Hamano 2016-01-24 22:27 ` Jeff King 2016-01-24 23:39 ` Eric Sunshine 2016-01-25 9:56 ` Jeff King 2016-01-25 2:26 ` Junio C Hamano 2016-01-25 10:01 ` Jeff King 2016-01-25 19:29 ` Karthik Nayak 2016-01-25 20:21 ` Junio C Hamano 2016-01-26 2:37 ` Jeff King 2016-01-26 3:00 ` Jeff King 2016-01-26 3:25 ` Junio C Hamano 2016-01-24 23:05 ` Jeff King 2016-01-24 23:08 ` [PATCH 1/2] t6300: use test_atom for some un-modern tests Jeff King 2016-01-24 23:08 ` [PATCH 2/2] tag: do not show ambiguous tag names as "tags/foo" Jeff King 2016-01-26 0:04 ` Junio C Hamano 2016-01-26 4:25 ` Karthik Nayak
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.