{"thread":{"id":"15301","subject":"[PATCH] for-each-ref: `:short` format for `refname`","startedAt":"2008-08-31T12:41:07Z","lastAt":"2008-09-09T08:57:42Z","messageCount":26,"participants":["Bert Wesarg","SZEDER Gábor","Shawn O. Pearce","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"89310","messageId":"1220186467-24623-1-git-send-email-bert.wesarg@googlemail.com","threadId":"15301","inReplyTo":"7vprnpbqmo.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-08-31T12:41:07Z","receivedAt":"2008-08-31T12:41:07Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"This strips from the refname the common directory prefix with the\nmatched pattern.\n\nThis is particular usefull for bash completion, to get refs without\n`refs/heads` or `refs/tags`.\n\nSigned-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n\n---\n Documentation/git-for-each-ref.txt |    5 ++\n builtin-for-each-ref.c             |   74 +++++++++++++++++++++++++++++++----\n t/t6300-for-each-ref.sh            |   61 +++++++++++++++++++++++++++++\n 3 files changed, 131 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex eae6c0e..deeae79 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -74,6 +74,11 @@ For all objects, the following names can be used:\n \n refname::\n \tThe name of the ref (the part after $GIT_DIR/).\n+\tFor a short name of the ref append `:short`. This will strip\n+\tthe common directory prefix with the pattern which matches this ref.\n+\tI.e. for a the pattern `refs/heads` you get `master`, or for\n+\t`refs/tags/v1.5.[01].*` you get `v1.5.[01].*`.\n+\tThis is particular usefull for bash completion.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 21e92bb..946f79b 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -31,6 +31,7 @@ struct ref_sort {\n \n struct refinfo {\n \tchar *refname;\n+\tconst char *pattern; /* the pattern which matched this ref */\n \tunsigned char objectname[20];\n \tstruct atom_value *value;\n };\n@@ -546,6 +547,40 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v\n }\n \n /*\n+ * Use the matched pattern from ref to shorten the refname\n+ */\n+static char *get_short_ref(struct refinfo *ref)\n+{\n+\tint rlen, plen, common = 0;\n+\n+\tif (!ref->pattern)\n+\t\treturn ref->refname;\n+\n+\trlen = strlen(ref->refname);\n+\tplen = strlen(ref->pattern);\n+\n+\tif ((plen <= rlen) &&\n+\t    !strncmp(ref->refname, ref->pattern, plen) &&\n+\t    (ref->refname[plen] == '\\0' ||\n+\t     ref->refname[plen] == '/' ||\n+\t     ref->pattern[plen - 1] == '/')) {\n+\t\tcommon = plen + (ref->refname[plen] == '/');\n+\t\t/* prevent stripping the whole refname */\n+\t\tif (common == rlen)\n+\t\t\tcommon = 0;\n+\t} else {\n+\t\t/* find the first wildcard and go back to the previous '/' */\n+\t\tcommon = strcspn(ref->pattern, \"*?[\");\n+\t\twhile (common >= 0 && ref->pattern[common] != '/')\n+\t\t\t--common;\n+\t\tcommon++;\n+\t}\n+\n+\treturn ref->refname + common;\n+}\n+\n+\n+/*\n  * Parse the object referred by ref, and grab needed value.\n  */\n static void populate_value(struct refinfo *ref)\n@@ -570,13 +605,33 @@ static void populate_value(struct refinfo *ref)\n \tfor (i = 0; i < used_atom_cnt; i++) {\n \t\tconst char *name = used_atom[i];\n \t\tstruct atom_value *v = &ref->value[i];\n-\t\tif (!strcmp(name, \"refname\"))\n-\t\t\tv->s = ref->refname;\n-\t\telse if (!strcmp(name, \"*refname\")) {\n-\t\t\tint len = strlen(ref->refname);\n-\t\t\tchar *s = xmalloc(len + 4);\n-\t\t\tsprintf(s, \"%s^{}\", ref->refname);\n-\t\t\tv->s = s;\n+\t\tint deref = 0;\n+\t\tif (*name == '*') {\n+\t\t\tderef = 1;\n+\t\t\tname++;\n+\t\t}\n+\t\tif (!prefixcmp(name, \"refname\")) {\n+\t\t\tconst char *formatp = strchr(name, ':');\n+\t\t\tconst char *refname = ref->refname;\n+\n+\t\t\t/* look for \"short\" refname format */\n+\t\t\tif (formatp) {\n+\t\t\t\tformatp++;\n+\t\t\t\tif (!strcmp(formatp, \"short\"))\n+\t\t\t\t\trefname = get_short_ref(ref);\n+\t\t\t\telse\n+\t\t\t\t\tdie(\"unknown refname format %s\",\n+\t\t\t\t\t    formatp);\n+\t\t\t}\n+\n+\t\t\tif (!deref)\n+\t\t\t\tv->s = refname;\n+\t\t\telse {\n+\t\t\t\tint len = strlen(refname);\n+\t\t\t\tchar *s = xmalloc(len + 4);\n+\t\t\t\tsprintf(s, \"%s^{}\", refname);\n+\t\t\t\tv->s = s;\n+\t\t\t}\n \t\t}\n \t}\n \n@@ -641,9 +696,9 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f\n \tstruct grab_ref_cbdata *cb = cb_data;\n \tstruct refinfo *ref;\n \tint cnt;\n+\tconst char **pattern = cb->grab_pattern;\n \n-\tif (*cb->grab_pattern) {\n-\t\tconst char **pattern;\n+\tif (*pattern) {\n \t\tint namelen = strlen(refname);\n \t\tfor (pattern = cb->grab_pattern; *pattern; pattern++) {\n \t\t\tconst char *p = *pattern;\n@@ -668,6 +723,7 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f\n \t */\n \tref = xcalloc(1, sizeof(*ref));\n \tref->refname = xstrdup(refname);\n+\tref->pattern = *pattern;\n \thashcpy(ref->objectname, sha1);\n \n \tcnt = cb->grab_cnt;\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 8ced593..a4a2fd3 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -262,6 +262,67 @@ for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n \t\"\n done\n \n+cat >expected <<\\EOF\n+master\n+testtag\n+master\n+testtag\n+EOF\n+\n+test_expect_success 'Check short refname format' '\n+\t(git for-each-ref --format=\"%(refname:short)\" refs/heads &&\n+\tgit for-each-ref --format=\"%(refname:short)\" refs/tags &&\n+\tgit for-each-ref --format=\"%(refname:short)\" refs/heads/ &&\n+\tgit for-each-ref --format=\"%(refname:short)\" refs/tags/) >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+master\n+testtag\n+EOF\n+\n+test_expect_success 'Check short refname format with multiple patterns' '\n+\t(git for-each-ref --format=\"%(refname:short)\" refs/heads refs/tags) >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/heads/master\n+refs/tags/testtag\n+EOF\n+\n+test_expect_success 'Check short refname format without patterns' '\n+\tgit for-each-ref --format=\"%(refname:short)\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check for invalid refname format' '\n+\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n+'\n+\n+cat >expected <<\\EOF\n+heads/master\n+master\n+testtag\n+EOF\n+\n+test_expect_success 'Check short refname format with wildcard pattern' '\n+\t(git for-each-ref --format=\"%(refname:short)\" refs/*/m* &&\n+\tgit for-each-ref --format=\"%(refname:short)\" refs/heads/?aster &&\n+\tgit for-each-ref --format=\"%(refname:short)\" refs/tags/t[aeiou]sttag) >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+refs/heads/master\n+EOF\n+\n+test_expect_success 'Check disabled short refname format with exact pattern' '\n+\t(git for-each-ref --format=\"%(refname:short)\" refs/heads/master) >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'an unusual tag with an incomplete line' '\n \n \tgit tag -m \"bogo\" bogo &&\n-- \ntg: (445cac1..) t/for-each-ref-refshort (depends on: master)\n"},{"id":"89416","messageId":"20080901131523.GA6739@neumann","threadId":"15301","inReplyTo":"1220186467-24623-1-git-send-email-bert.wesarg@googlemail.com","subject":"Re: [PATCH] for-each-ref: `:short` format for `refname`","fromName":"SZEDER Gábor","fromEmail":"szeder@ira.uka.de","sentAt":"2008-09-01T13:15:23Z","receivedAt":"2008-09-01T13:15:23Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"Hi,\n\nOn Sun, Aug 31, 2008 at 02:41:07PM +0200, Bert Wesarg wrote:\n> This strips from the refname the common directory prefix with the\n> matched pattern.\n> \n> This is particular usefull for bash completion, to get refs without\n> `refs/heads` or `refs/tags`.\n\n>  refname::\n>  \tThe name of the ref (the part after $GIT_DIR/).\n> +\tFor a short name of the ref append `:short`. This will strip\n> +\tthe common directory prefix with the pattern which matches this ref.\n> +\tI.e. for a the pattern `refs/heads` you get `master`, or for\n> +\t`refs/tags/v1.5.[01].*` you get `v1.5.[01].*`.\n> +\tThis is particular usefull for bash completion.\nShould this last sentence really belong to the documentation?\n\nFurthermore, I think ':strip' better describes what this format\nactually does.  Even you have used the word 'strip' in the commit\nmessage and in the documentation as well.\n\n\nAs far as bash completion is concerned, I'm for it, as it does exactly\nwhat the completion script needs to perform better, it doesn't have\nthose conceptual issues 'refbasename' has, and it's only a tad slower\nthan 'refbasename'.\n\nHowever, if we consider possible use cases other than bash completion,\nI don't know which one is more useful.  For example, if you have two\nbranches 'foo/bar' and 'foo/baz', then 'git merge $(git for-each-ref\n--format=%(refbasename) refs/heads/foo)' will work as expected, but\n'refname:short' not, as it will output only 'bar' and 'baz' which 'git\nmerge' can not find.\n\n\nBest,\nGábor\n"},{"id":"89418","messageId":"36ca99e90809010713h7c673d10j6addd1624a655371@mail.gmail.com","threadId":"15301","inReplyTo":"20080901131523.GA6739@neumann","subject":"Re: [PATCH] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-01T14:13:15Z","receivedAt":"2008-09-01T14:13:15Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Mon, Sep 1, 2008 at 15:15, SZEDER Gábor <szeder@ira.uka.de> wrote:\n> Hi,\n>\n> On Sun, Aug 31, 2008 at 02:41:07PM +0200, Bert Wesarg wrote:\n>> This strips from the refname the common directory prefix with the\n>> matched pattern.\n>>\n>> This is particular usefull for bash completion, to get refs without\n>> `refs/heads` or `refs/tags`.\n>\n>>  refname::\n>>       The name of the ref (the part after $GIT_DIR/).\n>> +     For a short name of the ref append `:short`. This will strip\n>> +     the common directory prefix with the pattern which matches this ref.\n>> +     I.e. for a the pattern `refs/heads` you get `master`, or for\n>> +     `refs/tags/v1.5.[01].*` you get `v1.5.[01].*`.\n>> +     This is particular usefull for bash completion.\n> Should this last sentence really belong to the documentation?\nAt least it is not the only example in the documentation.\n\n>\n> Furthermore, I think ':strip' better describes what this format\n> actually does.  Even you have used the word 'strip' in the commit\n> message and in the documentation as well.\nTrue, I'm ok with this proposal.\n\n>\n>\n> As far as bash completion is concerned, I'm for it, as it does exactly\n> what the completion script needs to perform better, it doesn't have\n> those conceptual issues 'refbasename' has, and it's only a tad slower\n> than 'refbasename'.\n>\n> However, if we consider possible use cases other than bash completion,\n> I don't know which one is more useful.  For example, if you have two\n> branches 'foo/bar' and 'foo/baz', then 'git merge $(git for-each-ref\n> --format=%(refbasename) refs/heads/foo)' will work as expected, but\n> 'refname:short' not, as it will output only 'bar' and 'baz' which 'git\n> merge' can not find.\nYeah, thats an disadvantage and I thought about this, too. But I have\nno particular opinion about it.\n\nRegards,\nBert\n\n> Best,\n> Gábor\n"},{"id":"89429","messageId":"36ca99e90809011052s568fa6e4y89e56769f63806c1@mail.gmail.com","threadId":"15301","inReplyTo":"36ca99e90809010713h7c673d10j6addd1624a655371@mail.gmail.com","subject":"Re: [PATCH] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-01T17:52:03Z","receivedAt":"2008-09-01T17:52:03Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Mon, Sep 1, 2008 at 16:13, Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n> On Mon, Sep 1, 2008 at 15:15, SZEDER Gábor <szeder@ira.uka.de> wrote:\n>> However, if we consider possible use cases other than bash completion,\n>> I don't know which one is more useful.  For example, if you have two\n>> branches 'foo/bar' and 'foo/baz', then 'git merge $(git for-each-ref\n>> --format=%(refbasename) refs/heads/foo)' will work as expected, but\n>> 'refname:short' not, as it will output only 'bar' and 'baz' which 'git\n>> merge' can not find.\n> Yeah, thats an disadvantage and I thought about this, too. But I have\n> no particular opinion about it.\nOk, I have a new idea, which could be made all happy:\n\nIMHO the goal of this new format for refname should be, that it can be\nused as an ref on the command line. This isn't given with my current\n:short proposal (which I call :strip as of now), as Gábor showed. What\nwe need is the reverse of what happened with refnames given on the\ncommand line to commands like checkout/merge/... The only thing that\ncomes near to this is this from refs.c:\n\n    const char *ref_rev_parse_rules[] = {\n            \"%.*s\",\n            \"refs/%.*s\",\n            \"refs/tags/%.*s\",\n            \"refs/heads/%.*s\",\n            \"refs/remotes/%.*s\",\n            \"refs/remotes/%.*s/HEAD\",\n            NULL\n    };\n\nWhich doesn't look very useful, because every ref from for_each_ref\nwould match rule one. So we probably need to try the reverse of this\nlist. Now my knowledge from git internals is really low, I don't know\nif this is sane. I know that this can't be bijective but at least the\nbash completion would be happy with this idea.\n\nComments, thoughts, brown paper bags...\n\nThanks,\nBert\n"},{"id":"89433","messageId":"20080901191051.GD7482@spearce.org","threadId":"15301","inReplyTo":"36ca99e90809011052s568fa6e4y89e56769f63806c1@mail.gmail.com","subject":"Re: [PATCH] for-each-ref: `:short` format for `refname`","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-09-01T19:10:51Z","receivedAt":"2008-09-01T19:10:51Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n> IMHO the goal of this new format for refname should be, that it can be\n> used as an ref on the command line. This isn't given with my current\n> :short proposal (which I call :strip as of now), as Gábor showed. What\n> we need is the reverse of what happened with refnames given on the\n> command line to commands like checkout/merge/... The only thing that\n> comes near to this is this from refs.c:\n> \n>     const char *ref_rev_parse_rules[] = {\n>             \"%.*s\",\n>             \"refs/%.*s\",\n>             \"refs/tags/%.*s\",\n>             \"refs/heads/%.*s\",\n>             \"refs/remotes/%.*s\",\n>             \"refs/remotes/%.*s/HEAD\",\n>             NULL\n>     };\n> \n> Which doesn't look very useful, because every ref from for_each_ref\n> would match rule one. So we probably need to try the reverse of this\n> list.\n\nYup.  If you search the list backwards and extract the part of the\nref that matches %.*s you'll get a name that other tools can find,\nand which is the shortest name possible.\n\nYou can still get ambiguous names.  Avoiding them requires going\nthrough all refs and building their short forms, then using the\nfull ref name for any ref which had more than one name shorten to\nthe same string.  Ugly, but implementable, and probably something\nthat should be considered.\n\n-- \nShawn.\n"},{"id":"89442","messageId":"36ca99e90809011410w646cc6eajb3063ea3501f173c@mail.gmail.com","threadId":"15301","inReplyTo":"20080901191051.GD7482@spearce.org","subject":"Re: [PATCH] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-01T21:10:38Z","receivedAt":"2008-09-01T21:10:38Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Mon, Sep 1, 2008 at 21:10, Shawn O. Pearce <spearce@spearce.org> wrote:\n> Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n>> IMHO the goal of this new format for refname should be, that it can be\n>> used as an ref on the command line. This isn't given with my current\n>> :short proposal (which I call :strip as of now), as Gábor showed. What\n>> we need is the reverse of what happened with refnames given on the\n>> command line to commands like checkout/merge/... The only thing that\n>> comes near to this is this from refs.c:\n>>\n>>     const char *ref_rev_parse_rules[] = {\n>>             \"%.*s\",\n>>             \"refs/%.*s\",\n>>             \"refs/tags/%.*s\",\n>>             \"refs/heads/%.*s\",\n>>             \"refs/remotes/%.*s\",\n>>             \"refs/remotes/%.*s/HEAD\",\n>>             NULL\n>>     };\n>>\n>> Which doesn't look very useful, because every ref from for_each_ref\n>> would match rule one. So we probably need to try the reverse of this\n>> list.\n>\n> Yup.  If you search the list backwards and extract the part of the\n> ref that matches %.*s you'll get a name that other tools can find,\n> and which is the shortest name possible.\n>\n> You can still get ambiguous names.  Avoiding them requires going\n> through all refs and building their short forms, then using the\n> full ref name for any ref which had more than one name shorten to\n> the same string.  Ugly, but implementable, and probably something\n> that should be considered.\nWhat about: try the list backwards until the first match, than try the\nmatched part (this what %.*s matched) with the forward list, if both\ngive the same pattern, its not disambiguous. If not try the next\npattern backwards.\n\nIts quadratic in the number of patterns, but this is maybe smaller\nthan scanning all refs (which may include a sort phase).\n\nBert\n> --\n> Shawn.\n>\n"},{"id":"89443","messageId":"7v7i9vv9n2.fsf@gitster.siamese.dyndns.org","threadId":"15301","inReplyTo":"36ca99e90809011410w646cc6eajb3063ea3501f173c@mail.gmail.com","subject":"Re: [PATCH] for-each-ref: `:short` format for `refname`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-01T21:28:17Z","receivedAt":"2008-09-01T21:28:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Bert Wesarg\" <bert.wesarg@googlemail.com> writes:\n\n> On Mon, Sep 1, 2008 at 21:10, Shawn O. Pearce <spearce@spearce.org> wrote:\n> ...\n>> You can still get ambiguous names.  Avoiding them requires going\n>> through all refs and building their short forms, then using the\n>> full ref name for any ref which had more than one name shorten to\n>> the same string.  Ugly, but implementable, and probably something\n>> that should be considered.\n>\n> What about: try the list backwards until the first match, than try the\n> matched part (this what %.*s matched) with the forward list, if both\n> give the same pattern, its not disambiguous. If not try the next\n> pattern backwards.\n\nHow does it catch the case where you have both 'xyzzy' branch and 'xyzzy'\ntag, which is the point of disambiguation issue Shawn raised?\n"},{"id":"89446","messageId":"36ca99e90809011444v3fca09c4o4d9dcf1a7249a00a@mail.gmail.com","threadId":"15301","inReplyTo":"7v7i9vv9n2.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-01T21:44:09Z","receivedAt":"2008-09-01T21:44:09Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Mon, Sep 1, 2008 at 23:28, Junio C Hamano <gitster@pobox.com> wrote:\n> \"Bert Wesarg\" <bert.wesarg@googlemail.com> writes:\n>\n>> On Mon, Sep 1, 2008 at 21:10, Shawn O. Pearce <spearce@spearce.org> wrote:\n>> ...\n>>> You can still get ambiguous names.  Avoiding them requires going\n>>> through all refs and building their short forms, then using the\n>>> full ref name for any ref which had more than one name shorten to\n>>> the same string.  Ugly, but implementable, and probably something\n>>> that should be considered.\n>>\n>> What about: try the list backwards until the first match, than try the\n>> matched part (this what %.*s matched) with the forward list, if both\n>> give the same pattern, its not disambiguous. If not try the next\n>> pattern backwards.\n>\n> How does it catch the case where you have both 'xyzzy' branch and 'xyzzy'\n> tag, which is the point of disambiguation issue Shawn raised?\nRight.\n"},{"id":"89491","messageId":"36ca99e90809020026j37c41a8fu99b45abbd02eb372@mail.gmail.com","threadId":"15301","inReplyTo":"36ca99e90809011444v3fca09c4o4d9dcf1a7249a00a@mail.gmail.com","subject":"Re: [PATCH] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-02T07:26:27Z","receivedAt":"2008-09-02T07:26:27Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Mon, Sep 1, 2008 at 23:44, Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n> On Mon, Sep 1, 2008 at 23:28, Junio C Hamano <gitster@pobox.com> wrote:\n>> \"Bert Wesarg\" <bert.wesarg@googlemail.com> writes:\n>>\n>>> On Mon, Sep 1, 2008 at 21:10, Shawn O. Pearce <spearce@spearce.org> wrote:\n>>> ...\n>>>> You can still get ambiguous names.  Avoiding them requires going\n>>>> through all refs and building their short forms, then using the\n>>>> full ref name for any ref which had more than one name shorten to\n>>>> the same string.  Ugly, but implementable, and probably something\n>>>> that should be considered.\n>>>\n>>> What about: try the list backwards until the first match, than try the\n>>> matched part (this what %.*s matched) with the forward list, if both\n>>> give the same pattern, its not disambiguous. If not try the next\n>>> pattern backwards.\n>>\n>> How does it catch the case where you have both 'xyzzy' branch and 'xyzzy'\n>> tag, which is the point of disambiguation issue Shawn raised?\n> Right.\nI was wrong:\n\ngiven these two refs:\n\n  refs/heads/xyzzy\n  refs/tags/xyzzy\n\nfirst try to shorten \"refs/heads/xyzzy\":\n\n  first (from the end) matched pattern is \"refs/heads/%.*s\" with\n\"xyzzy\" as result\n\n  but resolved ref for \"xyzzy\" is \"refs/tags/xyzzy\" => continue\n\n  next matched pattern is \"%.*s\" with \"refs/heads/xyzzy\" as result\n\n  end result is therefore: \"refs/heads/xyzzy\"\n\nsecond try to shorten \"refs/tags/xyzzy\":\n\n  first (from the end) matched pattern is \"refs/tags/%.*s\" with\n\"xyzzy\" as result\n\n  resolved ref for \"xyzzy\" is \"refs/tags/xyzzy\" => end\n\n  end result is therefore: \"xyzzy\"\n\nthe output would be:\n\n  refs/heads/xyzzy\n  xyzzy\n\nThe question is now, if this is usable for bash completion? Current\nbash completion would handle this case wrong, because you get two\nxyzzy.\n\nBert\n"},{"id":"89508","messageId":"20080902143912.GB28704@spearce.org","threadId":"15301","inReplyTo":"36ca99e90809020026j37c41a8fu99b45abbd02eb372@mail.gmail.com","subject":"Re: [PATCH] for-each-ref: `:short` format for `refname`","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-09-02T14:39:12Z","receivedAt":"2008-09-02T14:39:12Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n> \n> given these two refs:\n> \n>   refs/heads/xyzzy\n>   refs/tags/xyzzy\n> \n> first try to shorten \"refs/heads/xyzzy\":\n> \n>   first (from the end) matched pattern is \"refs/heads/%.*s\" with\n> \"xyzzy\" as result\n> \n>   but resolved ref for \"xyzzy\" is \"refs/tags/xyzzy\" => continue\n> \n>   next matched pattern is \"%.*s\" with \"refs/heads/xyzzy\" as result\n> \n>   end result is therefore: \"refs/heads/xyzzy\"\n> \n> second try to shorten \"refs/tags/xyzzy\":\n> \n>   first (from the end) matched pattern is \"refs/tags/%.*s\" with\n> \"xyzzy\" as result\n> \n>   resolved ref for \"xyzzy\" is \"refs/tags/xyzzy\" => end\n> \n>   end result is therefore: \"xyzzy\"\n> \n> the output would be:\n> \n>   refs/heads/xyzzy\n>   xyzzy\n> \n> The question is now, if this is usable for bash completion? Current\n> bash completion would handle this case wrong, because you get two\n> xyzzy.\n\nI think this is reasonable.  Its better than what we have today,\nwhich is ambiguous completion.  So this looks reasoanble to me.\nUsually people don't have ambiguous names, but it happens.  I've\nbeen known to do something stupid like this:\n\n  git checkout -b v1.0 v1.0\n  git reset --hard v1.2\n  git log v1.0\n  # wtf?!?!!?!\n\n;-)\n\n-- \nShawn.\n"},{"id":"89555","messageId":"1220392637-24978-1-git-send-email-bert.wesarg@googlemail.com","threadId":"15301","inReplyTo":"20080902143912.GB28704@spearce.org","subject":"[PATCH v2] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-02T21:57:17Z","receivedAt":"2008-09-02T21:57:17Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"Tries to shorten the refname to a non-ambiguous name.\nI.e. the full and the short refname points to the same object.\n\nSigned-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\nCc: git@vger.kernel.org\nCc: szeder@ira.uka.de\nCc: Shawn O. Pearce <spearce@spearce.org>\n---\n Documentation/git-for-each-ref.txt |    2 +\n builtin-for-each-ref.c             |  133 ++++++++++++++++++++++++++++++++++--\n t/t6300-for-each-ref.sh            |   31 ++++++++\n 3 files changed, 159 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex eae6c0e..89158d9 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -74,6 +74,8 @@ For all objects, the following names can be used:\n \n refname::\n \tThe name of the ref (the part after $GIT_DIR/).\n+\tFor a non-ambiguous short name of the ref append `:short`.\n+\tI.e. both the full and short name will resolve to the same object.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 21e92bb..6359ad7 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -546,6 +546,105 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v\n }\n \n /*\n+ * generate a format suitable for scanf from a ref_rev_parse_rules\n+ * rule, that is replace the \"%.*s\" spec with a \"%s\" spec\n+ */\n+static void gen_scanf_fmt(char *scanf_fmt, const char *rule)\n+{\n+\tchar *spec;\n+\n+\tspec = strstr(rule, \"%.*s\");\n+\tif (!spec || strstr(spec + 4, \"%.*s\"))\n+\t\tdie(\"invalid rule in ref_rev_parse_rules: %s\", rule);\n+\n+\t/* copy all until spec */\n+\tstrncpy(scanf_fmt, rule, spec - rule);\n+\tscanf_fmt[spec - rule] = '\\0';\n+\t/* copy new spec */\n+\tstrcat(scanf_fmt, \"%s\");\n+\t/* copy remaining rule */\n+\tstrcat(scanf_fmt, spec + 4);\n+\n+\treturn;\n+}\n+\n+/*\n+ * Shorten the refname to an non-ambiguous form\n+ */\n+static char *get_short_ref(struct refinfo *ref)\n+{\n+\tint i;\n+\tstatic char **scanf_fmts;\n+\tstatic int nr_rules;\n+\tchar *short_name;\n+\tunsigned char fullref_sha1[20];\n+\n+\t/* pre generate scanf formats from ref_rev_parse_rules[] */\n+\tif (!nr_rules) {\n+\t\tsize_t total_len = 0;\n+\n+\t\t/* the rule list is NULL terminated, count them first */\n+\t\tfor (; ref_rev_parse_rules[nr_rules]; nr_rules++)\n+\t\t\t/* no +1 because strlen(\"%s\") < strlen(\"%.*s\") */\n+\t\t\ttotal_len += strlen(ref_rev_parse_rules[nr_rules]);\n+\n+\t\tscanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);\n+\n+\t\ttotal_len = 0;\n+\t\tfor (i = 0; i < nr_rules; i++) {\n+\t\t\tscanf_fmts[i] = (char *)&scanf_fmts[nr_rules]\n+\t\t\t\t\t+ total_len;\n+\t\t\tgen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);\n+\t\t\ttotal_len += strlen(ref_rev_parse_rules[i]);\n+\t\t}\n+\t}\n+\n+\t/* bail out if there are no rules */\n+\tif (!nr_rules)\n+\t\treturn ref->refname;\n+\n+\tread_ref(ref->refname, fullref_sha1);\n+\n+\t/* buffer for scanf result, at most ref->refname must fit */\n+\tshort_name = strdup(ref->refname);\n+\n+\t/* skip first rule, will always match */\n+\tfor (i = 0; i < nr_rules - 1; i++) {\n+\t\tconst char **p;\n+\t\tint short_name_len;\n+\n+\t\tif (1 != sscanf(ref->refname, scanf_fmts[nr_rules - 1 - i],\n+\t\t\t\tshort_name))\n+\t\t\tcontinue;\n+\n+\t\tshort_name_len = strlen(short_name);\n+\n+\t\t/* check if full and short point to the same object\n+\t\t * by checking all rules in forward direction\n+\t\t */\n+\t\tfor (p = ref_rev_parse_rules; *p; p++) {\n+\t\t\tunsigned char short_sha1[20];\n+\n+\t\t\t/* check for valid ref */\n+\t\t\tif (read_ref(mkpath(*p, short_name_len, short_name),\n+\t\t\t\t     short_sha1))\n+\t\t\t\tcontinue;\n+\n+\t\t\t/* if the objects differ the short name is ambiguous */\n+\t\t\tif (hashcmp(fullref_sha1, short_sha1))\n+\t\t\t\tbreak;\n+\n+\t\t\t/* ok, short and full ref point to the same object */\n+\t\t\treturn short_name;\n+\t\t}\n+\t}\n+\n+\tfree(short_name);\n+\treturn ref->refname;\n+}\n+\n+\n+/*\n  * Parse the object referred by ref, and grab needed value.\n  */\n static void populate_value(struct refinfo *ref)\n@@ -570,13 +669,33 @@ static void populate_value(struct refinfo *ref)\n \tfor (i = 0; i < used_atom_cnt; i++) {\n \t\tconst char *name = used_atom[i];\n \t\tstruct atom_value *v = &ref->value[i];\n-\t\tif (!strcmp(name, \"refname\"))\n-\t\t\tv->s = ref->refname;\n-\t\telse if (!strcmp(name, \"*refname\")) {\n-\t\t\tint len = strlen(ref->refname);\n-\t\t\tchar *s = xmalloc(len + 4);\n-\t\t\tsprintf(s, \"%s^{}\", ref->refname);\n-\t\t\tv->s = s;\n+\t\tint deref = 0;\n+\t\tif (*name == '*') {\n+\t\t\tderef = 1;\n+\t\t\tname++;\n+\t\t}\n+\t\tif (!prefixcmp(name, \"refname\")) {\n+\t\t\tconst char *formatp = strchr(name, ':');\n+\t\t\tconst char *refname = ref->refname;\n+\n+\t\t\t/* look for \"short\" refname format */\n+\t\t\tif (formatp) {\n+\t\t\t\tformatp++;\n+\t\t\t\tif (!strcmp(formatp, \"short\"))\n+\t\t\t\t\trefname = get_short_ref(ref);\n+\t\t\t\telse\n+\t\t\t\t\tdie(\"unknown refname format %s\",\n+\t\t\t\t\t    formatp);\n+\t\t\t}\n+\n+\t\t\tif (!deref)\n+\t\t\t\tv->s = refname;\n+\t\t\telse {\n+\t\t\t\tint len = strlen(refname);\n+\t\t\t\tchar *s = xmalloc(len + 4);\n+\t\t\t\tsprintf(s, \"%s^{}\", refname);\n+\t\t\t\tv->s = s;\n+\t\t\t}\n \t\t}\n \t}\n \ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 8ced593..ad8d48e 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -262,6 +262,37 @@ for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n \t\"\n done\n \n+cat >expected <<\\EOF\n+master\n+testtag\n+EOF\n+\n+test_expect_success 'Check short refname format' '\n+\t(git for-each-ref --format=\"%(refname:short)\" refs/heads &&\n+\tgit for-each-ref --format=\"%(refname:short)\" refs/tags) >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check for invalid refname format' '\n+\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n+'\n+\n+cat >expected <<\\EOF\n+heads/master\n+master\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs' '\n+\tgit checkout -b newtag &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Branch\" &&\n+\tsetdate_and_increment &&\n+\tgit tag -m \"Tagging at $datestamp\" master &&\n+\tgit for-each-ref --format \"%(refname:short)\" refs/heads/master refs/tags/master >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'an unusual tag with an incomplete line' '\n \n \tgit tag -m \"bogo\" bogo &&\n-- \n1.6.0\n"},{"id":"89568","messageId":"7v63pei1pz.fsf@gitster.siamese.dyndns.org","threadId":"15301","inReplyTo":"1220392637-24978-1-git-send-email-bert.wesarg@googlemail.com","subject":"Re: [PATCH v2] for-each-ref: `:short` format for `refname`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-02T23:10:00Z","receivedAt":"2008-09-02T23:10:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Bert Wesarg <bert.wesarg@googlemail.com> writes:\n\n> Tries to shorten the refname to a non-ambiguous name.\n> I.e. the full and the short refname points to the same object.\n\nThe definition of \"ambiguity\" here is wrong (see below).\n\n> +\t/* skip first rule, will always match */\n> +\tfor (i = 0; i < nr_rules - 1; i++) {\n> +\t\tconst char **p;\n> +\t\tint short_name_len;\n> +\n> +\t\tif (1 != sscanf(ref->refname, scanf_fmts[nr_rules - 1 - i],\n> +\t\t\t\tshort_name))\n> +\t\t\tcontinue;\n> +\n> +\t\tshort_name_len = strlen(short_name);\n> +\n> +\t\t/* check if full and short point to the same object\n\nStyle?\n\n> +\t\t * by checking all rules in forward direction\n> +\t\t */\n\nI think this part of the code is wrong, in that it talks about what object\nthe ref points at.  That is not what ref ambiguity is about.\n\nGiven a tag that points at a version 1.0.0 commit, this sequence will\ncreate:\n\n\t$ git tag foo v1.0.0^0\n        $ git branch foo v1.0.0^0\n\nambiguous branch and tag whose names are both 'foo', even though they\npoint at the same thing.  The right API to use would be resolve_ref(), I\nthink.\n\nOther than that, it is well done.\n\nAlthough I was initially a bit surprised by the size of the patch to\nimplement something so (conceptually) simple, the code was easy and\nstraightforward to follow.\n\nThanks.\n"},{"id":"89594","messageId":"36ca99e90809030133r43fc5a3agad1aa38339a758a3@mail.gmail.com","threadId":"15301","inReplyTo":"7v63pei1pz.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v2] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-03T08:33:48Z","receivedAt":"2008-09-03T08:33:48Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Wed, Sep 3, 2008 at 01:10, Junio C Hamano <gitster@pobox.com> wrote:\n> Bert Wesarg <bert.wesarg@googlemail.com> writes:\n>\n>> Tries to shorten the refname to a non-ambiguous name.\n>> I.e. the full and the short refname points to the same object.\n>\n> The definition of \"ambiguity\" here is wrong (see below).\n>\n>> +              * by checking all rules in forward direction\n>> +              */\n>\n> I think this part of the code is wrong, in that it talks about what object\n> the ref points at.  That is not what ref ambiguity is about.\n>\n> Given a tag that points at a version 1.0.0 commit, this sequence will\n> create:\n>\n>        $ git tag foo v1.0.0^0\n>        $ git branch foo v1.0.0^0\n>\n> ambiguous branch and tag whose names are both 'foo', even though they\n> point at the same thing.  The right API to use would be resolve_ref(), I\n> think.\nI use resolve_ref():\n\nint read_ref(const char *ref, unsigned char *sha1)\n{\n\tif (resolve_ref(ref, sha1, 1, NULL))\n\t\treturn 0;\n\treturn -1;\n}\n\nOk, I think I get your point: it doesn't matter if the full and short\npoint to different objects, it's enough for ambiguity that the short\nname resolves to more than one ref.\n\nNew patch in reply.\n\n>\n> Other than that, it is well done.\n>\n> Although I was initially a bit surprised by the size of the patch to\n> implement something so (conceptually) simple, the code was easy and\n> straightforward to follow.\n>\n> Thanks.\nYour welcome.\n\nBert\n>\n"},{"id":"89596","messageId":"1220431352-30605-1-git-send-email-bert.wesarg@googlemail.com","threadId":"15301","inReplyTo":"36ca99e90809030133r43fc5a3agad1aa38339a758a3@mail.gmail.com","subject":"[PATCH v3] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-03T08:42:32Z","receivedAt":"2008-09-03T08:42:32Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"Try to shorten the refname to a non-ambiguous name.\n\nChanges in v3:\n * don't compare sha1's, its ambiguous if the short name\n   resovles to more than one ref\n * use xstrdup()\n * use indexes for the loops to clarify backward/forward\n   direction\n\n\nSigned-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\nCc: git@vger.kernel.org\nCc: szeder@ira.uka.de\nCc: Shawn O. Pearce <spearce@spearce.org>\n---\n Documentation/git-for-each-ref.txt |    1 +\n builtin-for-each-ref.c             |  138 ++++++++++++++++++++++++++++++++++--\n t/t6300-for-each-ref.sh            |   44 +++++++++++\n 3 files changed, 176 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex eae6c0e..4016f59 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -74,6 +74,7 @@ For all objects, the following names can be used:\n \n refname::\n \tThe name of the ref (the part after $GIT_DIR/).\n+\tFor a non-ambiguous short name of the ref append `:short`.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 21e92bb..abe0804 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -546,6 +546,110 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v\n }\n \n /*\n+ * generate a format suitable for scanf from a ref_rev_parse_rules\n+ * rule, that is replace the \"%.*s\" spec with a \"%s\" spec\n+ */\n+static void gen_scanf_fmt(char *scanf_fmt, const char *rule)\n+{\n+\tchar *spec;\n+\n+\tspec = strstr(rule, \"%.*s\");\n+\tif (!spec || strstr(spec + 4, \"%.*s\"))\n+\t\tdie(\"invalid rule in ref_rev_parse_rules: %s\", rule);\n+\n+\t/* copy all until spec */\n+\tstrncpy(scanf_fmt, rule, spec - rule);\n+\tscanf_fmt[spec - rule] = '\\0';\n+\t/* copy new spec */\n+\tstrcat(scanf_fmt, \"%s\");\n+\t/* copy remaining rule */\n+\tstrcat(scanf_fmt, spec + 4);\n+\n+\treturn;\n+}\n+\n+/*\n+ * Shorten the refname to an non-ambiguous form\n+ */\n+static char *get_short_ref(struct refinfo *ref)\n+{\n+\tint i;\n+\tstatic char **scanf_fmts;\n+\tstatic int nr_rules;\n+\tchar *short_name;\n+\n+\t/* pre generate scanf formats from ref_rev_parse_rules[] */\n+\tif (!nr_rules) {\n+\t\tsize_t total_len = 0;\n+\n+\t\t/* the rule list is NULL terminated, count them first */\n+\t\tfor (; ref_rev_parse_rules[nr_rules]; nr_rules++)\n+\t\t\t/* no +1 because strlen(\"%s\") < strlen(\"%.*s\") */\n+\t\t\ttotal_len += strlen(ref_rev_parse_rules[nr_rules]);\n+\n+\t\tscanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);\n+\n+\t\ttotal_len = 0;\n+\t\tfor (i = 0; i < nr_rules; i++) {\n+\t\t\tscanf_fmts[i] = (char *)&scanf_fmts[nr_rules]\n+\t\t\t\t\t+ total_len;\n+\t\t\tgen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);\n+\t\t\ttotal_len += strlen(ref_rev_parse_rules[i]);\n+\t\t}\n+\t}\n+\n+\t/* bail out if there are no rules */\n+\tif (!nr_rules)\n+\t\treturn ref->refname;\n+\n+\t/* buffer for scanf result, at most ref->refname must fit */\n+\tshort_name = xstrdup(ref->refname);\n+\n+\t/* skip first rule, it will always match */\n+\tfor (i = nr_rules - 1; i > 0 ; --i) {\n+\t\tint j;\n+\t\tint short_name_len;\n+\n+\t\tif (1 != sscanf(ref->refname, scanf_fmts[i], short_name))\n+\t\t\tcontinue;\n+\n+\t\tshort_name_len = strlen(short_name);\n+\n+\t\t/*\n+\t\t * check if the short name resolves to a valid ref,\n+\t\t * but use only rules prior to the matched one\n+\t\t */\n+\t\tfor (j = 0; j < i; j++) {\n+\t\t\tconst char *p = ref_rev_parse_rules[j];\n+\t\t\tunsigned char short_objectname[20];\n+\n+\t\t\t/* check for valid ref */\n+\t\t\tif (read_ref(mkpath(p, short_name_len, short_name),\n+\t\t\t\t     short_objectname))\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * short_name resolves to a valid ref,\n+\t\t\t * but its a differnet path than that of ref->refname,\n+\t\t\t * therefore the short name is ambiguous\n+\t\t\t */\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/*\n+\t\t * short name is non-ambiguous if all previous rules\n+\t\t * don't resolve to a valid ref\n+\t\t */\n+\t\tif (j == i)\n+\t\t\treturn short_name;\n+\t}\n+\n+\tfree(short_name);\n+\treturn ref->refname;\n+}\n+\n+\n+/*\n  * Parse the object referred by ref, and grab needed value.\n  */\n static void populate_value(struct refinfo *ref)\n@@ -570,13 +674,33 @@ static void populate_value(struct refinfo *ref)\n \tfor (i = 0; i < used_atom_cnt; i++) {\n \t\tconst char *name = used_atom[i];\n \t\tstruct atom_value *v = &ref->value[i];\n-\t\tif (!strcmp(name, \"refname\"))\n-\t\t\tv->s = ref->refname;\n-\t\telse if (!strcmp(name, \"*refname\")) {\n-\t\t\tint len = strlen(ref->refname);\n-\t\t\tchar *s = xmalloc(len + 4);\n-\t\t\tsprintf(s, \"%s^{}\", ref->refname);\n-\t\t\tv->s = s;\n+\t\tint deref = 0;\n+\t\tif (*name == '*') {\n+\t\t\tderef = 1;\n+\t\t\tname++;\n+\t\t}\n+\t\tif (!prefixcmp(name, \"refname\")) {\n+\t\t\tconst char *formatp = strchr(name, ':');\n+\t\t\tconst char *refname = ref->refname;\n+\n+\t\t\t/* look for \"short\" refname format */\n+\t\t\tif (formatp) {\n+\t\t\t\tformatp++;\n+\t\t\t\tif (!strcmp(formatp, \"short\"))\n+\t\t\t\t\trefname = get_short_ref(ref);\n+\t\t\t\telse\n+\t\t\t\t\tdie(\"unknown refname format %s\",\n+\t\t\t\t\t    formatp);\n+\t\t\t}\n+\n+\t\t\tif (!deref)\n+\t\t\t\tv->s = refname;\n+\t\t\telse {\n+\t\t\t\tint len = strlen(refname);\n+\t\t\t\tchar *s = xmalloc(len + 4);\n+\t\t\t\tsprintf(s, \"%s^{}\", refname);\n+\t\t\t\tv->s = s;\n+\t\t\t}\n \t\t}\n \t}\n \ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 8ced593..4f247dd 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -262,6 +262,50 @@ for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n \t\"\n done\n \n+cat >expected <<\\EOF\n+master\n+testtag\n+EOF\n+\n+test_expect_success 'Check short refname format' '\n+\t(git for-each-ref --format=\"%(refname:short)\" refs/heads &&\n+\tgit for-each-ref --format=\"%(refname:short)\" refs/tags) >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check for invalid refname format' '\n+\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n+'\n+\n+cat >expected <<\\EOF\n+heads/master\n+master\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs' '\n+\tgit checkout -b newtag &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Branch\" &&\n+\tsetdate_and_increment &&\n+\tgit tag -m \"Tagging at $datestamp\" master &&\n+\tgit for-each-ref --format \"%(refname:short)\" refs/heads/master refs/tags/master >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+heads/ambiguous\n+ambiguous\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs II' '\n+\tgit checkout master &&\n+\tgit tag ambiguous testtag^0 &&\n+\tgit branch ambiguous testtag^0 &&\n+\tgit for-each-ref --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'an unusual tag with an incomplete line' '\n \n \tgit tag -m \"bogo\" bogo &&\n-- \n1.6.0\n"},{"id":"89634","messageId":"20080903151829.GB27682@spearce.org","threadId":"15301","inReplyTo":"1220431352-30605-1-git-send-email-bert.wesarg@googlemail.com","subject":"Re: [PATCH v3] for-each-ref: `:short` format for `refname`","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-09-03T15:18:29Z","receivedAt":"2008-09-03T15:18:29Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n> Try to shorten the refname to a non-ambiguous name.\n> \n> Changes in v3:\n>  * don't compare sha1's, its ambiguous if the short name\n>    resovles to more than one ref\n>  * use xstrdup()\n>  * use indexes for the loops to clarify backward/forward\n>    direction\n> \n> \n> Signed-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n> Cc: git@vger.kernel.org\n> Cc: szeder@ira.uka.de\n> Cc: Shawn O. Pearce <spearce@spearce.org>\n> ---\n\nLooks good.  But the commit messages shouldn't have the \"Changes\nin v3\" section or probably the \"Cc:\" lines.  The changes in v3\npart usually goes below the ---.\n\nI don't usually work on for-each-ref, but I'll toss an ack in anyway:\n\nAcked-by: Shawn O. Pearce <spearce@spearce.org>\n\n-- \nShawn.\n"},{"id":"89642","messageId":"36ca99e90809030933m15ef9cf5p1a56bfe90071fc10@mail.gmail.com","threadId":"15301","inReplyTo":"20080903151829.GB27682@spearce.org","subject":"Re: [PATCH v3] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-03T16:33:45Z","receivedAt":"2008-09-03T16:33:45Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Wed, Sep 3, 2008 at 17:18, Shawn O. Pearce <spearce@spearce.org> wrote:\n> Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n>> Try to shorten the refname to a non-ambiguous name.\n>>\n>> Changes in v3:\n>>  * don't compare sha1's, its ambiguous if the short name\n>>    resovles to more than one ref\n>>  * use xstrdup()\n>>  * use indexes for the loops to clarify backward/forward\n>>    direction\n>>\n>>\n>> Signed-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n>> Cc: git@vger.kernel.org\n>> Cc: szeder@ira.uka.de\n>> Cc: Shawn O. Pearce <spearce@spearce.org>\n>> ---\n>\n> Looks good.  But the commit messages shouldn't have the \"Changes\n> in v3\" section or probably the \"Cc:\" lines.  The changes in v3\n> part usually goes below the ---.\nOk, but shouldn't git format-patch detect that there is already a ---\nin the commit message, or is it illegal to have more than one ---?\n\nAnd while we are at it, shouldn't git format-patch detect, that there\nis already a [*PATCH*]-prefix in the first line of the commit?\n\nI always need to edit these two things after format-patch.\n\nBert\n>\n> --\n> Shawn.\n>\n"},{"id":"89647","messageId":"36ca99e90809030956r5ab94a14he088265e28ea9b14@mail.gmail.com","threadId":"15301","inReplyTo":"36ca99e90809030933m15ef9cf5p1a56bfe90071fc10@mail.gmail.com","subject":"Re: [PATCH v3] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-03T16:56:58Z","receivedAt":"2008-09-03T16:56:58Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Wed, Sep 3, 2008 at 18:33, Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n> On Wed, Sep 3, 2008 at 17:18, Shawn O. Pearce <spearce@spearce.org> wrote:\n>> Bert Wesarg <bert.wesarg@googlemail.com> wrote:\n>>> Try to shorten the refname to a non-ambiguous name.\n>>>\n>>> Changes in v3:\n>>>  * don't compare sha1's, its ambiguous if the short name\n>>>    resovles to more than one ref\n>>>  * use xstrdup()\n>>>  * use indexes for the loops to clarify backward/forward\n>>>    direction\n>>>\n>>>\n>>> Signed-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n>>> Cc: git@vger.kernel.org\n>>> Cc: szeder@ira.uka.de\n>>> Cc: Shawn O. Pearce <spearce@spearce.org>\n>>> ---\n>>\n>> Looks good.  But the commit messages shouldn't have the \"Changes\n>> in v3\" section or probably the \"Cc:\" lines.  The changes in v3\n>> part usually goes below the ---.\n> Ok, but shouldn't git format-patch detect that there is already a ---\n> in the commit message, or is it illegal to have more than one ---?\n>\n> And while we are at it, shouldn't git format-patch detect, that there\n> is already a [*PATCH*]-prefix in the first line of the commit?\nOk, I was too fast, -k is obviously the right option for this.\n\n>\n> I always need to edit these two things after format-patch.\n>\n> Bert\n>>\n>> --\n>> Shawn.\n>>\n>\n"},{"id":"89665","messageId":"7vtzcxaxgr.fsf@gitster.siamese.dyndns.org","threadId":"15301","inReplyTo":"1220431352-30605-1-git-send-email-bert.wesarg@googlemail.com","subject":"Re: [PATCH v3] for-each-ref: `:short` format for `refname`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-03T18:36:04Z","receivedAt":"2008-09-03T18:36:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Bert Wesarg <bert.wesarg@googlemail.com> writes:\n\n> Try to shorten the refname to a non-ambiguous name.\n>\n> Changes in v3:\n>  * don't compare sha1's, its ambiguous if the short name\n>    resovles to more than one ref\n>  * use xstrdup()\n>  * use indexes for the loops to clarify backward/forward\n>    direction\n\nPlease don't do \"Changes in v3\" in the commit log message; do so after\n\"---\" is very welcome as it gives people on the discussion _right now_\nto understand the context of the change.\n\nAlso please do not omit discussion in the commit log message, to help\npeople _not_ on the discussion right now.\n\nYour earlier versions will not appear on the history anywhere, and v3 is\nthe _only_ variant available to the people who need to understand what\nthis change was made for and why the code to implement the change was done\nin the way the patch shows.  Write your commit log message to help people\nwho have \"git show $this\" and nothing else to figure these things out in\nyear 2010.\n\nNamely:\n\n * Why \"shortening\" is a good thing?  For what use case this new feature\n   was invented for?\n\n * What are the definitions of \"shortening\" and \"non-ambiguous\" in this\n   feature's context?\n\nIn addition, for a complex feature with different possiblilities, it is a\ngood practice to describe what alternatatives and pros-and-cons among them\nwere considered while reaching this final version.  This is to help people\nwho would want to explore enhancement possibilities later; you beforehand\nwarn them about dead ends you have already explored.  For this particular\npatch/feature, I do not think it is necessary, though.\n"},{"id":"89872","messageId":"1220649383-17916-1-git-send-email-bert.wesarg@googlemail.com","threadId":"15301","inReplyTo":"7vtzcxaxgr.fsf@gitster.siamese.dyndns.org","subject":"[PATCH v4] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-05T21:16:23Z","receivedAt":"2008-09-05T21:16:23Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"Tries to shorten the refname to a non-ambiguous name.\n\nSzeder Gábor noticed that the git bash completion takes a\ntremendous amount of time to strip leading components from\nheads and tags refs (i.e. refs/heads, refs/tags, ...). He\nproposed a new atom called 'refbasename' which removes at\nmost two leading components from the ref name.\n\nI myself, proposed a more dynamic solution, which strips off\ncommon leading components with the matched pattern.\n\nBut the current bash solution and both proposals suffer from\none mayor problem: ambiguous refs.\n\nA ref is ambiguous, if it resolves to more than one full refs.\nI.e. given the refs refs/heads/xyzzy and refs/tags/xyzzy. The\n(short) ref xyzzy can point to both refs.\n\n( Note: Its irrelevant whether the referenced objects are the\n  same or not. )\n\nThis proposal solves this by checking for ambiguity of the\nshorten ref name.\n\nThe shortening is done with the same rules for resolving refs\nbut in the reverse order. The short name is checked if it\nresolves to a different ref.\n\nTo continue the above example, the output would be like this:\n\nheads/xyzzy\nxyzzy\n\nSo, if you want just tags, xyzzy is not ambiguous, because it\nwill resolve to a tag. If you need the heads you get a also\na non-ambiguous short form of the ref.\n\nTo integrate this new format into the bash completion to get\nonly non-ambiguous refs is beyond the scope of this patch.\n\nSigned-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n\n---\nCc: git@vger.kernel.org\nCc: szeder@ira.uka.de\nCc: Shawn O. Pearce <spearce@spearce.org>\n\n Documentation/git-for-each-ref.txt |    1 +\n builtin-for-each-ref.c             |  135 ++++++++++++++++++++++++++++++++++--\n t/t6300-for-each-ref.sh            |   44 ++++++++++++\n 3 files changed, 173 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex eae6c0e..4016f59 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -74,6 +74,7 @@ For all objects, the following names can be used:\n \n refname::\n \tThe name of the ref (the part after $GIT_DIR/).\n+\tFor a non-ambiguous short name of the ref append `:short`.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 21e92bb..9b44092 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -546,6 +546,107 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v\n }\n \n /*\n+ * generate a format suitable for scanf from a ref_rev_parse_rules\n+ * rule, that is replace the \"%.*s\" spec with a \"%s\" spec\n+ */\n+static void gen_scanf_fmt(char *scanf_fmt, const char *rule)\n+{\n+\tchar *spec;\n+\n+\tspec = strstr(rule, \"%.*s\");\n+\tif (!spec || strstr(spec + 4, \"%.*s\"))\n+\t\tdie(\"invalid rule in ref_rev_parse_rules: %s\", rule);\n+\n+\t/* copy all until spec */\n+\tstrncpy(scanf_fmt, rule, spec - rule);\n+\tscanf_fmt[spec - rule] = '\\0';\n+\t/* copy new spec */\n+\tstrcat(scanf_fmt, \"%s\");\n+\t/* copy remaining rule */\n+\tstrcat(scanf_fmt, spec + 4);\n+\n+\treturn;\n+}\n+\n+/*\n+ * Shorten the refname to an non-ambiguous form\n+ */\n+static char *get_short_ref(struct refinfo *ref)\n+{\n+\tint i;\n+\tstatic char **scanf_fmts;\n+\tstatic int nr_rules;\n+\tchar *short_name;\n+\n+\t/* pre generate scanf formats from ref_rev_parse_rules[] */\n+\tif (!nr_rules) {\n+\t\tsize_t total_len = 0;\n+\n+\t\t/* the rule list is NULL terminated, count them first */\n+\t\tfor (; ref_rev_parse_rules[nr_rules]; nr_rules++)\n+\t\t\t/* no +1 because strlen(\"%s\") < strlen(\"%.*s\") */\n+\t\t\ttotal_len += strlen(ref_rev_parse_rules[nr_rules]);\n+\n+\t\tscanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);\n+\n+\t\ttotal_len = 0;\n+\t\tfor (i = 0; i < nr_rules; i++) {\n+\t\t\tscanf_fmts[i] = (char *)&scanf_fmts[nr_rules]\n+\t\t\t\t\t+ total_len;\n+\t\t\tgen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);\n+\t\t\ttotal_len += strlen(ref_rev_parse_rules[i]);\n+\t\t}\n+\t}\n+\n+\t/* bail out if there are no rules */\n+\tif (!nr_rules)\n+\t\treturn ref->refname;\n+\n+\t/* buffer for scanf result, at most ref->refname must fit */\n+\tshort_name = xstrdup(ref->refname);\n+\n+\t/* skip first rule, it will always match */\n+\tfor (i = nr_rules - 1; i > 0 ; --i) {\n+\t\tint j;\n+\t\tint short_name_len;\n+\n+\t\tif (1 != sscanf(ref->refname, scanf_fmts[i], short_name))\n+\t\t\tcontinue;\n+\n+\t\tshort_name_len = strlen(short_name);\n+\n+\t\t/*\n+\t\t * check if the short name resolves to a valid ref,\n+\t\t * but use only rules prior to the matched one\n+\t\t */\n+\t\tfor (j = 0; j < i; j++) {\n+\t\t\tconst char *rule = ref_rev_parse_rules[j];\n+\t\t\tunsigned char short_objectname[20];\n+\n+\t\t\t/*\n+\t\t\t * the short name is ambiguous, if it resolves\n+\t\t\t * (with this previous rule) to a valid ref\n+\t\t\t * read_ref() returns 0 on success\n+\t\t\t */\n+\t\t\tif (!read_ref(mkpath(rule, short_name_len, short_name),\n+\t\t\t\t      short_objectname))\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\t/*\n+\t\t * short name is non-ambiguous if all previous rules\n+\t\t * haven't resolved to a valid ref\n+\t\t */\n+\t\tif (j == i)\n+\t\t\treturn short_name;\n+\t}\n+\n+\tfree(short_name);\n+\treturn ref->refname;\n+}\n+\n+\n+/*\n  * Parse the object referred by ref, and grab needed value.\n  */\n static void populate_value(struct refinfo *ref)\n@@ -570,13 +671,33 @@ static void populate_value(struct refinfo *ref)\n \tfor (i = 0; i < used_atom_cnt; i++) {\n \t\tconst char *name = used_atom[i];\n \t\tstruct atom_value *v = &ref->value[i];\n-\t\tif (!strcmp(name, \"refname\"))\n-\t\t\tv->s = ref->refname;\n-\t\telse if (!strcmp(name, \"*refname\")) {\n-\t\t\tint len = strlen(ref->refname);\n-\t\t\tchar *s = xmalloc(len + 4);\n-\t\t\tsprintf(s, \"%s^{}\", ref->refname);\n-\t\t\tv->s = s;\n+\t\tint deref = 0;\n+\t\tif (*name == '*') {\n+\t\t\tderef = 1;\n+\t\t\tname++;\n+\t\t}\n+\t\tif (!prefixcmp(name, \"refname\")) {\n+\t\t\tconst char *formatp = strchr(name, ':');\n+\t\t\tconst char *refname = ref->refname;\n+\n+\t\t\t/* look for \"short\" refname format */\n+\t\t\tif (formatp) {\n+\t\t\t\tformatp++;\n+\t\t\t\tif (!strcmp(formatp, \"short\"))\n+\t\t\t\t\trefname = get_short_ref(ref);\n+\t\t\t\telse\n+\t\t\t\t\tdie(\"unknown refname format %s\",\n+\t\t\t\t\t    formatp);\n+\t\t\t}\n+\n+\t\t\tif (!deref)\n+\t\t\t\tv->s = refname;\n+\t\t\telse {\n+\t\t\t\tint len = strlen(refname);\n+\t\t\t\tchar *s = xmalloc(len + 4);\n+\t\t\t\tsprintf(s, \"%s^{}\", refname);\n+\t\t\t\tv->s = s;\n+\t\t\t}\n \t\t}\n \t}\n \ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 8ced593..4f247dd 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -262,6 +262,50 @@ for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n \t\"\n done\n \n+cat >expected <<\\EOF\n+master\n+testtag\n+EOF\n+\n+test_expect_success 'Check short refname format' '\n+\t(git for-each-ref --format=\"%(refname:short)\" refs/heads &&\n+\tgit for-each-ref --format=\"%(refname:short)\" refs/tags) >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'Check for invalid refname format' '\n+\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n+'\n+\n+cat >expected <<\\EOF\n+heads/master\n+master\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs' '\n+\tgit checkout -b newtag &&\n+\techo \"Using $datestamp\" > one &&\n+\tgit add one &&\n+\tgit commit -m \"Branch\" &&\n+\tsetdate_and_increment &&\n+\tgit tag -m \"Tagging at $datestamp\" master &&\n+\tgit for-each-ref --format \"%(refname:short)\" refs/heads/master refs/tags/master >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<\\EOF\n+heads/ambiguous\n+ambiguous\n+EOF\n+\n+test_expect_success 'Check ambiguous head and tag refs II' '\n+\tgit checkout master &&\n+\tgit tag ambiguous testtag^0 &&\n+\tgit branch ambiguous testtag^0 &&\n+\tgit for-each-ref --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'an unusual tag with an incomplete line' '\n \n \tgit tag -m \"bogo\" bogo &&\n-- \n1.6.0\n"},{"id":"89877","messageId":"7vej3yutes.fsf@gitster.siamese.dyndns.org","threadId":"15301","inReplyTo":"1220649383-17916-1-git-send-email-bert.wesarg@googlemail.com","subject":"Re: [PATCH v4] for-each-ref: `:short` format for `refname`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-05T22:20:11Z","receivedAt":"2008-09-05T22:20:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Bert Wesarg <bert.wesarg@googlemail.com> writes:\n\n> ...\n> To integrate this new format into the bash completion to get\n> only non-ambiguous refs is beyond the scope of this patch.\n>\n> Signed-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n>\n> ---\n> Cc: git@vger.kernel.org\n> Cc: szeder@ira.uka.de\n> Cc: Shawn O. Pearce <spearce@spearce.org>\n\nNice writeup of the history of this patch, if on tad-too-verbose side.\n\n> diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\n> index 21e92bb..9b44092 100644\n> --- a/builtin-for-each-ref.c\n> +++ b/builtin-for-each-ref.c\n> @@ -546,6 +546,107 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v\n> +/*\n> + * Shorten the refname to an non-ambiguous form\n> + */\n> +static char *get_short_ref(struct refinfo *ref)\n> +{\n> ...\n> +\t/* skip first rule, it will always match */\n> +\tfor (i = nr_rules - 1; i > 0 ; --i) {\n> +\t\tint j;\n> +\t\tint short_name_len;\n> +\n> +\t\tif (1 != sscanf(ref->refname, scanf_fmts[i], short_name))\n> +\t\t\tcontinue;\n> +\n> +\t\tshort_name_len = strlen(short_name);\n> +\n> +\t\t/*\n> +\t\t * check if the short name resolves to a valid ref,\n> +\t\t * but use only rules prior to the matched one\n> +\t\t */\n> +\t\tfor (j = 0; j < i; j++) {\n> ...\n> +\t\t}\n> +\t\t/*\n> +\t\t * short name is non-ambiguous if all previous rules\n> +\t\t * haven't resolved to a valid ref\n> +\t\t */\n> +\t\tif (j == i)\n> +\t\t\treturn short_name;\n\nIs this inner loop essentially the same as calling dwim_ref(), while\ntemporarily turning warn_ambiguous_refs on, and checking for return value\nof one?\n\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index 8ced593..4f247dd 100755\n> --- a/t/t6300-for-each-ref.sh\n> +++ b/t/t6300-for-each-ref.sh\n> @@ -262,6 +262,50 @@ for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n>  \t\"\n>  done\n>  \n> +cat >expected <<\\EOF\n> +master\n> +testtag\n> +EOF\n> +\n> +test_expect_success 'Check short refname format' '\n> +\t(git for-each-ref --format=\"%(refname:short)\" refs/heads &&\n> +\tgit for-each-ref --format=\"%(refname:short)\" refs/tags) >actual &&\n> +\ttest_cmp expected actual\n> +'\n\nNot a complaint nor objection but mere curiosity.  Why does this run two\nfor-each-ref, not just one with two patterns?\n\n> +test_expect_success 'Check for invalid refname format' '\n> +\ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n> +'\n\nGood.\n\n> +cat >expected <<\\EOF\n> +heads/master\n> +master\n> +EOF\n> +\n> +test_expect_success 'Check ambiguous head and tag refs' '\n> +\tgit checkout -b newtag &&\n> +\techo \"Using $datestamp\" > one &&\n> +\tgit add one &&\n> +\tgit commit -m \"Branch\" &&\n> +\tsetdate_and_increment &&\n> +\tgit tag -m \"Tagging at $datestamp\" master &&\n> +\tgit for-each-ref --format \"%(refname:short)\" refs/heads/master refs/tags/master >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +cat >expected <<\\EOF\n> +heads/ambiguous\n> +ambiguous\n> +EOF\n> +\n> +test_expect_success 'Check ambiguous head and tag refs II' '\n> +\tgit checkout master &&\n> +\tgit tag ambiguous testtag^0 &&\n> +\tgit branch ambiguous testtag^0 &&\n> +\tgit for-each-ref --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n> +\ttest_cmp expected actual\n> +'\n> +\n\nCan we also try first creating a clone of some repo and run:\n\n\tfor-each-ref --format=\"%(refname:short)\" refs/remotes\n\nI am unsure how \"remotes/origin\" when \"refs/remotes/origin/HEAD\" points at\ntheir 'master' branch behaves with your code, and/or how it should behave.\n\nOther than that, nicely done.\n"},{"id":"89918","messageId":"36ca99e90809061116q7b9559f8w2b939f22e5f94fb7@mail.gmail.com","threadId":"15301","inReplyTo":"7vej3yutes.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v4] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-06T18:16:05Z","receivedAt":"2008-09-06T18:16:05Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Sat, Sep 6, 2008 at 00:20, Junio C Hamano <gitster@pobox.com> wrote:\n> Bert Wesarg <bert.wesarg@googlemail.com> writes:\n>\n>> ...\n>> To integrate this new format into the bash completion to get\n>> only non-ambiguous refs is beyond the scope of this patch.\n>>\n>> Signed-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n>>\n>> ---\n>> Cc: git@vger.kernel.org\n>> Cc: szeder@ira.uka.de\n>> Cc: Shawn O. Pearce <spearce@spearce.org>\n>\n> Nice writeup of the history of this patch, if on tad-too-verbose side.\n>\n>> diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\n>> index 21e92bb..9b44092 100644\n>> --- a/builtin-for-each-ref.c\n>> +++ b/builtin-for-each-ref.c\n>> @@ -546,6 +546,107 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v\n>> +/*\n>> + * Shorten the refname to an non-ambiguous form\n>> + */\n>> +static char *get_short_ref(struct refinfo *ref)\n>> +{\n>> ...\n>> +     /* skip first rule, it will always match */\n>> +     for (i = nr_rules - 1; i > 0 ; --i) {\n>> +             int j;\n>> +             int short_name_len;\n>> +\n>> +             if (1 != sscanf(ref->refname, scanf_fmts[i], short_name))\n>> +                     continue;\n>> +\n>> +             short_name_len = strlen(short_name);\n>> +\n>> +             /*\n>> +              * check if the short name resolves to a valid ref,\n>> +              * but use only rules prior to the matched one\n>> +              */\n>> +             for (j = 0; j < i; j++) {\n>> ...\n>> +             }\n>> +             /*\n>> +              * short name is non-ambiguous if all previous rules\n>> +              * haven't resolved to a valid ref\n>> +              */\n>> +             if (j == i)\n>> +                     return short_name;\n>\n> Is this inner loop essentially the same as calling dwim_ref(), while\n> temporarily turning warn_ambiguous_refs on, and checking for return value\n> of one?\nNot exactly.\n\nShort version:\n\nTo follow my above example, with dwim_ref() we would get this:\n\nheads/xyzzy\ntags/xyzzy\n\nLong version:\n\nCurrently we consider only rules prior to the matched rule (the rule\nwhich gives us the short name). That is, if any of these rules will\nalso resolve to a valid ref, the short name is  ambiguous, else its\nunambiguous. If we would consider subsequent rules past the matched\none, we may find more valid refs for this short name. Because the\ncurrent rule would match first if we try to resolve the short name, we\ndon't have to check these rules. We get only a \"ambiguous refs\"\nwarning.\n\nI have no opinion if we want this 'strict unambiguousness'.\n\n>\n>> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n>> index 8ced593..4f247dd 100755\n>> --- a/t/t6300-for-each-ref.sh\n>> +++ b/t/t6300-for-each-ref.sh\n>> @@ -262,6 +262,50 @@ for i in \"--perl --shell\" \"-s --python\" \"--python --tcl\" \"--tcl --perl\"; do\n>>       \"\n>>  done\n>>\n>> +cat >expected <<\\EOF\n>> +master\n>> +testtag\n>> +EOF\n>> +\n>> +test_expect_success 'Check short refname format' '\n>> +     (git for-each-ref --format=\"%(refname:short)\" refs/heads &&\n>> +     git for-each-ref --format=\"%(refname:short)\" refs/tags) >actual &&\n>> +     test_cmp expected actual\n>> +'\n>\n> Not a complaint nor objection but mere curiosity.  Why does this run two\n> for-each-ref, not just one with two patterns?\nIts more a leftover from the :strip version, where the pattern was the point of\ninterest.\n\n>\n>> +test_expect_success 'Check for invalid refname format' '\n>> +     test_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n>> +'\n>\n> Good.\n>\n>> +cat >expected <<\\EOF\n>> +heads/master\n>> +master\n>> +EOF\n>> +\n>> +test_expect_success 'Check ambiguous head and tag refs' '\n>> +     git checkout -b newtag &&\n>> +     echo \"Using $datestamp\" > one &&\n>> +     git add one &&\n>> +     git commit -m \"Branch\" &&\n>> +     setdate_and_increment &&\n>> +     git tag -m \"Tagging at $datestamp\" master &&\n>> +     git for-each-ref --format \"%(refname:short)\" refs/heads/master refs/tags/master >actual &&\n>> +     test_cmp expected actual\n>> +'\n>> +\n>> +cat >expected <<\\EOF\n>> +heads/ambiguous\n>> +ambiguous\n>> +EOF\n>> +\n>> +test_expect_success 'Check ambiguous head and tag refs II' '\n>> +     git checkout master &&\n>> +     git tag ambiguous testtag^0 &&\n>> +     git branch ambiguous testtag^0 &&\n>> +     git for-each-ref --format \"%(refname:short)\" refs/heads/ambiguous refs/tags/ambiguous >actual &&\n>> +     test_cmp expected actual\n>> +'\n>> +\n>\n> Can we also try first creating a clone of some repo and run:\n>\n>        for-each-ref --format=\"%(refname:short)\" refs/remotes\n>\n> I am unsure how \"remotes/origin\" when \"refs/remotes/origin/HEAD\" points at\n> their 'master' branch behaves with your code, and/or how it should behave.\nI will look at this.\n\n\n>\n> Other than that, nicely done.\n>\n>\n\nBert\n"},{"id":"90146","messageId":"7vfxoadz5c.fsf@gitster.siamese.dyndns.org","threadId":"15301","inReplyTo":"1220649383-17916-1-git-send-email-bert.wesarg@googlemail.com","subject":"Re: [PATCH v4] for-each-ref: `:short` format for `refname`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-08T22:57:19Z","receivedAt":"2008-09-08T22:57:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Any followup on this topic since:\n\n  http://thread.gmane.org/gmane.comp.version-control.git/94478/focus=95041\n"},{"id":"90149","messageId":"20080908230402.GC14448@spearce.org","threadId":"15301","inReplyTo":"7vfxoadz5c.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v4] for-each-ref: `:short` format for `refname`","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-09-08T23:04:02Z","receivedAt":"2008-09-08T23:04:02Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Junio C Hamano <gitster@pobox.com> wrote:\n> Any followup on this topic since:\n> \n>   http://thread.gmane.org/gmane.comp.version-control.git/94478/focus=95041\n> \n\nSorry.  This patch looked good to me, but the commit message was\nperhaps a bit long on the history of the patch.  I don't quite have\nthe time to give it enough of a read to offer an Ack'd-by.  But it\nseems correct on the first read.\n\n-- \nShawn.\n"},{"id":"90172","messageId":"36ca99e90809082352q3c87447eme73379673fe652f4@mail.gmail.com","threadId":"15301","inReplyTo":"7vfxoadz5c.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v4] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-09T06:52:00Z","receivedAt":"2008-09-09T06:52:00Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, Sep 9, 2008 at 00:57, Junio C Hamano <gitster@pobox.com> wrote:\n> Any followup on this topic since:\n>\n>  http://thread.gmane.org/gmane.comp.version-control.git/94478/focus=95041\n>\n>\nI plan on looking at the refs/remote test today.\n\nAny opinions, whether we want the 'strict' mode? i.e.:\n\nfor refs/heads/xyzzy and refs/tags/xyzzy:\n\nloose mode (current implementation):\n\n  refs/heads/xyzzy => heads/xyzzy\n  refs/tags/xyzzy  => xyzzy\n\nthere would be a ambiguous warning (if enabled) if you use xyzzy as a\ntag, but it resolves correctly to the tag.\n\nstrict mode:\n\n  refs/heads/xyzzy => heads/xyzzy\n  refs/tags/xyzzy  => tags/xyzzy\n\nwill always produce a non-ambiguous short forms.\n\nBert\n"},{"id":"90179","messageId":"7vod2xagmr.fsf@gitster.siamese.dyndns.org","threadId":"15301","inReplyTo":"36ca99e90809082352q3c87447eme73379673fe652f4@mail.gmail.com","subject":"Re: [PATCH v4] for-each-ref: `:short` format for `refname`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-09T08:05:32Z","receivedAt":"2008-09-09T08:05:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Bert Wesarg\" <bert.wesarg@googlemail.com> writes:\n\n> Any opinions, whether we want the 'strict' mode? i.e.:\n>\n> for refs/heads/xyzzy and refs/tags/xyzzy:\n>\n> loose mode (current implementation):\n>\n>   refs/heads/xyzzy => heads/xyzzy\n>   refs/tags/xyzzy  => xyzzy\n>\n> there would be a ambiguous warning (if enabled) if you use xyzzy as a\n> tag, but it resolves correctly to the tag.\n>\n> strict mode:\n>\n>   refs/heads/xyzzy => heads/xyzzy\n>   refs/tags/xyzzy  => tags/xyzzy\n>\n> will always produce a non-ambiguous short forms.\n\nI have no strong opinions either way, but if we want to pick only one, I\nsuspect that the loose mode would be more appropriate for bash completion\npurposes exactly because:\n\n (1) the shorter form would match the users' expectations, and;\n\n (2) if it triggers ambiguity warning to use that result that matches\n     users' expectations, it is a *good thing* --- it reminds the user\n     that s/he is playing with fire _if_ the user is of careful type who\n     enables the ambiguity warning.\n\nThinking about it from a different angle, it would make more sense to use\nloose mode if the user does not have ambiguity warning configured, and use\nstrict mode if the warning is enabled.  Then people who will get warnings\nfrom ambiguity will not get an ambiguous completion, and people who won't\nwill get shorter but still unambiguous completion.\n\nWhich means, despite what I said earlier, now I have a mild preference to\ntie the choice to core.wawrnambigousrefs configuration.\n"},{"id":"90192","messageId":"36ca99e90809090157m64253b89p6adf6a49b6d62e99@mail.gmail.com","threadId":"15301","inReplyTo":"7vod2xagmr.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v4] for-each-ref: `:short` format for `refname`","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-09-09T08:57:42Z","receivedAt":"2008-09-09T08:57:42Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Tue, Sep 9, 2008 at 10:05, Junio C Hamano <gitster@pobox.com> wrote:\n> \"Bert Wesarg\" <bert.wesarg@googlemail.com> writes:\n>\n>> Any opinions, whether we want the 'strict' mode? i.e.:\n>>\n>> for refs/heads/xyzzy and refs/tags/xyzzy:\n>>\n>> loose mode (current implementation):\n>>\n>>   refs/heads/xyzzy => heads/xyzzy\n>>   refs/tags/xyzzy  => xyzzy\n>>\n>> there would be a ambiguous warning (if enabled) if you use xyzzy as a\n>> tag, but it resolves correctly to the tag.\n>>\n>> strict mode:\n>>\n>>   refs/heads/xyzzy => heads/xyzzy\n>>   refs/tags/xyzzy  => tags/xyzzy\n>>\n>> will always produce a non-ambiguous short forms.\n>\n> I have no strong opinions either way, but if we want to pick only one, I\n> suspect that the loose mode would be more appropriate for bash completion\n> purposes exactly because:\n>\n>  (1) the shorter form would match the users' expectations, and;\n>\n>  (2) if it triggers ambiguity warning to use that result that matches\n>     users' expectations, it is a *good thing* --- it reminds the user\n>     that s/he is playing with fire _if_ the user is of careful type who\n>     enables the ambiguity warning.\n>\n> Thinking about it from a different angle, it would make more sense to use\n> loose mode if the user does not have ambiguity warning configured, and use\n> strict mode if the warning is enabled.  Then people who will get warnings\n> from ambiguity will not get an ambiguous completion, and people who won't\n> will get shorter but still unambiguous completion.\n>\n> Which means, despite what I said earlier, now I have a mild preference to\n> tie the choice to core.wawrnambigousrefs configuration.\nA really nice idea.  Await PATCH v5.  I had to change my plans for\ntoday, unfortunately.\n\nBert\n>\n"}]}