{"thread":{"id":"15274","subject":"[RFC/PATCH 1/2] for-each-ref: add new format 'refbasename'","startedAt":"2008-08-29T10:37:19Z","lastAt":"2008-08-31T07:11:22Z","messageCount":8,"participants":["SZEDER Gábor","Shawn O. Pearce","Bert Wesarg","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"89117","messageId":"0329f1ab2642e1dab701b5fc6517bfb1b4ea2d46.1220004755.git.szeder@ira.uka.de","threadId":"15274","inReplyTo":null,"subject":"[RFC/PATCH 1/2] for-each-ref: add new format 'refbasename'","fromName":"SZEDER Gábor","fromEmail":"szeder@ira.uka.de","sentAt":"2008-08-29T10:37:19Z","receivedAt":"2008-08-29T10:37:19Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"fot-each-ref's refname format outputs each ref in its full format, e.g.\n'refs/heads/foo' and 'refs/tags/bar'.  However, there are tools that\nneed only the last part of the refname, e.g. only 'foo' and 'bar'.  Such\na tool is git's bash completion script, which spends considerable amount\nof time removing the unneeded parts from for-each-ref's output.\n\nTherefore, we introduce a new for-each-ref format called 'refbasename',\nwhich strips everything before and including the second '/' in the ref's\nname from the output.\n\nSigned-off-by: SZEDER Gábor <szeder@ira.uka.de>\n---\n\n  I assumed that refs always look like 'refs/{heads,tags,whatever}/foo',\n  hence this patch breaks if a ref might look like 'refs/foo' or just\n  'foo'.  But can I really rely on that?\n\n builtin-for-each-ref.c |    5 +++++\n 1 files changed, 5 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 21e92bb..1993ff4 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -66,6 +66,7 @@ static struct {\n \t{ \"subject\" },\n \t{ \"body\" },\n \t{ \"contents\" },\n+\t{ \"refbasename\" },\n };\n \n /*\n@@ -577,6 +578,10 @@ static void populate_value(struct refinfo *ref)\n \t\t\tchar *s = xmalloc(len + 4);\n \t\t\tsprintf(s, \"%s^{}\", ref->refname);\n \t\t\tv->s = s;\n+\t\t} else if (!strcmp(name, \"refbasename\")) {\n+\t\t\tchar * p = strchr(ref->refname, '/');\n+\t\t\tp = strchr(p+1, '/');\n+\t\t\tv->s = p+1;\n \t\t}\n \t}\n \n-- \n1.6.0.1.133.g10dd.dirty\n"},{"id":"89118","messageId":"31df5ea75841b142240e04ac82153c39d5e11abb.1220004755.git.szeder@ira.uka.de","threadId":"15274","inReplyTo":"0329f1ab2642e1dab701b5fc6517bfb1b4ea2d46.1220004755.git.szeder@ira.uka.de","subject":"[RFC/PATCH 2/2] bash: use for-each-ref format 'refbasename'","fromName":"SZEDER Gábor","fromEmail":"szeder@ira.uka.de","sentAt":"2008-08-29T10:37:20Z","receivedAt":"2008-08-29T10:37:20Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"Using this format simplifies the code for completing refs and (in some\ncases) improves performance significantly.\n\nFor repositories like the current git.git (with more than 200 refs)\nthere are no real performance difference, but for a repository with 2000\nrefs the total time needed to complete the refs is around 40% less (from\naround 480ms to around 295ms).\n\nSigned-off-by: SZEDER Gábor <szeder@ira.uka.de>\n---\n contrib/completion/git-completion.bash |   26 ++++++--------------------\n 1 files changed, 6 insertions(+), 20 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 4f64f8a..8269c42 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -154,11 +154,8 @@ __git_heads ()\n {\n \tlocal cmd i is_hash=y dir=\"$(__gitdir \"$1\")\"\n \tif [ -d \"$dir\" ]; then\n-\t\tfor i in $(git --git-dir=\"$dir\" \\\n-\t\t\tfor-each-ref --format='%(refname)' \\\n-\t\t\trefs/heads ); do\n-\t\t\techo \"${i#refs/heads/}\"\n-\t\tdone\n+\t\tgit --git-dir=\"$dir\" for-each-ref --format='%(refbasename)' \\\n+\t\t\trefs/heads\n \t\treturn\n \tfi\n \tfor i in $(git ls-remote \"$1\" 2>/dev/null); do\n@@ -175,11 +172,8 @@ __git_tags ()\n {\n \tlocal cmd i is_hash=y dir=\"$(__gitdir \"$1\")\"\n \tif [ -d \"$dir\" ]; then\n-\t\tfor i in $(git --git-dir=\"$dir\" \\\n-\t\t\tfor-each-ref --format='%(refname)' \\\n-\t\t\trefs/tags ); do\n-\t\t\techo \"${i#refs/tags/}\"\n-\t\tdone\n+\t\tgit --git-dir=\"$dir\" for-each-ref --format='%(refbasename)' \\\n+\t\t\trefs/tags\n \t\treturn\n \tfi\n \tfor i in $(git ls-remote \"$1\" 2>/dev/null); do\n@@ -197,16 +191,8 @@ __git_refs ()\n \tlocal cmd i is_hash=y dir=\"$(__gitdir \"$1\")\"\n \tif [ -d \"$dir\" ]; then\n \t\tif [ -e \"$dir/HEAD\" ]; then echo HEAD; fi\n-\t\tfor i in $(git --git-dir=\"$dir\" \\\n-\t\t\tfor-each-ref --format='%(refname)' \\\n-\t\t\trefs/tags refs/heads refs/remotes); do\n-\t\t\tcase \"$i\" in\n-\t\t\t\trefs/tags/*)    echo \"${i#refs/tags/}\" ;;\n-\t\t\t\trefs/heads/*)   echo \"${i#refs/heads/}\" ;;\n-\t\t\t\trefs/remotes/*) echo \"${i#refs/remotes/}\" ;;\n-\t\t\t\t*)              echo \"$i\" ;;\n-\t\t\tesac\n-\t\tdone\n+\t\tgit --git-dir=\"$dir\" for-each-ref --format='%(refbasename)' \\\n+\t\t\trefs/tags refs/heads refs/remotes\n \t\treturn\n \tfi\n \tfor i in $(git ls-remote \"$dir\" 2>/dev/null); do\n-- \n1.6.0.1.133.g10dd.dirty\n"},{"id":"89125","messageId":"20080829143448.GC7403@spearce.org","threadId":"15274","inReplyTo":"0329f1ab2642e1dab701b5fc6517bfb1b4ea2d46.1220004755.git.szeder@ira.uka.de","subject":"Re: [RFC/PATCH 1/2] for-each-ref: add new format 'refbasename'","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-29T14:34:48Z","receivedAt":"2008-08-29T14:34:48Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"SZEDER GGGbor <szeder@ira.uka.de> wrote:\n> Therefore, we introduce a new for-each-ref format called 'refbasename',\n> which strips everything before and including the second '/' in the ref's\n> name from the output.\n> \n>   I assumed that refs always look like 'refs/{heads,tags,whatever}/foo',\n>   hence this patch breaks if a ref might look like 'refs/foo' or just\n>   'foo'.  But can I really rely on that?\n> \n> diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\n> index 21e92bb..1993ff4 100644\n> --- a/builtin-for-each-ref.c\n> +++ b/builtin-for-each-ref.c\n> @@ -577,6 +578,10 @@ static void populate_value(struct refinfo *ref)\n>  \t\t\tchar *s = xmalloc(len + 4);\n>  \t\t\tsprintf(s, \"%s^{}\", ref->refname);\n>  \t\t\tv->s = s;\n> +\t\t} else if (!strcmp(name, \"refbasename\")) {\n> +\t\t\tchar * p = strchr(ref->refname, '/');\n> +\t\t\tp = strchr(p+1, '/');\n> +\t\t\tv->s = p+1;\n\nPlease be careful here and check for !p.  A refname may be missing\none or two '/' in which case you will cause the process to segfault.\n\nI don't think its a good idea to assume you'll always have to '/'\nin the name.  \"refs/foo\" can be created by git-update-ref.  Or if\nwe ever started to report on HEAD this output tag would crash.\n\nAlso, as a style nit, its \"p + 1\" not \"p+1\".\n\nTest cases?\n\nOn the other hand, I like where this is going.  Given that it\nhas such a nice effect on the bash completion for larger repos\nI'd like to see it in Git.\n\n-- \nShawn.\n"},{"id":"89144","messageId":"20080829164508.GF8000@neumann","threadId":"15274","inReplyTo":"20080829143448.GC7403@spearce.org","subject":"[RFC/PATCH 1/2 v2] for-each-ref: add new format 'refbasename'","fromName":"SZEDER Gábor","fromEmail":"szeder@ira.uka.de","sentAt":"2008-08-29T16:45:08Z","receivedAt":"2008-08-29T16:45:08Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"for-each-ref's refname format outputs each ref in its full format, e.g.\n'refs/heads/foo' and 'refs/tags/bar'.  However, there are tools that\nneed only the last part of the refname, e.g. only 'foo' and 'bar'.  Such\na tool is git's bash completion script, which spends considerable amount\nof time removing the unneeded parts from for-each-ref's output.\n\nTherefore, we introduce a new for-each-ref format called 'refbasename',\nwhich strips the leading parts of the refname:\n\n  * If the refname doesn't contain any '/', then it is printed as is.\n\n  * If the refname contains one '/', then the string following the '/'\n    is printed (e.g. 'refs/foo' becomes 'foo').\n\n  * If the refname contains two (or more) '/', then the string following\n    the second '/' is printed (e.g. 'refs/heads/foo' becomes 'foo').\n    \nSigned-off-by: SZEDER Gábor <szeder@ira.uka.de>\n---\n\nOn Fri, Aug 29, 2008 at 07:34:48AM -0700, Shawn O. Pearce wrote:\n> SZEDER GGGbor <szeder@ira.uka.de> wrote:\n> > +\t\t} else if (!strcmp(name, \"refbasename\")) {\n> > +\t\t\tchar * p = strchr(ref->refname, '/');\n> > +\t\t\tp = strchr(p+1, '/');\n> > +\t\t\tv->s = p+1;\n> \n> Please be careful here and check for !p.  A refname may be missing\n> one or two '/' in which case you will cause the process to segfault.\n> \n> I don't think its a good idea to assume you'll always have to '/'\n> in the name.  \"refs/foo\" can be created by git-update-ref.  Or if\n> we ever started to report on HEAD this output tag would crash.\nAh, I feared as much.\n\nThis raises the question what should the refbasename format print in\nthose cases.  The first case (no '/' at all) is trivial, but the other\ntwo are a bit problematic:\n\n  * The refname 'foo/bar' seems to be valid, or at least the command\n    sequence 'mkdir .git/foo; cp .git/refs/heads/master .git/foo/bar;\n    git checkout foo/bar' works as expected.  OTOH, 'git for-each-ref\n    --format=\"%(refname)\"' doesn't print anything.\n\n  * The refname 'refs/head/foo/bar' should become 'foo/bar':  it's in \n    sync with how 'git branch' lists it, and this behaviour is needed\n    by the bash completion changes.  However, unfortunately it\n    contradicts to the format's name, because basename removes all\n    directory components.  Maybe someone has a better idea on how to\n    name this format...\n\n\n builtin-for-each-ref.c  |   12 ++++++++++++\n t/t6300-for-each-ref.sh |    2 ++\n 2 files changed, 14 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 21e92bb..23d064b 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -66,6 +66,7 @@ static struct {\n \t{ \"subject\" },\n \t{ \"body\" },\n \t{ \"contents\" },\n+\t{ \"refbasename\" },\n };\n \n /*\n@@ -577,6 +578,17 @@ static void populate_value(struct refinfo *ref)\n \t\t\tchar *s = xmalloc(len + 4);\n \t\t\tsprintf(s, \"%s^{}\", ref->refname);\n \t\t\tv->s = s;\n+\t\t} else if (!strcmp(name, \"refbasename\")) {\n+\t\t\tchar * slash1 = strchr(ref->refname, '/');\n+\t\t\tif (!slash1)\n+\t\t\t\tv->s = ref->refname;\n+\t\t\telse {\n+\t\t\t\tchar * slash2 = strchr(slash1 + 1, '/');\n+\t\t\t\tif (!slash2)\n+\t\t\t\t\tv->s = slash1 + 1;\n+\t\t\t\telse\n+\t\t\t\t\tv->s = slash2 + 1;\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..b317a01 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -66,6 +66,7 @@ test_atom head subject 'Initial'\n test_atom head body ''\n test_atom head contents 'Initial\n '\n+test_atom head refbasename 'master'\n \n test_atom tag refname refs/tags/testtag\n test_atom tag objecttype tag\n@@ -95,6 +96,7 @@ test_atom tag subject 'Tagging at 1151939927'\n test_atom tag body ''\n test_atom tag contents 'Tagging at 1151939927\n '\n+test_atom tag refbasename 'testtag'\n \n test_expect_success 'Check invalid atoms names are errors' '\n \ttest_must_fail git-for-each-ref --format=\"%(INVALID)\" refs/heads\n-- \n1.6.0.1.149.g903b0\n"},{"id":"89146","messageId":"36ca99e90808291121j7b202917r2308719970668be3@mail.gmail.com","threadId":"15274","inReplyTo":"0329f1ab2642e1dab701b5fc6517bfb1b4ea2d46.1220004755.git.szeder@ira.uka.de","subject":"Re: [RFC/PATCH 1/2] for-each-ref: add new format 'refbasename'","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-08-29T18:21:17Z","receivedAt":"2008-08-29T18:21:17Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Fri, Aug 29, 2008 at 12:37, SZEDER Gábor <szeder@ira.uka.de> wrote:\n> fot-each-ref's refname format outputs each ref in its full format, e.g.\n> 'refs/heads/foo' and 'refs/tags/bar'.  However, there are tools that\n> need only the last part of the refname, e.g. only 'foo' and 'bar'.  Such\n> a tool is git's bash completion script, which spends considerable amount\n> of time removing the unneeded parts from for-each-ref's output.\n>\n> Therefore, we introduce a new for-each-ref format called 'refbasename',\n> which strips everything before and including the second '/' in the ref's\n> name from the output.\n\nWhy not strip the pattern (given on the command line) from the\nrefname? I.e. with a new format 'refshort' a 'git for-each-ref\n--format=%(refshort) refs/heads/' would strip off the refs/heads/ from\neach ref.\n\nThis should also work for multiple patterns (refs/heads/ refs/tags/)\nand for patterns which needs a fnmatch call (which has the\nFNM_PATHNAME option).\n\nThe bash completion script calls for-each-ref always with patterns.\n\nBert\n"},{"id":"89163","messageId":"1220046116-23703-1-git-send-email-bert.wesarg@googlemail.com","threadId":"15274","inReplyTo":"36ca99e90808291121j7b202917r2308719970668be3@mail.gmail.com","subject":"[PATCH] for-each-ref: new 'refshort' format","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-08-29T21:41:56Z","receivedAt":"2008-08-29T21:41:56Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"Here is a first sketch for this idea.\n\nThis strips from the refname the common prefix with the matched pattern.\n\nSigned-off-by: Bert Wesarg <bert.wesarg@googlemail.com>\n\n---\n\nI think we should use a ':short' modifier to 'reflog' and '*reflog'.\n\n builtin-for-each-ref.c |   39 +++++++++++++++++++++++++++++++++++++--\n 1 files changed, 37 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex 21e92bb..b80d753 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@@ -66,6 +67,7 @@ static struct {\n \t{ \"subject\" },\n \t{ \"body\" },\n \t{ \"contents\" },\n+\t{ \"refshort\" },\n };\n \n /*\n@@ -546,6 +548,36 @@ 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, len = 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\tlen = plen + (ref->refname[plen] == '/');\n+\t} else {\n+\t\tlen = strcspn(ref->pattern, \"*?[\");\n+\t\twhile (len >= 0 && ref->pattern[len] != '/')\n+\t\t\t--len;\n+\t\tlen++;\n+\t}\n+\n+\treturn ref->refname + len;\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@@ -577,6 +609,8 @@ static void populate_value(struct refinfo *ref)\n \t\t\tchar *s = xmalloc(len + 4);\n \t\t\tsprintf(s, \"%s^{}\", ref->refname);\n \t\t\tv->s = s;\n+\t\t} else if (!strcmp(name, \"refshort\")) {\n+\t\t\tv->s = get_short_ref(ref);\n \t\t}\n \t}\n \n@@ -641,9 +675,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 +702,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;\n-- \n1.6.0\n"},{"id":"89284","messageId":"7vmyiterfd.fsf@gitster.siamese.dyndns.org","threadId":"15274","inReplyTo":"1220046116-23703-1-git-send-email-bert.wesarg@googlemail.com","subject":"Re: [PATCH] for-each-ref: new 'refshort' format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-31T04:31:34Z","receivedAt":"2008-08-31T04:31:34Z","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> Here is a first sketch for this idea.\n>\n> This strips from the refname the common prefix with the matched pattern.\n\nLooks quite straightforward and to the point.\n"},{"id":"89292","messageId":"36ca99e90808310011s3af5929fw5bb67be2bff4fab@mail.gmail.com","threadId":"15274","inReplyTo":"7vmyiterfd.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] for-each-ref: new 'refshort' format","fromName":"Bert Wesarg","fromEmail":"bert.wesarg@googlemail.com","sentAt":"2008-08-31T07:11:22Z","receivedAt":"2008-08-31T07:11:22Z","isPatch":true,"sender":{"key":"bert.wesarg@googlemail.com","avatar":"https://avatars.githubusercontent.com/u/111934?v=4"},"body":"On Sun, Aug 31, 2008 at 06:31, Junio C Hamano <gitster@pobox.com> wrote:\n> Bert Wesarg <bert.wesarg@googlemail.com> writes:\n>\n>> Here is a first sketch for this idea.\n>>\n>> This strips from the refname the common prefix with the matched pattern.\n>\n> Looks quite straightforward and to the point.\n>\nWhat do you think about the ':short' modifier for 'refname' or\n'*refname' instead 'refshort'? i.e. %(refname:short)\n\nBert\n"}]}