{"thread":{"id":"44771","subject":"[PATCH v9 00/20] port branch.c to use ref-filter's printing options","startedAt":"2016-12-27T16:23:17Z","lastAt":"2016-12-28T09:41:26Z","messageCount":34,"participants":["Karthik Nayak","Junio C Hamano","Jacob Keller"],"isPatch":true,"patchVersion":9,"patchTotal":20},"messages":[{"id":"308368","messageId":"20161227162357.28212-1-Karthik.188@gmail.com","threadId":"44771","inReplyTo":null,"subject":"[PATCH v9 00/20] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:37Z","receivedAt":"2016-12-27T16:23:17Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"This is part of unification of the commands 'git tag -l, git branch -l\nand git for-each-ref'. This ports over branch.c to use ref-filter's\nprinting options.\n\nInitially posted here: $(gmane/279226). It was decided that this series\nwould follow up after refactoring ref-filter parsing mechanism, which\nis now merged into master (9606218b32344c5c756f7c29349d3845ef60b80c).\n\nv1 can be found here: $(gmane/288342)\nv2 can be found here: $(gmane/288863)\nv3 can be found here: $(gmane/290299)\nv4 can be found here: $(gmane/291106)\nv5b can be found here: $(gmane/292467)\nv6 can be found here: http://marc.info/?l=git&m=146330914118766&w=2\nv7 can be found here: http://marc.info/?l=git&m=147863593317362&w=2\nv8 can be found here: http://marc.info/?l=git&m=148112502029302&w=2\n\nChanges in this version:\n1. A few formatting errors.\n2. Made quote_literal_for_format() static.\n3. lstrip and rstrip doesn't die on less components.\n\nThanks Jacob, Junio, Jeff, Ramsay for their suggestions and help.\n\nKarthik Nayak (20):\n  ref-filter: implement %(if), %(then), and %(else) atoms\n  ref-filter: include reference to 'used_atom' within 'atom_value'\n  ref-filter: implement %(if:equals=<string>) and\n    %(if:notequals=<string>)\n  ref-filter: modify \"%(objectname:short)\" to take length\n  ref-filter: move get_head_description() from branch.c\n  ref-filter: introduce format_ref_array_item()\n  ref-filter: make %(upstream:track) prints \"[gone]\" for invalid\n    upstreams\n  ref-filter: add support for %(upstream:track,nobracket)\n  ref-filter: make \"%(symref)\" atom work with the ':short' modifier\n  ref-filter: introduce refname_atom_parser_internal()\n  ref-filter: introduce refname_atom_parser()\n  ref-filter: make remote_ref_atom_parser() use\n    refname_atom_parser_internal()\n  ref-filter: rename the 'strip' option to 'lstrip'\n  ref-filter: Do not abruptly die when using the 'lstrip=<N>' option\n  ref-filter: modify the 'lstrip=<N>' option to work with negative '<N>'\n  ref-filter: add an 'rstrip=<N>' option to atoms which deal with\n    refnames\n  ref-filter: allow porcelain to translate messages in the output\n  branch, tag: use porcelain output\n  branch: use ref-filter printing APIs\n  branch: implement '--format' option\n\n Documentation/git-branch.txt       |   7 +-\n Documentation/git-for-each-ref.txt |  86 +++++--\n builtin/branch.c                   | 290 +++++++---------------\n builtin/tag.c                      |   6 +-\n ref-filter.c                       | 490 +++++++++++++++++++++++++++++++------\n ref-filter.h                       |   7 +\n t/t3203-branch-output.sh           |  16 +-\n t/t6040-tracking-info.sh           |   2 +-\n t/t6300-for-each-ref.sh            |  88 ++++++-\n t/t6302-for-each-ref-filter.sh     |  94 +++++++\n 10 files changed, 780 insertions(+), 306 deletions(-)\n\nInterdiff:\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 6393c3c..4051a18 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -306,9 +306,9 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n \treturn max;\n }\n\n-const char *quote_literal_for_format(const char *s)\n+static const char *quote_literal_for_format(const char *s)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n+\tstatic struct strbuf buf = STRBUF_INIT;\n\n \tstrbuf_reset(&buf);\n \twhile (*s) {\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -76,10 +77,10 @@ static struct used_atom {\n \t\tstruct {\n \t\t\tenum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;\n \t\t\tstruct refname_atom refname;\n-\t\t\tunsigned int nobracket: 1;\n+\t\t\tunsigned int nobracket : 1;\n \t\t} remote_ref;\n@@ -1106,7 +1126,8 @@ static const char *lstrip_ref_components(const char *refname, int len)\n \t\tconst char *p = refname;\n\n \t\t/* Find total no of '/' separated path-components */\n-\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++);\n+\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++)\n+\t\t\t;\n \t\t/*\n \t\t * The number of components we need to strip is now\n \t\t * the total minus the components to be left (Plus one\n@@ -1116,11 +1137,10 @@ static const char *lstrip_ref_components(const char *refname, int len)\n \t\tremaining = i + len + 1;\n \t}\n\n-\twhile (remaining) {\n+\twhile (remaining > 0) {\n \t\tswitch (*start++) {\n \t\tcase '\\0':\n-\t\t\tdie(_(\"ref '%s' does not have %d components to :lstrip\"),\n-\t\t\t    refname, len);\n+\t\t\treturn \"\";\n \t\tcase '/':\n \t\t\tremaining--;\n \t\t\tbreak;\n@@ -1140,7 +1160,8 @@ static const char *rstrip_ref_components(const char *refname, int len)\n \t\tconst char *p = refname;\n\n \t\t/* Find total no of '/' separated path-components */\n-\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++);\n+\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++)\n+\t\t\t;\n \t\t/*\n \t\t * The number of components we need to strip is now\n \t\t * the total minus the components to be left (Plus one\n@@ -1150,11 +1171,10 @@ static const char *rstrip_ref_components(const char *refname, int len)\n \t\tremaining = i + len + 1;\n \t}\n\n-\twhile (remaining--) {\n+\twhile (remaining-- > 0) {\n \t\tchar *p = strrchr(start, '/');\n \t\tif (p == NULL)\n-\t\t\tdie(_(\"ref '%s' does not have %d components to :rstrip\"),\n-\t\t\t  refname, len);\n+\t\t\treturn \"\";\n \t\telse\n \t\t\tp[0] = '\\0';\n \t}\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -153,16 +153,6 @@ test_expect_success 'Check invalid atoms names are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n '\n\n-test_expect_success 'stripping refnames too far gives an error' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=3)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=-4)\"\n-'\n-\n-test_expect_success 'stripping refnames too far gives an error' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:rstrip=3)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:rstrip=-4)\"\n-'\n-\n test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n \tgit for-each-ref --format=\"%(authordate)\" refs/heads &&\n \tgit for-each-ref --format=\"%(authordate:default) %(authordate)\" refs/heads &&\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex c72baeb..81db67d 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -92,14 +95,13 @@ refname::\n        The name of the ref (the part after $GIT_DIR/).\n        For a non-ambiguous short name of the ref append `:short`.\n        The option core.warnAmbiguousRefs is used to select the strict\n-       abbreviation mode. If `lstrip=<N>` or `rstrip=<N>` option can\n+       abbreviation mode. The `lstrip=<N>` or `rstrip=<N>` option can\n        be appended to strip `<N>` slash-separated path components\n-       from or end of the refname respectively (e.g.,\n+       from the left or right of the refname respectively (e.g.,\n        `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and\n        `%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).  if\n        `<N>` is a negative number, then only `<N>` path components\n-       are left behind.  If a displayed ref has fewer components than\n-       `<N>`, the command aborts with an error.\n+       are left behind.\n\n objecttype::\n        The type of the object (`blob`, `tree`, `commit`, `tag`).\n\n\n\n--\n2.10.2\n"},{"id":"308369","messageId":"20161227162357.28212-2-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 01/20] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:38Z","receivedAt":"2016-12-27T16:23:28Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nImplement %(if), %(then) and %(else) atoms. Used as\n%(if)...%(then)...%(end) or %(if)...%(then)...%(else)...%(end). If the\nformat string between %(if) and %(then) expands to an empty string, or\nto only whitespaces, then the whole %(if)...%(end) expands to the string\nfollowing %(then). Otherwise, it expands to the string following\n%(else), if any. Nesting of this construct is possible.\n\nThis is in preparation for porting over `git branch -l` to use\nref-filter APIs for printing.\n\nAdd Documentation and tests regarding the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  34 ++++++++++\n ref-filter.c                       | 134 +++++++++++++++++++++++++++++++++++--\n t/t6302-for-each-ref-filter.sh     |  76 +++++++++++++++++++++\n 3 files changed, 237 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex abe13f3..5e80c34 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -149,6 +149,16 @@ align::\n \tquoted, but if nested then only the topmost level performs\n \tquoting.\n \n+if::\n+\tUsed as %(if)...%(then)...%(end) or\n+\t%(if)...%(then)...%(else)...%(end).  If there is an atom with\n+\tvalue or string literal after the %(if) then everything after\n+\tthe %(then) is printed, else if the %(else) atom is used, then\n+\teverything after %(else) is printed. We ignore space when\n+\tevaluating the string before %(then), this is useful when we\n+\tuse the %(HEAD) atom which prints either \"*\" or \" \" and we\n+\twant to apply the 'if' condition only on the 'HEAD' ref.\n+\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\n@@ -186,6 +196,14 @@ As a special case for the date-type fields, you may specify a format for\n the date by adding `:` followed by date format name (see the\n values the `--date` option to linkgit:git-rev-list[1] takes).\n \n+Some atoms like %(align) and %(if) always require a matching %(end).\n+We call them \"opening atoms\" and sometimes denote them as %($open).\n+\n+When a scripting language specific quoting is in effect, everything\n+between a top-level opening atom and its matching %(end) is evaluated\n+according to the semantics of the opening atom and its result is\n+quoted.\n+\n \n EXAMPLES\n --------\n@@ -273,6 +291,22 @@ eval=`git for-each-ref --shell --format=\"$fmt\" \\\n eval \"$eval\"\n ------------\n \n+\n+An example to show the usage of %(if)...%(then)...%(else)...%(end).\n+This prefixes the current branch with a star.\n+\n+------------\n+git for-each-ref --format=\"%(if)%(HEAD)%(then)* %(else)  %(end)%(refname:short)\" refs/heads/\n+------------\n+\n+\n+An example to show the usage of %(if)...%(then)...%(end).\n+This prints the authorname, if present.\n+\n+------------\n+git for-each-ref --format=\"%(refname)%(if)%(authorname)%(then) %(color:red)Authored by: %(authorname)%(end)\"\n+------------\n+\n SEE ALSO\n --------\n linkgit:git-show-ref[1]\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 1a97840..0a57872 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -22,6 +22,12 @@ struct align {\n \tunsigned int width;\n };\n \n+struct if_then_else {\n+\tunsigned int then_atom_seen : 1,\n+\t\telse_atom_seen : 1,\n+\t\tcondition_satisfied : 1;\n+};\n+\n /*\n  * An atom is a valid field atom listed below, possibly prefixed with\n  * a \"*\" to denote deref_tag().\n@@ -214,6 +220,9 @@ static struct {\n \t{ \"color\", FIELD_STR, color_atom_parser },\n \t{ \"align\", FIELD_STR, align_atom_parser },\n \t{ \"end\" },\n+\t{ \"if\" },\n+\t{ \"then\" },\n+\t{ \"else\" },\n };\n \n #define REF_FORMATTING_STATE_INIT  { 0, NULL }\n@@ -221,7 +230,7 @@ static struct {\n struct ref_formatting_stack {\n \tstruct ref_formatting_stack *prev;\n \tstruct strbuf output;\n-\tvoid (*at_end)(struct ref_formatting_stack *stack);\n+\tvoid (*at_end)(struct ref_formatting_stack **stack);\n \tvoid *at_end_data;\n };\n \n@@ -354,13 +363,14 @@ static void pop_stack_element(struct ref_formatting_stack **stack)\n \t*stack = prev;\n }\n \n-static void end_align_handler(struct ref_formatting_stack *stack)\n+static void end_align_handler(struct ref_formatting_stack **stack)\n {\n-\tstruct align *align = (struct align *)stack->at_end_data;\n+\tstruct ref_formatting_stack *cur = *stack;\n+\tstruct align *align = (struct align *)cur->at_end_data;\n \tstruct strbuf s = STRBUF_INIT;\n \n-\tstrbuf_utf8_align(&s, align->position, align->width, stack->output.buf);\n-\tstrbuf_swap(&stack->output, &s);\n+\tstrbuf_utf8_align(&s, align->position, align->width, cur->output.buf);\n+\tstrbuf_swap(&cur->output, &s);\n \tstrbuf_release(&s);\n }\n \n@@ -374,6 +384,104 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s\n \tnew->at_end_data = &atomv->u.align;\n }\n \n+static void if_then_else_handler(struct ref_formatting_stack **stack)\n+{\n+\tstruct ref_formatting_stack *cur = *stack;\n+\tstruct ref_formatting_stack *prev = cur->prev;\n+\tstruct if_then_else *if_then_else = (struct if_then_else *)cur->at_end_data;\n+\n+\tif (!if_then_else->then_atom_seen)\n+\t\tdie(_(\"format: %%(if) atom used without a %%(then) atom\"));\n+\n+\tif (if_then_else->else_atom_seen) {\n+\t\t/*\n+\t\t * There is an %(else) atom: we need to drop one state from the\n+\t\t * stack, either the %(else) branch if the condition is satisfied, or\n+\t\t * the %(then) branch if it isn't.\n+\t\t */\n+\t\tif (if_then_else->condition_satisfied) {\n+\t\t\tstrbuf_reset(&cur->output);\n+\t\t\tpop_stack_element(&cur);\n+\t\t} else {\n+\t\t\tstrbuf_swap(&cur->output, &prev->output);\n+\t\t\tstrbuf_reset(&cur->output);\n+\t\t\tpop_stack_element(&cur);\n+\t\t}\n+\t} else if (!if_then_else->condition_satisfied) {\n+\t\t/*\n+\t\t * No %(else) atom: just drop the %(then) branch if the\n+\t\t * condition is not satisfied.\n+\t\t */\n+\t\tstrbuf_reset(&cur->output);\n+\t}\n+\n+\t*stack = cur;\n+\tfree(if_then_else);\n+}\n+\n+static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n+{\n+\tstruct ref_formatting_stack *new;\n+\tstruct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);\n+\n+\tpush_stack_element(&state->stack);\n+\tnew = state->stack;\n+\tnew->at_end = if_then_else_handler;\n+\tnew->at_end_data = if_then_else;\n+}\n+\n+static int is_empty(const char *s)\n+{\n+\twhile (*s != '\\0') {\n+\t\tif (!isspace(*s))\n+\t\t\treturn 0;\n+\t\ts++;\n+\t}\n+\treturn 1;\n+}\n+\n+static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n+{\n+\tstruct ref_formatting_stack *cur = state->stack;\n+\tstruct if_then_else *if_then_else = NULL;\n+\n+\tif (cur->at_end == if_then_else_handler)\n+\t\tif_then_else = (struct if_then_else *)cur->at_end_data;\n+\tif (!if_then_else)\n+\t\tdie(_(\"format: %%(then) atom used without an %%(if) atom\"));\n+\tif (if_then_else->then_atom_seen)\n+\t\tdie(_(\"format: %%(then) atom used more than once\"));\n+\tif (if_then_else->else_atom_seen)\n+\t\tdie(_(\"format: %%(then) atom used after %%(else)\"));\n+\tif_then_else->then_atom_seen = 1;\n+\t/*\n+\t * If there exists non-empty string between the 'if' and\n+\t * 'then' atom then the 'if' condition is satisfied.\n+\t */\n+\tif (cur->output.len && !is_empty(cur->output.buf))\n+\t\tif_then_else->condition_satisfied = 1;\n+\tstrbuf_reset(&cur->output);\n+}\n+\n+static void else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n+{\n+\tstruct ref_formatting_stack *prev = state->stack;\n+\tstruct if_then_else *if_then_else = NULL;\n+\n+\tif (prev->at_end == if_then_else_handler)\n+\t\tif_then_else = (struct if_then_else *)prev->at_end_data;\n+\tif (!if_then_else)\n+\t\tdie(_(\"format: %%(else) atom used without an %%(if) atom\"));\n+\tif (!if_then_else->then_atom_seen)\n+\t\tdie(_(\"format: %%(else) atom used without a %%(then) atom\"));\n+\tif (if_then_else->else_atom_seen)\n+\t\tdie(_(\"format: %%(else) atom used more than once\"));\n+\tif_then_else->else_atom_seen = 1;\n+\tpush_stack_element(&state->stack);\n+\tstate->stack->at_end_data = prev->at_end_data;\n+\tstate->stack->at_end = prev->at_end;\n+}\n+\n static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n {\n \tstruct ref_formatting_stack *current = state->stack;\n@@ -381,14 +489,17 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta\n \n \tif (!current->at_end)\n \t\tdie(_(\"format: %%(end) atom used without corresponding atom\"));\n-\tcurrent->at_end(current);\n+\tcurrent->at_end(&state->stack);\n+\n+\t/*  Stack may have been popped within at_end(), hence reset the current pointer */\n+\tcurrent = state->stack;\n \n \t/*\n \t * Perform quote formatting when the stack element is that of\n \t * a supporting atom. If nested then perform quote formatting\n \t * only on the topmost supporting atom.\n \t */\n-\tif (!state->stack->prev->prev) {\n+\tif (!current->prev->prev) {\n \t\tquote_formatting(&s, current->output.buf, state->quote_style);\n \t\tstrbuf_swap(&current->output, &s);\n \t}\n@@ -1049,6 +1160,15 @@ static void populate_value(struct ref_array_item *ref)\n \t\t} else if (!strcmp(name, \"end\")) {\n \t\t\tv->handler = end_atom_handler;\n \t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"if\")) {\n+\t\t\tv->handler = if_atom_handler;\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"then\")) {\n+\t\t\tv->handler = then_atom_handler;\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"else\")) {\n+\t\t\tv->handler = else_atom_handler;\n+\t\t\tcontinue;\n \t\t} else\n \t\t\tcontinue;\n \ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex d0ab09f..fed3013 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -327,4 +327,80 @@ test_expect_success 'reverse version sort' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'improper usage of %(if), %(then), %(else) and %(end) atoms' '\n+\ttest_must_fail git for-each-ref --format=\"%(if)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(then) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(then) %(then) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(then) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(then) %(else)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(else) %(then) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(then) %(else) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(end)\"\n+'\n+\n+test_expect_success 'check %(if)...%(then)...%(end) atoms' '\n+\tgit for-each-ref --format=\"%(refname)%(if)%(authorname)%(then) Author: %(authorname)%(end)\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/master Author: A U Thor\n+\trefs/heads/side Author: A U Thor\n+\trefs/odd/spot Author: A U Thor\n+\trefs/tags/annotated-tag\n+\trefs/tags/doubly-annotated-tag\n+\trefs/tags/doubly-signed-tag\n+\trefs/tags/foo1.10 Author: A U Thor\n+\trefs/tags/foo1.3 Author: A U Thor\n+\trefs/tags/foo1.6 Author: A U Thor\n+\trefs/tags/four Author: A U Thor\n+\trefs/tags/one Author: A U Thor\n+\trefs/tags/signed-tag\n+\trefs/tags/three Author: A U Thor\n+\trefs/tags/two Author: A U Thor\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'check %(if)...%(then)...%(else)...%(end) atoms' '\n+\tgit for-each-ref --format=\"%(if)%(authorname)%(then)%(authorname)%(else)No author%(end): %(refname)\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tA U Thor: refs/heads/master\n+\tA U Thor: refs/heads/side\n+\tA U Thor: refs/odd/spot\n+\tNo author: refs/tags/annotated-tag\n+\tNo author: refs/tags/doubly-annotated-tag\n+\tNo author: refs/tags/doubly-signed-tag\n+\tA U Thor: refs/tags/foo1.10\n+\tA U Thor: refs/tags/foo1.3\n+\tA U Thor: refs/tags/foo1.6\n+\tA U Thor: refs/tags/four\n+\tA U Thor: refs/tags/one\n+\tNo author: refs/tags/signed-tag\n+\tA U Thor: refs/tags/three\n+\tA U Thor: refs/tags/two\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+test_expect_success 'ignore spaces in %(if) atom usage' '\n+\tgit for-each-ref --format=\"%(refname:short): %(if)%(HEAD)%(then)Head ref%(else)Not Head ref%(end)\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tmaster: Head ref\n+\tside: Not Head ref\n+\todd/spot: Not Head ref\n+\tannotated-tag: Not Head ref\n+\tdoubly-annotated-tag: Not Head ref\n+\tdoubly-signed-tag: Not Head ref\n+\tfoo1.10: Not Head ref\n+\tfoo1.3: Not Head ref\n+\tfoo1.6: Not Head ref\n+\tfour: Not Head ref\n+\tone: Not Head ref\n+\tsigned-tag: Not Head ref\n+\tthree: Not Head ref\n+\ttwo: Not Head ref\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"308370","messageId":"20161227162357.28212-4-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 03/20] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:40Z","receivedAt":"2016-12-27T16:23:31Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nImplement %(if:equals=<string>) wherein the if condition is only\nsatisfied if the value obtained between the %(if:...) and %(then) atom\nis the same as the given '<string>'.\n\nSimilarly, implement (if:notequals=<string>) wherein the if condition\nis only satisfied if the value obtained between the %(if:...) and\n%(then) atom is different from the given '<string>'.\n\nThis is done by introducing 'if_atom_parser()' which parses the given\n%(if) atom and then stores the data in used_atom which is later passed\non to the used_atom of the %(then) atom, so that it can do the required\ncomparisons.\n\nAdd tests and Documentation for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 +++\n ref-filter.c                       | 46 +++++++++++++++++++++++++++++++++-----\n t/t6302-for-each-ref-filter.sh     | 18 +++++++++++++++\n 3 files changed, 62 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 5e80c34..39aab09 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -158,6 +158,9 @@ if::\n \tevaluating the string before %(then), this is useful when we\n \tuse the %(HEAD) atom which prints either \"*\" or \" \" and we\n \twant to apply the 'if' condition only on the 'HEAD' ref.\n+\tAppend \":equals=<string>\" or \":notequals=<string>\" to compare\n+\tthe value between the %(if:...) and %(then) atoms with the\n+\tgiven string.\n \n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\ndiff --git a/ref-filter.c b/ref-filter.c\nindex f31c4b6..e002629 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -16,6 +16,7 @@\n #include \"trailer.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n+typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;\n \n struct align {\n \talign_type position;\n@@ -23,6 +24,8 @@ struct align {\n };\n \n struct if_then_else {\n+\tcmp_status cmp_status;\n+\tconst char *str;\n \tunsigned int then_atom_seen : 1,\n \t\telse_atom_seen : 1,\n \t\tcondition_satisfied : 1;\n@@ -50,6 +53,10 @@ static struct used_atom {\n \t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n \t\t\tunsigned int nlines;\n \t\t} contents;\n+\t\tstruct {\n+\t\t\tcmp_status cmp_status;\n+\t\t\tconst char *str;\n+\t\t} if_then_else;\n \t\tenum { O_FULL, O_SHORT } objectname;\n \t} u;\n } *used_atom;\n@@ -179,6 +186,21 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)\n \tstring_list_clear(&params, 0);\n }\n \n+static void if_atom_parser(struct used_atom *atom, const char *arg)\n+{\n+\tif (!arg) {\n+\t\tatom->u.if_then_else.cmp_status = COMPARE_NONE;\n+\t\treturn;\n+\t} else if (skip_prefix(arg, \"equals=\", &atom->u.if_then_else.str)) {\n+\t\tatom->u.if_then_else.cmp_status = COMPARE_EQUAL;\n+\t} else if (skip_prefix(arg, \"notequals=\", &atom->u.if_then_else.str)) {\n+\t\tatom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;\n+\t} else {\n+\t\tdie(_(\"unrecognized %%(if) argument: %s\"), arg);\n+\t}\n+}\n+\n+\n static struct {\n \tconst char *name;\n \tcmp_type cmp_type;\n@@ -220,7 +242,7 @@ static struct {\n \t{ \"color\", FIELD_STR, color_atom_parser },\n \t{ \"align\", FIELD_STR, align_atom_parser },\n \t{ \"end\" },\n-\t{ \"if\" },\n+\t{ \"if\", FIELD_STR, if_atom_parser },\n \t{ \"then\" },\n \t{ \"else\" },\n };\n@@ -422,6 +444,9 @@ static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_stat\n \tstruct ref_formatting_stack *new;\n \tstruct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);\n \n+\tif_then_else->str = atomv->atom->u.if_then_else.str;\n+\tif_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;\n+\n \tpush_stack_element(&state->stack);\n \tnew = state->stack;\n \tnew->at_end = if_then_else_handler;\n@@ -453,10 +478,17 @@ static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_st\n \t\tdie(_(\"format: %%(then) atom used after %%(else)\"));\n \tif_then_else->then_atom_seen = 1;\n \t/*\n-\t * If there exists non-empty string between the 'if' and\n-\t * 'then' atom then the 'if' condition is satisfied.\n+\t * If the 'equals' or 'notequals' attribute is used then\n+\t * perform the required comparison. If not, only non-empty\n+\t * strings satisfy the 'if' condition.\n \t */\n-\tif (cur->output.len && !is_empty(cur->output.buf))\n+\tif (if_then_else->cmp_status == COMPARE_EQUAL) {\n+\t\tif (!strcmp(if_then_else->str, cur->output.buf))\n+\t\t\tif_then_else->condition_satisfied = 1;\n+\t} else \tif (if_then_else->cmp_status == COMPARE_UNEQUAL) {\n+\t\tif (strcmp(if_then_else->str, cur->output.buf))\n+\t\t\tif_then_else->condition_satisfied = 1;\n+\t} else if (cur->output.len && !is_empty(cur->output.buf))\n \t\tif_then_else->condition_satisfied = 1;\n \tstrbuf_reset(&cur->output);\n }\n@@ -1158,7 +1190,11 @@ static void populate_value(struct ref_array_item *ref)\n \t\t} else if (!strcmp(name, \"end\")) {\n \t\t\tv->handler = end_atom_handler;\n \t\t\tcontinue;\n-\t\t} else if (!strcmp(name, \"if\")) {\n+\t\t} else if (starts_with(name, \"if\")) {\n+\t\t\tconst char *s;\n+\n+\t\t\tif (skip_prefix(name, \"if:\", &s))\n+\t\t\t\tv->s = xstrdup(s);\n \t\t\tv->handler = if_atom_handler;\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"then\")) {\ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex fed3013..a09a1a4 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -403,4 +403,22 @@ test_expect_success 'ignore spaces in %(if) atom usage' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'check %(if:equals=<string>)' '\n+\tgit for-each-ref --format=\"%(if:equals=master)%(refname:short)%(then)Found master%(else)Not master%(end)\" refs/heads/ >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tFound master\n+\tNot master\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'check %(if:notequals=<string>)' '\n+\tgit for-each-ref --format=\"%(if:notequals=master)%(refname:short)%(then)Not master%(else)Found master%(end)\" refs/heads/ >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tFound master\n+\tNot master\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"308371","messageId":"20161227162357.28212-3-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 02/20] ref-filter: include reference to 'used_atom' within 'atom_value'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:39Z","receivedAt":"2016-12-27T16:23:32Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nEnsure that each 'atom_value' has a reference to its corresponding\n'used_atom'. This let's us use values within 'used_atom' in the\n'handler' function.\n\nHence we can get the %(align) atom's parameters directly from the\n'used_atom' therefore removing the necessity of passing %(align) atom's\nparameters to 'atom_value'.\n\nThis also acts as a preparatory patch for the upcoming patch where we\nintroduce %(if:equals=) and %(if:notequals=).\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n ref-filter.c | 8 +++-----\n 1 file changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 0a57872..f31c4b6 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -241,11 +241,9 @@ struct ref_formatting_state {\n \n struct atom_value {\n \tconst char *s;\n-\tunion {\n-\t\tstruct align align;\n-\t} u;\n \tvoid (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n+\tstruct used_atom *atom;\n };\n \n /*\n@@ -381,7 +379,7 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s\n \tpush_stack_element(&state->stack);\n \tnew = state->stack;\n \tnew->at_end = end_align_handler;\n-\tnew->at_end_data = &atomv->u.align;\n+\tnew->at_end_data = &atomv->atom->u.align;\n }\n \n static void if_then_else_handler(struct ref_formatting_stack **stack)\n@@ -1090,6 +1088,7 @@ static void populate_value(struct ref_array_item *ref)\n \t\tstruct branch *branch = NULL;\n \n \t\tv->handler = append_atom;\n+\t\tv->atom = atom;\n \n \t\tif (*name == '*') {\n \t\t\tderef = 1;\n@@ -1154,7 +1153,6 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\t\tv->s = \" \";\n \t\t\tcontinue;\n \t\t} else if (starts_with(name, \"align\")) {\n-\t\t\tv->u.align = atom->u.align;\n \t\t\tv->handler = align_atom_handler;\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"end\")) {\n-- \n2.10.2\n\n"},{"id":"308372","messageId":"20161227162357.28212-6-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 05/20] ref-filter: move get_head_description() from branch.c","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:42Z","receivedAt":"2016-12-27T16:23:36Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nMove the implementation of get_head_description() from branch.c to\nref-filter.  This gives a description of the HEAD ref if called. This\nis used as the refname for the HEAD ref whenever the\nFILTER_REFS_DETACHED_HEAD option is used. Make it public because we\nneed it to calculate the length of the HEAD refs description in\nbranch.c:calc_maxwidth() when we port branch.c to use ref-filter\nAPIs.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/branch.c | 33 ---------------------------------\n ref-filter.c     | 38 ++++++++++++++++++++++++++++++++++++--\n ref-filter.h     |  2 ++\n 3 files changed, 38 insertions(+), 35 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 9d30f55..6423ebc 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -364,39 +364,6 @@ static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,\n \tstrbuf_release(&subject);\n }\n \n-static char *get_head_description(void)\n-{\n-\tstruct strbuf desc = STRBUF_INIT;\n-\tstruct wt_status_state state;\n-\tmemset(&state, 0, sizeof(state));\n-\twt_status_get_state(&state, 1);\n-\tif (state.rebase_in_progress ||\n-\t    state.rebase_interactive_in_progress)\n-\t\tstrbuf_addf(&desc, _(\"(no branch, rebasing %s)\"),\n-\t\t\t    state.branch);\n-\telse if (state.bisect_in_progress)\n-\t\tstrbuf_addf(&desc, _(\"(no branch, bisect started on %s)\"),\n-\t\t\t    state.branch);\n-\telse if (state.detached_from) {\n-\t\tif (state.detached_at)\n-\t\t\t/* TRANSLATORS: make sure this matches\n-\t\t\t   \"HEAD detached at \" in wt-status.c */\n-\t\t\tstrbuf_addf(&desc, _(\"(HEAD detached at %s)\"),\n-\t\t\t\tstate.detached_from);\n-\t\telse\n-\t\t\t/* TRANSLATORS: make sure this matches\n-\t\t\t   \"HEAD detached from \" in wt-status.c */\n-\t\t\tstrbuf_addf(&desc, _(\"(HEAD detached from %s)\"),\n-\t\t\t\tstate.detached_from);\n-\t}\n-\telse\n-\t\tstrbuf_addstr(&desc, _(\"(no branch)\"));\n-\tfree(state.branch);\n-\tfree(state.onto);\n-\tfree(state.detached_from);\n-\treturn strbuf_detach(&desc, NULL);\n-}\n-\n static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,\n \t\t\t\t      struct ref_filter *filter, const char *remote_prefix)\n {\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 385fc04..5511a20 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -14,6 +14,7 @@\n #include \"git-compat-util.h\"\n #include \"version.h\"\n #include \"trailer.h\"\n+#include \"wt-status.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;\n@@ -1101,6 +1102,37 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t*s = refname;\n }\n \n+char *get_head_description(void)\n+{\n+\tstruct strbuf desc = STRBUF_INIT;\n+\tstruct wt_status_state state;\n+\tmemset(&state, 0, sizeof(state));\n+\twt_status_get_state(&state, 1);\n+\tif (state.rebase_in_progress ||\n+\t    state.rebase_interactive_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, rebasing %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.bisect_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, bisect started on %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.detached_from) {\n+\t\t/* TRANSLATORS: make sure these match _(\"HEAD detached at \")\n+\t\t   and _(\"HEAD detached from \") in wt-status.c */\n+\t\tif (state.detached_at)\n+\t\t\tstrbuf_addf(&desc, _(\"(HEAD detached at %s)\"),\n+\t\t\t\tstate.detached_from);\n+\t\telse\n+\t\t\tstrbuf_addf(&desc, _(\"(HEAD detached from %s)\"),\n+\t\t\t\tstate.detached_from);\n+\t}\n+\telse\n+\t\tstrbuf_addstr(&desc, _(\"(no branch)\"));\n+\tfree(state.branch);\n+\tfree(state.onto);\n+\tfree(state.detached_from);\n+\treturn strbuf_detach(&desc, NULL);\n+}\n+\n /*\n  * Parse the object referred by ref, and grab needed value.\n  */\n@@ -1140,9 +1172,11 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\tname++;\n \t\t}\n \n-\t\tif (starts_with(name, \"refname\"))\n+\t\tif (starts_with(name, \"refname\")) {\n \t\t\trefname = ref->refname;\n-\t\telse if (starts_with(name, \"symref\"))\n+\t\t\tif (ref->kind & FILTER_REFS_DETACHED_HEAD)\n+\t\t\t\trefname = get_head_description();\n+\t\t} else if (starts_with(name, \"symref\"))\n \t\t\trefname = ref->symref ? ref->symref : \"\";\n \t\telse if (starts_with(name, \"upstream\")) {\n \t\t\tconst char *branch_name;\ndiff --git a/ref-filter.h b/ref-filter.h\nindex fc55fa3..f78323d 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -108,5 +108,7 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);\n struct ref_sorting *ref_default_sorting(void);\n /*  Function to parse --merged and --no-merged options */\n int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);\n+/*  Get the current HEAD's description */\n+char *get_head_description(void);\n \n #endif /*  REF_FILTER_H  */\n-- \n2.10.2\n\n"},{"id":"308373","messageId":"20161227162357.28212-5-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 04/20] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:41Z","receivedAt":"2016-12-27T16:23:38Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nAdd support for %(objectname:short=<length>) which would print the\nabbreviated unique objectname of given length. When no length is\nspecified, the length is 'DEFAULT_ABBREV'. The minimum length is\n'MINIMUM_ABBREV'. The length may be exceeded to ensure that the\nprovided object name is unique.\n\nAdd tests and documentation for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nHelped-by: Jacob Keller <jacob.keller@gmail.com>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 +++\n ref-filter.c                       | 25 +++++++++++++++++++------\n t/t6300-for-each-ref.sh            | 10 ++++++++++\n 3 files changed, 32 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 39aab09..c2cc03c 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -110,6 +110,9 @@ objectsize::\n objectname::\n \tThe object name (aka SHA-1).\n \tFor a non-ambiguous abbreviation of the object name append `:short`.\n+\tFor an abbreviation of the object name with desired length append\n+\t`:short=<length>`, where the minimum length is MINIMUM_ABBREV. The\n+\tlength may be exceeded to ensure unique object names.\n \n upstream::\n \tThe name of a local ref which can be considered ``upstream''\ndiff --git a/ref-filter.c b/ref-filter.c\nindex e002629..385fc04 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -57,7 +57,10 @@ static struct used_atom {\n \t\t\tcmp_status cmp_status;\n \t\t\tconst char *str;\n \t\t} if_then_else;\n-\t\tenum { O_FULL, O_SHORT } objectname;\n+\t\tstruct {\n+\t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n+\t\t\tunsigned int length;\n+\t\t} objectname;\n \t} u;\n } *used_atom;\n static int used_atom_cnt, need_tagged, need_symref;\n@@ -129,10 +132,17 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)\n static void objectname_atom_parser(struct used_atom *atom, const char *arg)\n {\n \tif (!arg)\n-\t\tatom->u.objectname = O_FULL;\n+\t\tatom->u.objectname.option = O_FULL;\n \telse if (!strcmp(arg, \"short\"))\n-\t\tatom->u.objectname = O_SHORT;\n-\telse\n+\t\tatom->u.objectname.option = O_SHORT;\n+\telse if (skip_prefix(arg, \"short=\", &arg)) {\n+\t\tatom->u.objectname.option = O_LENGTH;\n+\t\tif (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n+\t\t    atom->u.objectname.length == 0)\n+\t\t\tdie(_(\"positive value expected objectname:short=%s\"), arg);\n+\t\tif (atom->u.objectname.length < MINIMUM_ABBREV)\n+\t\t\tatom->u.objectname.length = MINIMUM_ABBREV;\n+\t} else\n \t\tdie(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n }\n \n@@ -606,12 +616,15 @@ static int grab_objectname(const char *name, const unsigned char *sha1,\n \t\t\t   struct atom_value *v, struct used_atom *atom)\n {\n \tif (starts_with(name, \"objectname\")) {\n-\t\tif (atom->u.objectname == O_SHORT) {\n+\t\tif (atom->u.objectname.option == O_SHORT) {\n \t\t\tv->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));\n \t\t\treturn 1;\n-\t\t} else if (atom->u.objectname == O_FULL) {\n+\t\t} else if (atom->u.objectname.option == O_FULL) {\n \t\t\tv->s = xstrdup(sha1_to_hex(sha1));\n \t\t\treturn 1;\n+\t\t} else if (atom->u.objectname.option == O_LENGTH) {\n+\t\t\tv->s = xstrdup(find_unique_abbrev(sha1, atom->u.objectname.length));\n+\t\t\treturn 1;\n \t\t} else\n \t\t\tdie(\"BUG: unknown %%(objectname) option\");\n \t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex aea1dfc..e67c694 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -60,6 +60,8 @@ test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n test_atom head objectname:short $(git rev-parse --short refs/heads/master)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom head tree $(git rev-parse refs/heads/master^{tree})\n test_atom head parent ''\n test_atom head numparent 0\n@@ -99,6 +101,8 @@ test_atom tag objecttype tag\n test_atom tag objectsize 154\n test_atom tag objectname $(git rev-parse refs/tags/testtag)\n test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom tag tree ''\n test_atom tag parent ''\n test_atom tag numparent ''\n@@ -164,6 +168,12 @@ test_expect_success 'Check invalid format specifiers are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n '\n \n+test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n+\ttest_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n+'\n+\n test_date () {\n \tf=$1 &&\n \tcommitter_date=$2 &&\n-- \n2.10.2\n\n"},{"id":"308374","messageId":"20161227162357.28212-7-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 06/20] ref-filter: introduce format_ref_array_item()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:43Z","receivedAt":"2016-12-27T16:23:40Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nTo allow column display, we will need to first render the output in a\nstring list to allow print_columns() to compute the proper size of\neach column before starting the actual output. Introduce the function\nformat_ref_array_item() that does the formatting of a ref_array_item\nto an strbuf.\n\nshow_ref_array_item() is kept as a convenience wrapper around it which\nobtains the strbuf and prints it the standard output.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n ref-filter.c | 16 ++++++++++++----\n ref-filter.h |  3 +++\n 2 files changed, 15 insertions(+), 4 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 5511a20..47b521c 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1833,10 +1833,10 @@ static void append_literal(const char *cp, const char *ep, struct ref_formatting\n \t}\n }\n \n-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n+void format_ref_array_item(struct ref_array_item *info, const char *format,\n+\t\t\t   int quote_style, struct strbuf *final_buf)\n {\n \tconst char *cp, *sp, *ep;\n-\tstruct strbuf *final_buf;\n \tstruct ref_formatting_state state = REF_FORMATTING_STATE_INIT;\n \n \tstate.quote_style = quote_style;\n@@ -1866,9 +1866,17 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n \t}\n \tif (state.stack->prev)\n \t\tdie(_(\"format: %%(end) atom missing\"));\n-\tfinal_buf = &state.stack->output;\n-\tfwrite(final_buf->buf, 1, final_buf->len, stdout);\n+\tstrbuf_addbuf(final_buf, &state.stack->output);\n \tpop_stack_element(&state.stack);\n+}\n+\n+void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n+{\n+\tstruct strbuf final_buf = STRBUF_INIT;\n+\n+\tformat_ref_array_item(info, format, quote_style, &final_buf);\n+\tfwrite(final_buf.buf, 1, final_buf.len, stdout);\n+\tstrbuf_release(&final_buf);\n \tputchar('\\n');\n }\n \ndiff --git a/ref-filter.h b/ref-filter.h\nindex f78323d..630e7c2 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -100,6 +100,9 @@ int parse_ref_filter_atom(const char *atom, const char *ep);\n int verify_ref_format(const char *format);\n /*  Sort the given ref_array as per the ref_sorting provided */\n void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);\n+/*  Based on the given format and quote_style, fill the strbuf */\n+void format_ref_array_item(struct ref_array_item *info, const char *format,\n+\t\t\t   int quote_style, struct strbuf *final_buf);\n /*  Print the ref using the given format and quote_style */\n void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style);\n /*  Callback function for parsing the sort option */\n-- \n2.10.2\n\n"},{"id":"308375","messageId":"20161227162357.28212-8-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 07/20] ref-filter: make %(upstream:track) prints \"[gone]\" for invalid upstreams","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:44Z","receivedAt":"2016-12-27T16:23:43Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nBorrowing from branch.c's implementation print \"[gone]\" whenever an\nunknown upstream ref is encountered instead of just ignoring it.\n\nThis makes sure that when branch.c is ported over to using ref-filter\nAPIs for printing, this feature is not lost.\n\nMake changes to t/t6300-for-each-ref.sh and\nDocumentation/git-for-each-ref.txt to reflect this change.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nHelped-by : Jacob Keller <jacob.keller@gmail.com>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 3 ++-\n ref-filter.c                       | 4 +++-\n t/t6300-for-each-ref.sh            | 2 +-\n 3 files changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex c2cc03c..24a1679 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -121,7 +121,8 @@ upstream::\n \t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n \tversion: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n \tor \"=\" (in sync).  Has no effect if the ref does not have\n-\ttracking information associated with it.\n+\ttracking information associated with it. `:track` also prints\n+\t\"[gone]\" whenever unknown upstream ref is encountered.\n \n push::\n \tThe name of a local ref which represents the `@{push}` location\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 47b521c..9989918 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1073,8 +1073,10 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t*s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n \telse if (atom->u.remote_ref == RR_TRACK) {\n \t\tif (stat_tracking_info(branch, &num_ours,\n-\t\t\t\t       &num_theirs, NULL))\n+\t\t\t\t       &num_theirs, NULL)) {\n+\t\t\t*s = \"[gone]\";\n \t\t\treturn;\n+\t\t}\n \n \t\tif (!num_ours && !num_theirs)\n \t\t\t*s = \"\";\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex e67c694..a2e3f55 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -382,7 +382,7 @@ test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n \n test_expect_success 'Check that :track[short] works when upstream is invalid' '\n \tcat >expected <<-\\EOF &&\n-\n+\t[gone]\n \n \tEOF\n \ttest_when_finished \"git config branch.master.merge refs/heads/master\" &&\n-- \n2.10.2\n\n"},{"id":"308376","messageId":"20161227162357.28212-9-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 08/20] ref-filter: add support for %(upstream:track,nobracket)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:45Z","receivedAt":"2016-12-27T16:23:45Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nAdd support for %(upstream:track,nobracket) which will print the\ntracking information without the brackets (i.e. \"ahead N, behind M\").\nThis is needed when we port branch.c to use ref-filter's printing APIs.\n\nAdd test and documentation for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 10 ++++--\n ref-filter.c                       | 67 +++++++++++++++++++++++++-------------\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 53 insertions(+), 26 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 24a1679..b17273e 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -120,9 +120,13 @@ upstream::\n \t`refname` above.  Additionally respects `:track` to show\n \t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n \tversion: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n-\tor \"=\" (in sync).  Has no effect if the ref does not have\n-\ttracking information associated with it. `:track` also prints\n-\t\"[gone]\" whenever unknown upstream ref is encountered.\n+\tor \"=\" (in sync). `:track` also prints \"[gone]\" whenever\n+\tunknown upstream ref is encountered. Append `:track,nobracket`\n+\tto show tracking information without brackets (i.e \"ahead N,\n+\tbehind M\").  Has no effect if the ref does not have tracking\n+\tinformation associated with it.  All the options apart from\n+\t`nobracket` are mutually exclusive, but if used together the\n+\tlast option is selected.\n \n push::\n \tThe name of a local ref which represents the `@{push}` location\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 9989918..6de0927 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -48,8 +48,10 @@ static struct used_atom {\n \tunion {\n \t\tchar color[COLOR_MAXLEN];\n \t\tstruct align align;\n-\t\tenum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }\n-\t\t\tremote_ref;\n+\t\tstruct {\n+\t\t\tenum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option;\n+\t\t\tunsigned int nobracket : 1;\n+\t\t} remote_ref;\n \t\tstruct {\n \t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;\n \t\t\tunsigned int nlines;\n@@ -77,16 +79,33 @@ static void color_atom_parser(struct used_atom *atom, const char *color_value)\n \n static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n {\n-\tif (!arg)\n-\t\tatom->u.remote_ref = RR_NORMAL;\n-\telse if (!strcmp(arg, \"short\"))\n-\t\tatom->u.remote_ref = RR_SHORTEN;\n-\telse if (!strcmp(arg, \"track\"))\n-\t\tatom->u.remote_ref = RR_TRACK;\n-\telse if (!strcmp(arg, \"trackshort\"))\n-\t\tatom->u.remote_ref = RR_TRACKSHORT;\n-\telse\n-\t\tdie(_(\"unrecognized format: %%(%s)\"), atom->name);\n+\tstruct string_list params = STRING_LIST_INIT_DUP;\n+\tint i;\n+\n+\tif (!arg) {\n+\t\tatom->u.remote_ref.option = RR_NORMAL;\n+\t\treturn;\n+\t}\n+\n+\tatom->u.remote_ref.nobracket = 0;\n+\tstring_list_split(&params, arg, ',', -1);\n+\n+\tfor (i = 0; i < params.nr; i++) {\n+\t\tconst char *s = params.items[i].string;\n+\n+\t\tif (!strcmp(s, \"short\"))\n+\t\t\tatom->u.remote_ref.option = RR_SHORTEN;\n+\t\telse if (!strcmp(s, \"track\"))\n+\t\t\tatom->u.remote_ref.option = RR_TRACK;\n+\t\telse if (!strcmp(s, \"trackshort\"))\n+\t\t\tatom->u.remote_ref.option = RR_TRACKSHORT;\n+\t\telse if (!strcmp(s, \"nobracket\"))\n+\t\t\tatom->u.remote_ref.nobracket = 1;\n+\t\telse\n+\t\t\tdie(_(\"unrecognized format: %%(%s)\"), atom->name);\n+\t}\n+\n+\tstring_list_clear(&params, 0);\n }\n \n static void body_atom_parser(struct used_atom *atom, const char *arg)\n@@ -1069,25 +1088,27 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t\t\t    struct branch *branch, const char **s)\n {\n \tint num_ours, num_theirs;\n-\tif (atom->u.remote_ref == RR_SHORTEN)\n+\tif (atom->u.remote_ref.option == RR_SHORTEN)\n \t\t*s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n-\telse if (atom->u.remote_ref == RR_TRACK) {\n+\telse if (atom->u.remote_ref.option == RR_TRACK) {\n \t\tif (stat_tracking_info(branch, &num_ours,\n \t\t\t\t       &num_theirs, NULL)) {\n-\t\t\t*s = \"[gone]\";\n-\t\t\treturn;\n-\t\t}\n-\n-\t\tif (!num_ours && !num_theirs)\n+\t\t\t*s = xstrdup(\"gone\");\n+\t\t} else if (!num_ours && !num_theirs)\n \t\t\t*s = \"\";\n \t\telse if (!num_ours)\n-\t\t\t*s = xstrfmt(\"[behind %d]\", num_theirs);\n+\t\t\t*s = xstrfmt(\"behind %d\", num_theirs);\n \t\telse if (!num_theirs)\n-\t\t\t*s = xstrfmt(\"[ahead %d]\", num_ours);\n+\t\t\t*s = xstrfmt(\"ahead %d\", num_ours);\n \t\telse\n-\t\t\t*s = xstrfmt(\"[ahead %d, behind %d]\",\n+\t\t\t*s = xstrfmt(\"ahead %d, behind %d\",\n \t\t\t\t     num_ours, num_theirs);\n-\t} else if (atom->u.remote_ref == RR_TRACKSHORT) {\n+\t\tif (!atom->u.remote_ref.nobracket && *s[0]) {\n+\t\t\tconst char *to_free = *s;\n+\t\t\t*s = xstrfmt(\"[%s]\", *s);\n+\t\t\tfree((void *)to_free);\n+\t\t}\n+\t} else if (atom->u.remote_ref.option == RR_TRACKSHORT) {\n \t\tif (stat_tracking_info(branch, &num_ours,\n \t\t\t\t       &num_theirs, NULL))\n \t\t\treturn;\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex a2e3f55..af76dc5 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -372,6 +372,8 @@ test_expect_success 'setup for upstream:track[short]' '\n \n test_atom head upstream:track '[ahead 1]'\n test_atom head upstream:trackshort '>'\n+test_atom head upstream:track,nobracket 'ahead 1'\n+test_atom head upstream:nobracket,track 'ahead 1'\n test_atom head push:track '[ahead 1]'\n test_atom head push:trackshort '>'\n \n-- \n2.10.2\n\n"},{"id":"308377","messageId":"20161227162357.28212-11-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 10/20] ref-filter: introduce refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:47Z","receivedAt":"2016-12-27T16:23:48Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nSince there are multiple atoms which print refs ('%(refname)',\n'%(symref)', '%(push)', '%(upstream)'), it makes sense to have a common\nground for parsing them. This would allow us to share implementations of\nthe atom modifiers between these atoms.\n\nIntroduce refname_atom_parser_internal() to act as a common parsing\nfunction for ref printing atoms. This would eventually be used to\nintroduce refname_atom_parser() and symref_atom_parser() and also be\ninternally used in remote_ref_atom_parser().\n\nHelped-by: Jeff King <peff@peff.net>\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n ref-filter.c | 21 +++++++++++++++++++++\n 1 file changed, 21 insertions(+)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex e98ef4b..c1ebc40 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -32,6 +32,11 @@ struct if_then_else {\n \t\tcondition_satisfied : 1;\n };\n \n+struct refname_atom {\n+\tenum { R_NORMAL, R_SHORT, R_STRIP } option;\n+\tunsigned int strip;\n+};\n+\n /*\n  * An atom is a valid field atom listed below, possibly prefixed with\n  * a \"*\" to denote deref_tag().\n@@ -64,6 +69,7 @@ static struct used_atom {\n \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n \t\t\tunsigned int length;\n \t\t} objectname;\n+\t\tstruct refname_atom refname;\n \t} u;\n } *used_atom;\n static int used_atom_cnt, need_tagged, need_symref;\n@@ -77,6 +83,21 @@ static void color_atom_parser(struct used_atom *atom, const char *color_value)\n \t\tdie(_(\"unrecognized color: %%(color:%s)\"), color_value);\n }\n \n+static void refname_atom_parser_internal(struct refname_atom *atom,\n+\t\t\t\t\t const char *arg, const char *name)\n+{\n+\tif (!arg)\n+\t\tatom->option = R_NORMAL;\n+\telse if (!strcmp(arg, \"short\"))\n+\t\tatom->option = R_SHORT;\n+\telse if (skip_prefix(arg, \"strip=\", &arg)) {\n+\t\tatom->option = R_STRIP;\n+\t\tif (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0)\n+\t\t\tdie(_(\"positive value expected refname:strip=%s\"), arg);\n+\t} else\n+\t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n+}\n+\n static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n {\n \tstruct string_list params = STRING_LIST_INIT_DUP;\n-- \n2.10.2\n\n"},{"id":"308378","messageId":"20161227162357.28212-10-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 09/20] ref-filter: make \"%(symref)\" atom work with the ':short' modifier","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:46Z","receivedAt":"2016-12-27T16:23:52Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nThe \"%(symref)\" atom doesn't work when used with the ':short' modifier\nbecause we strictly match only 'symref' for setting the 'need_symref'\nindicator. Fix this by comparing with the valid_atom rather than the\nused_atom.\n\nAdd tests for %(symref) and %(symref:short) while we're here.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n ref-filter.c            |  2 +-\n t/t6300-for-each-ref.sh | 24 ++++++++++++++++++++++++\n 2 files changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 6de0927..e98ef4b 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -352,7 +352,7 @@ int parse_ref_filter_atom(const char *atom, const char *ep)\n \t\tvalid_atom[i].parser(&used_atom[at], arg);\n \tif (*atom == '*')\n \t\tneed_tagged = 1;\n-\tif (!strcmp(used_atom[at].name, \"symref\"))\n+\tif (!strcmp(valid_atom[i].name, \"symref\"))\n \t\tneed_symref = 1;\n \treturn at;\n }\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex af76dc5..7663a36 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -38,6 +38,7 @@ test_atom() {\n \tcase \"$1\" in\n \t\thead) ref=refs/heads/master ;;\n \t\t tag) ref=refs/tags/testtag ;;\n+\t\t sym) ref=refs/heads/sym ;;\n \t\t   *) ref=$1 ;;\n \tesac\n \tprintf '%s\\n' \"$3\" >expected\n@@ -566,6 +567,7 @@ test_expect_success 'Verify sort with multiple keys' '\n \ttest_cmp expected actual\n '\n \n+\n test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n \ttest_when_finished \"git checkout master\" &&\n \tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n@@ -600,4 +602,26 @@ test_expect_success 'basic atom: head contents:trailers' '\n \ttest_cmp expect actual.clean\n '\n \n+test_expect_success 'Add symbolic ref for the following tests' '\n+\tgit symbolic-ref refs/heads/sym refs/heads/master\n+'\n+\n+cat >expected <<EOF\n+refs/heads/master\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref) atom' '\n+\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+heads/master\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:short) atom' '\n+\tgit for-each-ref --format=\"%(symref:short)\" refs/heads/sym >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"308379","messageId":"20161227162357.28212-13-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 12/20] ref-filter: make remote_ref_atom_parser() use refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:49Z","receivedAt":"2016-12-27T16:23:56Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nUse the recently introduced refname_atom_parser_internal() within\nremote_ref_atom_parser(), this provides a common base for all the ref\nprinting atoms, allowing %(upstream) and %(push) to also use the\n':strip' option.\n\nThe atoms '%(push)' and '%(upstream)' will retain the ':track' and\n':trackshort' atom modifiers to themselves as they have no meaning in\ncontext to the '%(refname)' and '%(symref)' atoms.\n\nUpdate the documentation and tests to reflect the same.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 31 ++++++++++++++++---------------\n ref-filter.c                       | 26 +++++++++++++++-----------\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 33 insertions(+), 26 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 0fa5818..e7cdff6 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -116,23 +116,24 @@ objectname::\n \n upstream::\n \tThe name of a local ref which can be considered ``upstream''\n-\tfrom the displayed ref. Respects `:short` in the same way as\n-\t`refname` above.  Additionally respects `:track` to show\n-\t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n-\tversion: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n-\tor \"=\" (in sync). `:track` also prints \"[gone]\" whenever\n-\tunknown upstream ref is encountered. Append `:track,nobracket`\n-\tto show tracking information without brackets (i.e \"ahead N,\n-\tbehind M\").  Has no effect if the ref does not have tracking\n-\tinformation associated with it.  All the options apart from\n-\t`nobracket` are mutually exclusive, but if used together the\n-\tlast option is selected.\n+\tfrom the displayed ref. Respects `:short` and `:strip` in the\n+\tsame way as `refname` above.  Additionally respects `:track`\n+\tto show \"[ahead N, behind M]\" and `:trackshort` to show the\n+\tterse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n+\tbehind), or \"=\" (in sync). `:track` also prints \"[gone]\"\n+\twhenever unknown upstream ref is encountered. Append\n+\t`:track,nobracket` to show tracking information without\n+\tbrackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n+\tdoes not have tracking information associated with it.  All\n+\tthe options apart from `nobracket` are mutually exclusive, but\n+\tif used together the last option is selected.\n \n push::\n-\tThe name of a local ref which represents the `@{push}` location\n-\tfor the displayed ref. Respects `:short`, `:track`, and\n-\t`:trackshort` options as `upstream` does. Produces an empty\n-\tstring if no `@{push}` ref is configured.\n+\tThe name of a local ref which represents the `@{push}`\n+\tlocation for the displayed ref. Respects `:short`, `:strip`,\n+\t`:track`, and `:trackshort` options as `upstream`\n+\tdoes. Produces an empty string if no `@{push}` ref is\n+\tconfigured.\n \n HEAD::\n \t'*' if HEAD matches current ref (the checked out branch), ' '\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7bfd656..9140539 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -54,7 +54,8 @@ static struct used_atom {\n \t\tchar color[COLOR_MAXLEN];\n \t\tstruct align align;\n \t\tstruct {\n-\t\t\tenum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option;\n+\t\t\tenum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;\n+\t\t\tstruct refname_atom refname;\n \t\t\tunsigned int nobracket : 1;\n \t\t} remote_ref;\n \t\tstruct {\n@@ -104,7 +105,9 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n \tint i;\n \n \tif (!arg) {\n-\t\tatom->u.remote_ref.option = RR_NORMAL;\n+\t\tatom->u.remote_ref.option = RR_REF;\n+\t\trefname_atom_parser_internal(&atom->u.remote_ref.refname,\n+\t\t\t\t\t     arg, atom->name);\n \t\treturn;\n \t}\n \n@@ -114,16 +117,17 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n \tfor (i = 0; i < params.nr; i++) {\n \t\tconst char *s = params.items[i].string;\n \n-\t\tif (!strcmp(s, \"short\"))\n-\t\t\tatom->u.remote_ref.option = RR_SHORTEN;\n-\t\telse if (!strcmp(s, \"track\"))\n+\t\tif (!strcmp(s, \"track\"))\n \t\t\tatom->u.remote_ref.option = RR_TRACK;\n \t\telse if (!strcmp(s, \"trackshort\"))\n \t\t\tatom->u.remote_ref.option = RR_TRACKSHORT;\n \t\telse if (!strcmp(s, \"nobracket\"))\n \t\t\tatom->u.remote_ref.nobracket = 1;\n-\t\telse\n-\t\t\tdie(_(\"unrecognized format: %%(%s)\"), atom->name);\n+\t\telse {\n+\t\t\tatom->u.remote_ref.option = RR_REF;\n+\t\t\trefname_atom_parser_internal(&atom->u.remote_ref.refname,\n+\t\t\t\t\t\t     arg, atom->name);\n+\t\t}\n \t}\n \n \tstring_list_clear(&params, 0);\n@@ -1119,8 +1123,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t\t\t    struct branch *branch, const char **s)\n {\n \tint num_ours, num_theirs;\n-\tif (atom->u.remote_ref.option == RR_SHORTEN)\n-\t\t*s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n+\tif (atom->u.remote_ref.option == RR_REF)\n+\t\t*s = show_ref(&atom->u.remote_ref.refname, refname);\n \telse if (atom->u.remote_ref.option == RR_TRACK) {\n \t\tif (stat_tracking_info(branch, &num_ours,\n \t\t\t\t       &num_theirs, NULL)) {\n@@ -1152,8 +1156,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t\t*s = \">\";\n \t\telse\n \t\t\t*s = \"<>\";\n-\t} else /* RR_NORMAL */\n-\t\t*s = refname;\n+\t} else\n+\t\tdie(\"BUG: unhandled RR_* enum\");\n }\n \n char *get_head_description(void)\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 18a9e25..c538084 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -55,8 +55,10 @@ test_atom head refname:strip=1 heads/master\n test_atom head refname:strip=2 master\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n+test_atom head upstream:strip=2 origin/master\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n+test_atom head push:strip=1 remotes/myfork/master\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n-- \n2.10.2\n\n"},{"id":"308380","messageId":"20161227162357.28212-12-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 11/20] ref-filter: introduce refname_atom_parser()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:48Z","receivedAt":"2016-12-27T16:23:59Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nUsing refname_atom_parser_internal(), introduce refname_atom_parser()\nwhich will parse the %(symref) and %(refname) atoms. Store the parsed\ninformation into the 'used_atom' structure based on the modifiers used\nalong with the atoms.\n\nNow the '%(symref)' atom supports the ':strip' atom modifier. Update the\nDocumentation and tests to reflect this.\n\nHelped-by: Jeff King <peff@peff.net>\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  5 +++\n ref-filter.c                       | 73 +++++++++++++++++++++-----------------\n t/t6300-for-each-ref.sh            |  9 +++++\n 3 files changed, 54 insertions(+), 33 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex b17273e..0fa5818 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -170,6 +170,11 @@ if::\n \tthe value between the %(if:...) and %(then) atoms with the\n \tgiven string.\n \n+symref::\n+\tThe ref which the given symbolic ref refers to. If not a\n+\tsymbolic ref, nothing is printed. Respects the `:short` and\n+\t`:strip` options in the same way as `refname` above.\n+\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\ndiff --git a/ref-filter.c b/ref-filter.c\nindex c1ebc40..7bfd656 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -187,6 +187,11 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg)\n \t\tdie(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n }\n \n+static void refname_atom_parser(struct used_atom *atom, const char *arg)\n+{\n+\treturn refname_atom_parser_internal(&atom->u.refname, arg, atom->name);\n+}\n+\n static align_type parse_align_position(const char *s)\n {\n \tif (!strcmp(s, \"right\"))\n@@ -257,7 +262,7 @@ static struct {\n \tcmp_type cmp_type;\n \tvoid (*parser)(struct used_atom *atom, const char *arg);\n } valid_atom[] = {\n-\t{ \"refname\" },\n+\t{ \"refname\" , FIELD_STR, refname_atom_parser },\n \t{ \"objecttype\" },\n \t{ \"objectsize\", FIELD_ULONG },\n \t{ \"objectname\", FIELD_STR, objectname_atom_parser },\n@@ -287,7 +292,7 @@ static struct {\n \t{ \"contents\", FIELD_STR, contents_atom_parser },\n \t{ \"upstream\", FIELD_STR, remote_ref_atom_parser },\n \t{ \"push\", FIELD_STR, remote_ref_atom_parser },\n-\t{ \"symref\" },\n+\t{ \"symref\", FIELD_STR, refname_atom_parser },\n \t{ \"flag\" },\n \t{ \"HEAD\" },\n \t{ \"color\", FIELD_STR, color_atom_parser },\n@@ -1082,21 +1087,16 @@ static inline char *copy_advance(char *dst, const char *src)\n \treturn dst;\n }\n \n-static const char *strip_ref_components(const char *refname, const char *nr_arg)\n+static const char *strip_ref_components(const char *refname, unsigned int len)\n {\n-\tchar *end;\n-\tlong nr = strtol(nr_arg, &end, 10);\n-\tlong remaining = nr;\n+\tlong remaining = len;\n \tconst char *start = refname;\n \n-\tif (nr < 1 || *end != '\\0')\n-\t\tdie(_(\":strip= requires a positive integer argument\"));\n-\n \twhile (remaining) {\n \t\tswitch (*start++) {\n \t\tcase '\\0':\n-\t\t\tdie(_(\"ref '%s' does not have %ld components to :strip\"),\n-\t\t\t    refname, nr);\n+\t\t\tdie(_(\"ref '%s' does not have %ud components to :strip\"),\n+\t\t\t    refname, len);\n \t\tcase '/':\n \t\t\tremaining--;\n \t\t\tbreak;\n@@ -1105,6 +1105,16 @@ static const char *strip_ref_components(const char *refname, const char *nr_arg)\n \treturn start;\n }\n \n+static const char *show_ref(struct refname_atom *atom, const char *refname)\n+{\n+\tif (atom->option == R_SHORT)\n+\t\treturn shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n+\telse if (atom->option == R_STRIP)\n+\t\treturn strip_ref_components(refname, atom->strip);\n+\telse\n+\t\treturn refname;\n+}\n+\n static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t\t\t    struct branch *branch, const char **s)\n {\n@@ -1177,6 +1187,21 @@ char *get_head_description(void)\n \treturn strbuf_detach(&desc, NULL);\n }\n \n+static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref)\n+{\n+\tif (!ref->symref)\n+\t\treturn \"\";\n+\telse\n+\t\treturn show_ref(&atom->u.refname, ref->symref);\n+}\n+\n+static const char *get_refname(struct used_atom *atom, struct ref_array_item *ref)\n+{\n+\tif (ref->kind & FILTER_REFS_DETACHED_HEAD)\n+\t\treturn get_head_description();\n+\treturn show_ref(&atom->u.refname, ref->refname);\n+}\n+\n /*\n  * Parse the object referred by ref, and grab needed value.\n  */\n@@ -1205,7 +1230,6 @@ static void populate_value(struct ref_array_item *ref)\n \t\tstruct atom_value *v = &ref->value[i];\n \t\tint deref = 0;\n \t\tconst char *refname;\n-\t\tconst char *formatp;\n \t\tstruct branch *branch = NULL;\n \n \t\tv->handler = append_atom;\n@@ -1216,12 +1240,10 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\tname++;\n \t\t}\n \n-\t\tif (starts_with(name, \"refname\")) {\n-\t\t\trefname = ref->refname;\n-\t\t\tif (ref->kind & FILTER_REFS_DETACHED_HEAD)\n-\t\t\t\trefname = get_head_description();\n-\t\t} else if (starts_with(name, \"symref\"))\n-\t\t\trefname = ref->symref ? ref->symref : \"\";\n+\t\tif (starts_with(name, \"refname\"))\n+\t\t\trefname = get_refname(atom, ref);\n+\t\telse if (starts_with(name, \"symref\"))\n+\t\t\trefname = get_symref(atom, ref);\n \t\telse if (starts_with(name, \"upstream\")) {\n \t\t\tconst char *branch_name;\n \t\t\t/* only local branches may have an upstream */\n@@ -1297,21 +1319,6 @@ static void populate_value(struct ref_array_item *ref)\n \t\t} else\n \t\t\tcontinue;\n \n-\t\tformatp = strchr(name, ':');\n-\t\tif (formatp) {\n-\t\t\tconst char *arg;\n-\n-\t\t\tformatp++;\n-\t\t\tif (!strcmp(formatp, \"short\"))\n-\t\t\t\trefname = shorten_unambiguous_ref(refname,\n-\t\t\t\t\t\t      warn_ambiguous_refs);\n-\t\t\telse if (skip_prefix(formatp, \"strip=\", &arg))\n-\t\t\t\trefname = strip_ref_components(refname, arg);\n-\t\t\telse\n-\t\t\t\tdie(_(\"unknown %.*s format %s\"),\n-\t\t\t\t    (int)(formatp - name), name, formatp);\n-\t\t}\n-\n \t\tif (!deref)\n \t\t\tv->s = refname;\n \t\telse\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 7663a36..18a9e25 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -624,4 +624,13 @@ test_expect_success 'Verify usage of %(symref:short) atom' '\n \ttest_cmp expected actual\n '\n \n+cat >expected <<EOF\n+master\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:strip) atom' '\n+\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"308381","messageId":"20161227162357.28212-15-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 14/20] ref-filter: Do not abruptly die when using the 'lstrip=<N>' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:51Z","receivedAt":"2016-12-27T16:24:02Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Currently when we use the 'lstrip=<N>' option, if 'N' is greater than\nthe number of components available in the refname, we abruptly end\nprogram execution by calling die().\n\nThis behavior is undesired since a single refname with few components\ncould end program execution. To avoid this, return an empty string\nwhenever the value 'N' is greater than the number of components\navailable, instead of calling die().\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 3 +--\n ref-filter.c                       | 3 +--\n t/t6300-for-each-ref.sh            | 4 ----\n 3 files changed, 2 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 46b4583..82c202a 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -98,8 +98,7 @@ refname::\n \tabbreviation mode. If `lstrip=<N>` is appended, strips `<N>`\n \tslash-separated path components from the front of the refname\n \t(e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n-\t`<N>` must be a positive integer.  If a displayed ref has fewer\n-\tcomponents than `<N>`, the command aborts with an error.\n+\t`<N>` must be a positive integer.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\ndiff --git a/ref-filter.c b/ref-filter.c\nindex e0015c6..76553eb 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1099,8 +1099,7 @@ static const char *lstrip_ref_components(const char *refname, unsigned int len)\n \twhile (remaining) {\n \t\tswitch (*start++) {\n \t\tcase '\\0':\n-\t\t\tdie(_(\"ref '%s' does not have %ud components to :lstrip\"),\n-\t\t\t    refname, len);\n+\t\t\treturn \"\";\n \t\tcase '/':\n \t\t\tremaining--;\n \t\t\tbreak;\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 5eb013c..d3d1a97 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -147,10 +147,6 @@ test_expect_success 'arguments to :lstrip must be positive integers' '\n \ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=foo)\"\n '\n \n-test_expect_success 'stripping refnames too far gives an error' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=3)\"\n-'\n-\n test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n \tgit for-each-ref --format=\"%(authordate)\" refs/heads &&\n \tgit for-each-ref --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n-- \n2.10.2\n\n"},{"id":"308382","messageId":"20161227162357.28212-14-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 13/20] ref-filter: rename the 'strip' option to 'lstrip'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:50Z","receivedAt":"2016-12-27T16:24:04Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"In preparation for the upcoming patch, where we introduce the 'rstrip'\noption. Rename the 'strip' option to 'lstrip' to remove ambiguity.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 10 +++++-----\n builtin/tag.c                      |  4 ++--\n ref-filter.c                       | 20 ++++++++++----------\n t/t6300-for-each-ref.sh            | 22 +++++++++++-----------\n 4 files changed, 28 insertions(+), 28 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex e7cdff6..46b4583 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -95,9 +95,9 @@ 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 \tThe option core.warnAmbiguousRefs is used to select the strict\n-\tabbreviation mode. If `strip=<N>` is appended, strips `<N>`\n+\tabbreviation mode. If `lstrip=<N>` is appended, strips `<N>`\n \tslash-separated path components from the front of the refname\n-\t(e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.\n+\t(e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n \t`<N>` must be a positive integer.  If a displayed ref has fewer\n \tcomponents than `<N>`, the command aborts with an error.\n \n@@ -116,7 +116,7 @@ objectname::\n \n upstream::\n \tThe name of a local ref which can be considered ``upstream''\n-\tfrom the displayed ref. Respects `:short` and `:strip` in the\n+\tfrom the displayed ref. Respects `:short` and `:lstrip` in the\n \tsame way as `refname` above.  Additionally respects `:track`\n \tto show \"[ahead N, behind M]\" and `:trackshort` to show the\n \tterse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n@@ -130,7 +130,7 @@ upstream::\n \n push::\n \tThe name of a local ref which represents the `@{push}`\n-\tlocation for the displayed ref. Respects `:short`, `:strip`,\n+\tlocation for the displayed ref. Respects `:short`, `:lstrip`,\n \t`:track`, and `:trackshort` options as `upstream`\n \tdoes. Produces an empty string if no `@{push}` ref is\n \tconfigured.\n@@ -174,7 +174,7 @@ if::\n symref::\n \tThe ref which the given symbolic ref refers to. If not a\n \tsymbolic ref, nothing is printed. Respects the `:short` and\n-\t`:strip` options in the same way as `refname` above.\n+\t`:lstrip` options in the same way as `refname` above.\n \n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 73df728..b4789ce 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -45,11 +45,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con\n \tif (!format) {\n \t\tif (filter->lines) {\n \t\t\tto_free = xstrfmt(\"%s %%(contents:lines=%d)\",\n-\t\t\t\t\t  \"%(align:15)%(refname:strip=2)%(end)\",\n+\t\t\t\t\t  \"%(align:15)%(refname:lstrip=2)%(end)\",\n \t\t\t\t\t  filter->lines);\n \t\t\tformat = to_free;\n \t\t} else\n-\t\t\tformat = \"%(refname:strip=2)\";\n+\t\t\tformat = \"%(refname:lstrip=2)\";\n \t}\n \n \tverify_ref_format(format);\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 9140539..e0015c6 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -33,8 +33,8 @@ struct if_then_else {\n };\n \n struct refname_atom {\n-\tenum { R_NORMAL, R_SHORT, R_STRIP } option;\n-\tunsigned int strip;\n+\tenum { R_NORMAL, R_SHORT, R_LSTRIP } option;\n+\tunsigned int lstrip;\n };\n \n /*\n@@ -91,10 +91,10 @@ static void refname_atom_parser_internal(struct refname_atom *atom,\n \t\tatom->option = R_NORMAL;\n \telse if (!strcmp(arg, \"short\"))\n \t\tatom->option = R_SHORT;\n-\telse if (skip_prefix(arg, \"strip=\", &arg)) {\n-\t\tatom->option = R_STRIP;\n-\t\tif (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0)\n-\t\t\tdie(_(\"positive value expected refname:strip=%s\"), arg);\n+\telse if (skip_prefix(arg, \"lstrip=\", &arg)) {\n+\t\tatom->option = R_LSTRIP;\n+\t\tif (strtoul_ui(arg, 10, &atom->lstrip) || atom->lstrip <= 0)\n+\t\t\tdie(_(\"positive value expected refname:lstrip=%s\"), arg);\n \t} else\n \t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n }\n@@ -1091,7 +1091,7 @@ static inline char *copy_advance(char *dst, const char *src)\n \treturn dst;\n }\n \n-static const char *strip_ref_components(const char *refname, unsigned int len)\n+static const char *lstrip_ref_components(const char *refname, unsigned int len)\n {\n \tlong remaining = len;\n \tconst char *start = refname;\n@@ -1099,7 +1099,7 @@ static const char *strip_ref_components(const char *refname, unsigned int len)\n \twhile (remaining) {\n \t\tswitch (*start++) {\n \t\tcase '\\0':\n-\t\t\tdie(_(\"ref '%s' does not have %ud components to :strip\"),\n+\t\t\tdie(_(\"ref '%s' does not have %ud components to :lstrip\"),\n \t\t\t    refname, len);\n \t\tcase '/':\n \t\t\tremaining--;\n@@ -1113,8 +1113,8 @@ static const char *show_ref(struct refname_atom *atom, const char *refname)\n {\n \tif (atom->option == R_SHORT)\n \t\treturn shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n-\telse if (atom->option == R_STRIP)\n-\t\treturn strip_ref_components(refname, atom->strip);\n+\telse if (atom->option == R_LSTRIP)\n+\t\treturn lstrip_ref_components(refname, atom->lstrip);\n \telse\n \t\treturn refname;\n }\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex c538084..5eb013c 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -51,14 +51,14 @@ test_atom() {\n \n test_atom head refname refs/heads/master\n test_atom head refname:short master\n-test_atom head refname:strip=1 heads/master\n-test_atom head refname:strip=2 master\n+test_atom head refname:lstrip=1 heads/master\n+test_atom head refname:lstrip=2 master\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n-test_atom head upstream:strip=2 origin/master\n+test_atom head upstream:lstrip=2 origin/master\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n-test_atom head push:strip=1 remotes/myfork/master\n+test_atom head push:lstrip=1 remotes/myfork/master\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n@@ -141,14 +141,14 @@ test_expect_success 'Check invalid atoms names are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n '\n \n-test_expect_success 'arguments to :strip must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=foo)\"\n+test_expect_success 'arguments to :lstrip must be positive integers' '\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=0)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=-1)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=foo)\"\n '\n \n test_expect_success 'stripping refnames too far gives an error' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=3)\"\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=3)\"\n '\n \n test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n@@ -630,8 +630,8 @@ cat >expected <<EOF\n master\n EOF\n \n-test_expect_success 'Verify usage of %(symref:strip) atom' '\n-\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n+\tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n \ttest_cmp expected actual\n '\n \n-- \n2.10.2\n\n"},{"id":"308383","messageId":"20161227162357.28212-16-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 15/20] ref-filter: modify the 'lstrip=<N>' option to work with negative '<N>'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:52Z","receivedAt":"2016-12-27T16:24:07Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Currently the 'lstrip=<N>' option only takes a positive value '<N>'\nand strips '<N>' slash-separated path components from the left. Modify\nthe 'lstrip' option to also take a negative number '<N>' which would\nonly _leave_ behind 'N' slash-separated path components from the left.\n\nAdd documentation and tests for the same.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 ++-\n ref-filter.c                       | 27 ++++++++++++++++++++++-----\n t/t6300-for-each-ref.sh            | 12 ++++++------\n 3 files changed, 30 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 82c202a..9055ba0 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -98,7 +98,8 @@ refname::\n \tabbreviation mode. If `lstrip=<N>` is appended, strips `<N>`\n \tslash-separated path components from the front of the refname\n \t(e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n-\t`<N>` must be a positive integer.\n+\tif `<N>` is a negative number, then only `<N>` path components\n+\tare left behind.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 76553eb..1cd92ea 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -34,7 +34,7 @@ struct if_then_else {\n \n struct refname_atom {\n \tenum { R_NORMAL, R_SHORT, R_LSTRIP } option;\n-\tunsigned int lstrip;\n+\tint lstrip;\n };\n \n /*\n@@ -93,8 +93,8 @@ static void refname_atom_parser_internal(struct refname_atom *atom,\n \t\tatom->option = R_SHORT;\n \telse if (skip_prefix(arg, \"lstrip=\", &arg)) {\n \t\tatom->option = R_LSTRIP;\n-\t\tif (strtoul_ui(arg, 10, &atom->lstrip) || atom->lstrip <= 0)\n-\t\t\tdie(_(\"positive value expected refname:lstrip=%s\"), arg);\n+\t\tif (strtol_i(arg, 10, &atom->lstrip))\n+\t\t\tdie(_(\"Integer value expected refname:lstrip=%s\"), arg);\n \t} else\n \t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n }\n@@ -1091,12 +1091,28 @@ static inline char *copy_advance(char *dst, const char *src)\n \treturn dst;\n }\n \n-static const char *lstrip_ref_components(const char *refname, unsigned int len)\n+static const char *lstrip_ref_components(const char *refname, int len)\n {\n \tlong remaining = len;\n \tconst char *start = refname;\n \n-\twhile (remaining) {\n+\tif (len < 0) {\n+\t\tint i;\n+\t\tconst char *p = refname;\n+\n+\t\t/* Find total no of '/' separated path-components */\n+\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++)\n+\t\t\t;\n+\t\t/*\n+\t\t * The number of components we need to strip is now\n+\t\t * the total minus the components to be left (Plus one\n+\t\t * because we count the number of '/', but the number\n+\t\t * of components is one more than the no of '/').\n+\t\t */\n+\t\tremaining = i + len + 1;\n+\t}\n+\n+\twhile (remaining > 0) {\n \t\tswitch (*start++) {\n \t\tcase '\\0':\n \t\t\treturn \"\";\n@@ -1105,6 +1121,7 @@ static const char *lstrip_ref_components(const char *refname, unsigned int len)\n \t\t\tbreak;\n \t\t}\n \t}\n+\n \treturn start;\n }\n \ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex d3d1a97..203cfaa 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -53,12 +53,16 @@ test_atom head refname refs/heads/master\n test_atom head refname:short master\n test_atom head refname:lstrip=1 heads/master\n test_atom head refname:lstrip=2 master\n+test_atom head refname:lstrip=-1 master\n+test_atom head refname:lstrip=-2 heads/master\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n test_atom head upstream:lstrip=2 origin/master\n+test_atom head upstream:lstrip=-2 origin/master\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n test_atom head push:lstrip=1 remotes/myfork/master\n+test_atom head push:lstrip=-1 master\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n@@ -141,12 +145,6 @@ test_expect_success 'Check invalid atoms names are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n '\n \n-test_expect_success 'arguments to :lstrip must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=foo)\"\n-'\n-\n test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n \tgit for-each-ref --format=\"%(authordate)\" refs/heads &&\n \tgit for-each-ref --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n@@ -624,10 +622,12 @@ test_expect_success 'Verify usage of %(symref:short) atom' '\n \n cat >expected <<EOF\n master\n+heads/master\n EOF\n \n test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n \tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n+\tgit for-each-ref --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n \ttest_cmp expected actual\n '\n \n-- \n2.10.2\n\n"},{"id":"308384","messageId":"20161227162357.28212-17-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 16/20] ref-filter: add an 'rstrip=<N>' option to atoms which deal with refnames","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:53Z","receivedAt":"2016-12-27T16:24:10Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Complimenting the existing 'lstrip=<N>' option, add an 'rstrip=<N>'\noption which strips `<N>` slash-separated path components from the end\nof the refname (e.g., `%(refname:rstrip=2)` turns `refs/tags/foo` into\n`refs`).\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 40 ++++++++++++++++++++-----------------\n ref-filter.c                       | 41 ++++++++++++++++++++++++++++++++++++--\n t/t6300-for-each-ref.sh            | 19 ++++++++++++++++++\n 3 files changed, 80 insertions(+), 20 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 9055ba0..81db67d 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -95,10 +95,12 @@ 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 \tThe option core.warnAmbiguousRefs is used to select the strict\n-\tabbreviation mode. If `lstrip=<N>` is appended, strips `<N>`\n-\tslash-separated path components from the front of the refname\n-\t(e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n-\tif `<N>` is a negative number, then only `<N>` path components\n+\tabbreviation mode. The `lstrip=<N>` or `rstrip=<N>` option can\n+\tbe appended to strip `<N>` slash-separated path components\n+\tfrom the left or right of the refname respectively (e.g.,\n+\t`%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and\n+\t`%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).  if\n+\t`<N>` is a negative number, then only `<N>` path components\n \tare left behind.\n \n objecttype::\n@@ -116,22 +118,23 @@ objectname::\n \n upstream::\n \tThe name of a local ref which can be considered ``upstream''\n-\tfrom the displayed ref. Respects `:short` and `:lstrip` in the\n-\tsame way as `refname` above.  Additionally respects `:track`\n-\tto show \"[ahead N, behind M]\" and `:trackshort` to show the\n-\tterse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n-\tbehind), or \"=\" (in sync). `:track` also prints \"[gone]\"\n-\twhenever unknown upstream ref is encountered. Append\n-\t`:track,nobracket` to show tracking information without\n-\tbrackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n-\tdoes not have tracking information associated with it.  All\n-\tthe options apart from `nobracket` are mutually exclusive, but\n-\tif used together the last option is selected.\n+\tfrom the displayed ref. Respects `:short`, `:lstrip` and\n+\t`:rstrip` in the same way as `refname` above.  Additionally\n+\trespects `:track` to show \"[ahead N, behind M]\" and\n+\t`:trackshort` to show the terse version: \">\" (ahead), \"<\"\n+\t(behind), \"<>\" (ahead and behind), or \"=\" (in sync). `:track`\n+\talso prints \"[gone]\" whenever unknown upstream ref is\n+\tencountered. Append `:track,nobracket` to show tracking\n+\tinformation without brackets (i.e \"ahead N, behind M\").  Has\n+\tno effect if the ref does not have tracking information\n+\tassociated with it.  All the options apart from `nobracket`\n+\tare mutually exclusive, but if used together the last option\n+\tis selected.\n \n push::\n \tThe name of a local ref which represents the `@{push}`\n \tlocation for the displayed ref. Respects `:short`, `:lstrip`,\n-\t`:track`, and `:trackshort` options as `upstream`\n+\t`:rstrip`, `:track`, and `:trackshort` options as `upstream`\n \tdoes. Produces an empty string if no `@{push}` ref is\n \tconfigured.\n \n@@ -173,8 +176,9 @@ if::\n \n symref::\n \tThe ref which the given symbolic ref refers to. If not a\n-\tsymbolic ref, nothing is printed. Respects the `:short` and\n-\t`:lstrip` options in the same way as `refname` above.\n+\tsymbolic ref, nothing is printed. Respects the `:short`,\n+\t`:lstrip` and `:rstrip` options in the same way as `refname`\n+\tabove.\n \n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 1cd92ea..dd7e751 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -33,8 +33,8 @@ struct if_then_else {\n };\n \n struct refname_atom {\n-\tenum { R_NORMAL, R_SHORT, R_LSTRIP } option;\n-\tint lstrip;\n+\tenum { R_NORMAL, R_SHORT, R_LSTRIP, R_RSTRIP } option;\n+\tint lstrip, rstrip;\n };\n \n /*\n@@ -95,6 +95,10 @@ static void refname_atom_parser_internal(struct refname_atom *atom,\n \t\tatom->option = R_LSTRIP;\n \t\tif (strtol_i(arg, 10, &atom->lstrip))\n \t\t\tdie(_(\"Integer value expected refname:lstrip=%s\"), arg);\n+\t} else if (skip_prefix(arg, \"rstrip=\", &arg)) {\n+\t\tatom->option = R_RSTRIP;\n+\t\tif (strtol_i(arg, 10, &atom->rstrip))\n+\t\t\tdie(_(\"Integer value expected refname:rstrip=%s\"), arg);\n \t} else\n \t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n }\n@@ -1125,12 +1129,45 @@ static const char *lstrip_ref_components(const char *refname, int len)\n \treturn start;\n }\n \n+static const char *rstrip_ref_components(const char *refname, int len)\n+{\n+\tlong remaining = len;\n+\tchar *start = xstrdup(refname);\n+\n+\tif (len < 0) {\n+\t\tint i;\n+\t\tconst char *p = refname;\n+\n+\t\t/* Find total no of '/' separated path-components */\n+\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++)\n+\t\t\t;\n+\t\t/*\n+\t\t * The number of components we need to strip is now\n+\t\t * the total minus the components to be left (Plus one\n+\t\t * because we count the number of '/', but the number\n+\t\t * of components is one more than the no of '/').\n+\t\t */\n+\t\tremaining = i + len + 1;\n+\t}\n+\n+\twhile (remaining-- > 0) {\n+\t\tchar *p = strrchr(start, '/');\n+\t\tif (p == NULL)\n+\t\t\treturn \"\";\n+\t\telse\n+\t\t\tp[0] = '\\0';\n+\t}\n+\treturn start;\n+}\n+\n static const char *show_ref(struct refname_atom *atom, const char *refname)\n {\n \tif (atom->option == R_SHORT)\n \t\treturn shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n \telse if (atom->option == R_LSTRIP)\n \t\treturn lstrip_ref_components(refname, atom->lstrip);\n+\telse if (atom->option == R_RSTRIP)\n+\t\treturn rstrip_ref_components(refname, atom->rstrip);\n \telse\n \t\treturn refname;\n }\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 203cfaa..25a9973 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -55,14 +55,22 @@ test_atom head refname:lstrip=1 heads/master\n test_atom head refname:lstrip=2 master\n test_atom head refname:lstrip=-1 master\n test_atom head refname:lstrip=-2 heads/master\n+test_atom head refname:rstrip=1 refs/heads\n+test_atom head refname:rstrip=2 refs\n+test_atom head refname:rstrip=-1 refs\n+test_atom head refname:rstrip=-2 refs/heads\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n test_atom head upstream:lstrip=2 origin/master\n test_atom head upstream:lstrip=-2 origin/master\n+test_atom head upstream:rstrip=2 refs/remotes\n+test_atom head upstream:rstrip=-2 refs/remotes\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n test_atom head push:lstrip=1 remotes/myfork/master\n test_atom head push:lstrip=-1 master\n+test_atom head push:rstrip=1 refs/remotes/myfork\n+test_atom head push:rstrip=-1 refs\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n@@ -631,4 +639,15 @@ test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n \ttest_cmp expected actual\n '\n \n+cat >expected <<EOF\n+refs\n+refs/heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n+\tgit for-each-ref --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n+\tgit for-each-ref --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"308385","messageId":"20161227162357.28212-18-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 17/20] ref-filter: allow porcelain to translate messages in the output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:54Z","receivedAt":"2016-12-27T16:24:13Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nIntroduce setup_ref_filter_porcelain_msg() so that the messages used in\nthe atom %(upstream:track) can be translated if needed. By default, keep\nthe messages untranslated, which is the right behavior for plumbing\ncommands. This is needed as we port branch.c to use ref-filter's\nprinting API's.\n\nWritten-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n ref-filter.c | 29 +++++++++++++++++++++++++----\n ref-filter.h |  2 ++\n 2 files changed, 27 insertions(+), 4 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex dd7e751..e478ec6 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -16,6 +16,27 @@\n #include \"trailer.h\"\n #include \"wt-status.h\"\n \n+static struct ref_msg {\n+\tconst char *gone;\n+\tconst char *ahead;\n+\tconst char *behind;\n+\tconst char *ahead_behind;\n+} msgs = {\n+\t /* Untranslated plumbing messages: */\n+\t\"gone\",\n+\t\"ahead %d\",\n+\t\"behind %d\",\n+\t\"ahead %d, behind %d\"\n+};\n+\n+void setup_ref_filter_porcelain_msg(void)\n+{\n+\tmsgs.gone = _(\"gone\");\n+\tmsgs.ahead = _(\"ahead %d\");\n+\tmsgs.behind = _(\"behind %d\");\n+\tmsgs.ahead_behind = _(\"ahead %d, behind %d\");\n+}\n+\n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;\n \n@@ -1181,15 +1202,15 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \telse if (atom->u.remote_ref.option == RR_TRACK) {\n \t\tif (stat_tracking_info(branch, &num_ours,\n \t\t\t\t       &num_theirs, NULL)) {\n-\t\t\t*s = xstrdup(\"gone\");\n+\t\t\t*s = xstrdup(msgs.gone);\n \t\t} else if (!num_ours && !num_theirs)\n \t\t\t*s = \"\";\n \t\telse if (!num_ours)\n-\t\t\t*s = xstrfmt(\"behind %d\", num_theirs);\n+\t\t\t*s = xstrfmt(msgs.behind, num_theirs);\n \t\telse if (!num_theirs)\n-\t\t\t*s = xstrfmt(\"ahead %d\", num_ours);\n+\t\t\t*s = xstrfmt(msgs.ahead, num_ours);\n \t\telse\n-\t\t\t*s = xstrfmt(\"ahead %d, behind %d\",\n+\t\t\t*s = xstrfmt(msgs.ahead_behind,\n \t\t\t\t     num_ours, num_theirs);\n \t\tif (!atom->u.remote_ref.nobracket && *s[0]) {\n \t\t\tconst char *to_free = *s;\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 630e7c2..44b36ed 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -113,5 +113,7 @@ struct ref_sorting *ref_default_sorting(void);\n int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);\n /*  Get the current HEAD's description */\n char *get_head_description(void);\n+/*  Set up translated strings in the output. */\n+void setup_ref_filter_porcelain_msg(void);\n \n #endif /*  REF_FILTER_H  */\n-- \n2.10.2\n\n"},{"id":"308386","messageId":"20161227162357.28212-19-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 18/20] branch, tag: use porcelain output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:55Z","receivedAt":"2016-12-27T16:24:16Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nCall ref-filter's setup_ref_filter_porcelain_msg() to enable\ntranslated messages for the %(upstream:tack) atom. Although branch.c\ndoesn't currently use ref-filter's printing API's, this will ensure\nthat when it does in the future patches, we do not need to worry about\ntranslation.\n\nWritten-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/branch.c | 2 ++\n builtin/tag.c    | 2 ++\n 2 files changed, 4 insertions(+)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 6423ebc..34cd61c 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -649,6 +649,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tOPT_END(),\n \t};\n \n+\tsetup_ref_filter_porcelain_msg();\n+\n \tmemset(&filter, 0, sizeof(filter));\n \tfilter.kind = FILTER_REFS_BRANCHES;\n \tfilter.abbrev = -1;\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex b4789ce..8a1a476 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -375,6 +375,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\tsetup_ref_filter_porcelain_msg();\n+\n \tgit_config(git_tag_config, sorting_tail);\n \n \tmemset(&opt, 0, sizeof(opt));\n-- \n2.10.2\n\n"},{"id":"308387","messageId":"20161227162357.28212-20-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 19/20] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:56Z","receivedAt":"2016-12-27T16:24:19Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nPort branch.c to use ref-filter APIs for printing. This clears out\nmost of the code used in branch.c for printing and replaces them with\ncalls made to the ref-filter library.\n\nIntroduce build_format() which gets the format required for printing\nof refs. Make amendments to print_ref_list() to reflect these changes.\n\nThe strings included in build_format() may not be safely quoted for\ninclusion (i.e. it might contain '%' which needs to be escaped with an\nadditional '%'). Introduce quote_literal_for_format() as a helper\nfunction which takes a string and returns a version of the string that\nis safely quoted to be used in the for-each-ref format which is built\nin build_format().\n\nChange calc_maxwidth() to also account for the length of HEAD ref, by\ncalling ref-filter:get_head_discription().\n\nAlso change the test in t6040 to reflect the changes.\n\nBefore this patch, all cross-prefix symrefs weren't shortened. Since\nwe're using ref-filter APIs, we shorten all symrefs by default. We also\nallow the user to change the format if needed with the introduction of\nthe '--format' option in the next patch.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Jeff King <peff@peff.net>\nHelped-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/branch.c         | 249 ++++++++++++++++-------------------------------\n t/t3203-branch-output.sh |   2 +-\n t/t6040-tracking-info.sh |   2 +-\n 3 files changed, 88 insertions(+), 165 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 34cd61c..2887545 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -36,12 +36,12 @@ static unsigned char head_sha1[20];\n \n static int branch_use_color = -1;\n static char branch_colors[][COLOR_MAXLEN] = {\n-\tGIT_COLOR_RESET,\n-\tGIT_COLOR_NORMAL,\t/* PLAIN */\n-\tGIT_COLOR_RED,\t\t/* REMOTE */\n-\tGIT_COLOR_NORMAL,\t/* LOCAL */\n-\tGIT_COLOR_GREEN,\t/* CURRENT */\n-\tGIT_COLOR_BLUE,\t\t/* UPSTREAM */\n+\t\"%(color:reset)\",\n+\t\"%(color:reset)\",\t/* PLAIN */\n+\t\"%(color:red)\",\t\t/* REMOTE */\n+\t\"%(color:reset)\",\t/* LOCAL */\n+\t\"%(color:green)\",\t/* CURRENT */\n+\t\"%(color:blue)\",\t/* UPSTREAM */\n };\n enum color_branch {\n \tBRANCH_COLOR_RESET = 0,\n@@ -280,180 +280,88 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,\n \treturn(ret);\n }\n \n-static void fill_tracking_info(struct strbuf *stat, const char *branch_name,\n-\t\tint show_upstream_ref)\n+static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n {\n-\tint ours, theirs;\n-\tchar *ref = NULL;\n-\tstruct branch *branch = branch_get(branch_name);\n-\tconst char *upstream;\n-\tstruct strbuf fancy = STRBUF_INIT;\n-\tint upstream_is_gone = 0;\n-\tint added_decoration = 1;\n-\n-\tif (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {\n-\t\tif (!upstream)\n-\t\t\treturn;\n-\t\tupstream_is_gone = 1;\n-\t}\n-\n-\tif (show_upstream_ref) {\n-\t\tref = shorten_unambiguous_ref(upstream, 0);\n-\t\tif (want_color(branch_use_color))\n-\t\t\tstrbuf_addf(&fancy, \"%s%s%s\",\n-\t\t\t\t\tbranch_get_color(BRANCH_COLOR_UPSTREAM),\n-\t\t\t\t\tref, branch_get_color(BRANCH_COLOR_RESET));\n-\t\telse\n-\t\t\tstrbuf_addstr(&fancy, ref);\n-\t}\n+\tint i, max = 0;\n+\tfor (i = 0; i < refs->nr; i++) {\n+\t\tstruct ref_array_item *it = refs->items[i];\n+\t\tconst char *desc = it->refname;\n+\t\tint w;\n \n-\tif (upstream_is_gone) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: gone]\"), fancy.buf);\n-\t\telse\n-\t\t\tadded_decoration = 0;\n-\t} else if (!ours && !theirs) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s]\"), fancy.buf);\n-\t\telse\n-\t\t\tadded_decoration = 0;\n-\t} else if (!ours) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: behind %d]\"), fancy.buf, theirs);\n-\t\telse\n-\t\t\tstrbuf_addf(stat, _(\"[behind %d]\"), theirs);\n+\t\tskip_prefix(it->refname, \"refs/heads/\", &desc);\n+\t\tskip_prefix(it->refname, \"refs/remotes/\", &desc);\n+\t\tif (it->kind == FILTER_REFS_DETACHED_HEAD) {\n+\t\t\tchar *head_desc = get_head_description();\n+\t\t\tw = utf8_strwidth(head_desc);\n+\t\t\tfree(head_desc);\n+\t\t} else\n+\t\t\tw = utf8_strwidth(desc);\n \n-\t} else if (!theirs) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: ahead %d]\"), fancy.buf, ours);\n-\t\telse\n-\t\t\tstrbuf_addf(stat, _(\"[ahead %d]\"), ours);\n-\t} else {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: ahead %d, behind %d]\"),\n-\t\t\t\t    fancy.buf, ours, theirs);\n-\t\telse\n-\t\t\tstrbuf_addf(stat, _(\"[ahead %d, behind %d]\"),\n-\t\t\t\t    ours, theirs);\n+\t\tif (it->kind == FILTER_REFS_REMOTES)\n+\t\t\tw += remote_bonus;\n+\t\tif (w > max)\n+\t\t\tmax = w;\n \t}\n-\tstrbuf_release(&fancy);\n-\tif (added_decoration)\n-\t\tstrbuf_addch(stat, ' ');\n-\tfree(ref);\n+\treturn max;\n }\n \n-static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,\n-\t\t\t     struct ref_filter *filter, const char *refname)\n+static const char *quote_literal_for_format(const char *s)\n {\n-\tstruct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;\n-\tconst char *sub = _(\" **** invalid ref ****\");\n-\tstruct commit *commit = item->commit;\n+\tstatic struct strbuf buf = STRBUF_INIT;\n \n-\tif (!parse_commit(commit)) {\n-\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);\n-\t\tsub = subject.buf;\n+\tstrbuf_reset(&buf);\n+\twhile (*s) {\n+\t\tconst char *ep = strchrnul(s, '%');\n+\t\tif (s < ep)\n+\t\t\tstrbuf_add(&buf, s, ep - s);\n+\t\tif (*ep == '%') {\n+\t\t\tstrbuf_addstr(&buf, \"%%\");\n+\t\t\ts = ep + 1;\n+\t\t} else {\n+\t\t\ts = ep;\n+\t\t}\n \t}\n-\n-\tif (item->kind == FILTER_REFS_BRANCHES)\n-\t\tfill_tracking_info(&stat, refname, filter->verbose > 1);\n-\n-\tstrbuf_addf(out, \" %s %s%s\",\n-\t\tfind_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),\n-\t\tstat.buf, sub);\n-\tstrbuf_release(&stat);\n-\tstrbuf_release(&subject);\n+\treturn buf.buf;\n }\n \n-static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,\n-\t\t\t\t      struct ref_filter *filter, const char *remote_prefix)\n+static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)\n {\n-\tchar c;\n-\tint current = 0;\n-\tint color;\n-\tstruct strbuf out = STRBUF_INIT, name = STRBUF_INIT;\n-\tconst char *prefix_to_show = \"\";\n-\tconst char *prefix_to_skip = NULL;\n-\tconst char *desc = item->refname;\n-\tchar *to_free = NULL;\n-\n-\tswitch (item->kind) {\n-\tcase FILTER_REFS_BRANCHES:\n-\t\tprefix_to_skip = \"refs/heads/\";\n-\t\tskip_prefix(desc, prefix_to_skip, &desc);\n-\t\tif (!filter->detached && !strcmp(desc, head))\n-\t\t\tcurrent = 1;\n-\t\telse\n-\t\t\tcolor = BRANCH_COLOR_LOCAL;\n-\t\tbreak;\n-\tcase FILTER_REFS_REMOTES:\n-\t\tprefix_to_skip = \"refs/remotes/\";\n-\t\tskip_prefix(desc, prefix_to_skip, &desc);\n-\t\tcolor = BRANCH_COLOR_REMOTE;\n-\t\tprefix_to_show = remote_prefix;\n-\t\tbreak;\n-\tcase FILTER_REFS_DETACHED_HEAD:\n-\t\tdesc = to_free = get_head_description();\n-\t\tcurrent = 1;\n-\t\tbreak;\n-\tdefault:\n-\t\tcolor = BRANCH_COLOR_PLAIN;\n-\t\tbreak;\n-\t}\n+\tstruct strbuf fmt = STRBUF_INIT;\n+\tstruct strbuf local = STRBUF_INIT;\n+\tstruct strbuf remote = STRBUF_INIT;\n \n-\tc = ' ';\n-\tif (current) {\n-\t\tc = '*';\n-\t\tcolor = BRANCH_COLOR_CURRENT;\n-\t}\n+\tstrbuf_addf(&fmt, \"%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)\",\n+\t\t    branch_get_color(BRANCH_COLOR_CURRENT));\n \n-\tstrbuf_addf(&name, \"%s%s\", prefix_to_show, desc);\n \tif (filter->verbose) {\n-\t\tint utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);\n-\t\tstrbuf_addf(&out, \"%c %s%-*s%s\", c, branch_get_color(color),\n-\t\t\t    maxwidth + utf8_compensation, name.buf,\n+\t\tstrbuf_addf(&local, \"%%(align:%d,left)%%(refname:lstrip=2)%%(end)\", maxwidth);\n+\t\tstrbuf_addf(&local, \"%s\", branch_get_color(BRANCH_COLOR_RESET));\n+\t\tstrbuf_addf(&local, \" %%(objectname:short=7) \");\n+\n+\t\tif (filter->verbose > 1)\n+\t\t\tstrbuf_addf(&local, \"%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)\"\n+\t\t\t\t    \"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)\",\n+\t\t\t\t    branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));\n+\t\telse\n+\t\t\tstrbuf_addf(&local, \"%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)\");\n+\n+\t\tstrbuf_addf(&remote, \"%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)\"\n+\t\t\t    \"%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),\n \t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n-\t} else\n-\t\tstrbuf_addf(&out, \"%c %s%s%s\", c, branch_get_color(color),\n-\t\t\t    name.buf, branch_get_color(BRANCH_COLOR_RESET));\n-\n-\tif (item->symref) {\n-\t\tconst char *symref = item->symref;\n-\t\tif (prefix_to_skip)\n-\t\t\tskip_prefix(symref, prefix_to_skip, &symref);\n-\t\tstrbuf_addf(&out, \" -> %s\", symref);\n-\t}\n-\telse if (filter->verbose)\n-\t\t/* \" f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h\" */\n-\t\tadd_verbose_info(&out, item, filter, desc);\n-\tif (column_active(colopts)) {\n-\t\tassert(!filter->verbose && \"--column and --verbose are incompatible\");\n-\t\tstring_list_append(&output, out.buf);\n \t} else {\n-\t\tprintf(\"%s\\n\", out.buf);\n+\t\tstrbuf_addf(&local, \"%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n+\t\tstrbuf_addf(&remote, \"%s%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), quote_literal_for_format(remote_prefix),\n+\t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n \t}\n-\tstrbuf_release(&name);\n-\tstrbuf_release(&out);\n-\tfree(to_free);\n-}\n-\n-static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n-{\n-\tint i, max = 0;\n-\tfor (i = 0; i < refs->nr; i++) {\n-\t\tstruct ref_array_item *it = refs->items[i];\n-\t\tconst char *desc = it->refname;\n-\t\tint w;\n \n-\t\tskip_prefix(it->refname, \"refs/heads/\", &desc);\n-\t\tskip_prefix(it->refname, \"refs/remotes/\", &desc);\n-\t\tw = utf8_strwidth(desc);\n+\tstrbuf_addf(&fmt, \"%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)\", local.buf, remote.buf);\n \n-\t\tif (it->kind == FILTER_REFS_REMOTES)\n-\t\t\tw += remote_bonus;\n-\t\tif (w > max)\n-\t\t\tmax = w;\n-\t}\n-\treturn max;\n+\tstrbuf_release(&local);\n+\tstrbuf_release(&remote);\n+\treturn strbuf_detach(&fmt, NULL);\n }\n \n static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)\n@@ -462,6 +370,8 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin\n \tstruct ref_array array;\n \tint maxwidth = 0;\n \tconst char *remote_prefix = \"\";\n+\tstruct strbuf out = STRBUF_INIT;\n+\tchar *format;\n \n \t/*\n \t * If we are listing more than just remote branches,\n@@ -473,18 +383,31 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin\n \n \tmemset(&array, 0, sizeof(array));\n \n-\tverify_ref_format(\"%(refname)%(symref)\");\n \tfilter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);\n \n \tif (filter->verbose)\n \t\tmaxwidth = calc_maxwidth(&array, strlen(remote_prefix));\n \n+\tformat = build_format(filter, maxwidth, remote_prefix);\n+\tverify_ref_format(format);\n+\n \tref_array_sort(sorting, &array);\n \n-\tfor (i = 0; i < array.nr; i++)\n-\t\tformat_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);\n+\tfor (i = 0; i < array.nr; i++) {\n+\t\tformat_ref_array_item(array.items[i], format, 0, &out);\n+\t\tif (column_active(colopts)) {\n+\t\t\tassert(!filter->verbose && \"--column and --verbose are incompatible\");\n+\t\t\t /* format to a string_list to let print_columns() do its job */\n+\t\t\tstring_list_append(&output, out.buf);\n+\t\t} else {\n+\t\t\tfwrite(out.buf, 1, out.len, stdout);\n+\t\t\tputchar('\\n');\n+\t\t}\n+\t\tstrbuf_release(&out);\n+\t}\n \n \tref_array_clear(&array);\n+\tfree(format);\n }\n \n static void reject_rebase_or_bisect_branch(const char *target)\ndiff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh\nindex 52283df..4521328 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -194,7 +194,7 @@ test_expect_success 'local-branch symrefs shortened properly' '\n \tgit symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one &&\n \tcat >expect <<-\\EOF &&\n \t  ref-to-branch -> branch-one\n-\t  ref-to-remote -> refs/remotes/origin/branch-one\n+\t  ref-to-remote -> origin/branch-one\n \tEOF\n \tgit branch >actual.raw &&\n \tgrep ref-to <actual.raw >actual &&\ndiff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh\nindex 3d5c238..97a0765 100755\n--- a/t/t6040-tracking-info.sh\n+++ b/t/t6040-tracking-info.sh\n@@ -44,7 +44,7 @@ b1 [ahead 1, behind 1] d\n b2 [ahead 1, behind 1] d\n b3 [behind 1] b\n b4 [ahead 2] f\n-b5 g\n+b5 [gone] g\n b6 c\n EOF\n \n-- \n2.10.2\n\n"},{"id":"308388","messageId":"20161227162357.28212-21-Karthik.188@gmail.com","threadId":"44771","inReplyTo":"20161227162357.28212-1-Karthik.188@gmail.com","subject":"[PATCH v9 20/20] branch: implement '--format' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-27T16:23:57Z","receivedAt":"2016-12-27T16:24:23Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nImplement the '--format' option provided by 'ref-filter'. This lets the\nuser list branches as per desired format similar to the implementation\nin 'git for-each-ref'.\n\nAdd tests and documentation for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-branch.txt |  7 ++++++-\n builtin/branch.c             | 14 +++++++++-----\n t/t3203-branch-output.sh     | 14 ++++++++++++++\n 3 files changed, 29 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 5516a47..1fae4ee 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -12,7 +12,7 @@ SYNOPSIS\n \t[--list] [-v [--abbrev=<length> | --no-abbrev]]\n \t[--column[=<options>] | --no-column]\n \t[(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>]\n-\t[--points-at <object>] [<pattern>...]\n+\t[--points-at <object>] [--format=<format>] [<pattern>...]\n 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]\n 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]\n 'git branch' --unset-upstream [<branchname>]\n@@ -250,6 +250,11 @@ start-point is either a local or remote-tracking branch.\n --points-at <object>::\n \tOnly list branches of the given object.\n \n+--format <format>::\n+\tA string that interpolates `%(fieldname)` from the object\n+\tpointed at by a ref being shown.  The format is the same as\n+\tthat of linkgit:git-for-each-ref[1].\n+\n Examples\n --------\n \ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 2887545..4051a18 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -28,6 +28,7 @@ static const char * const builtin_branch_usage[] = {\n \tN_(\"git branch [<options>] [-r] (-d | -D) <branch-name>...\"),\n \tN_(\"git branch [<options>] (-m | -M) [<old-branch>] <new-branch>\"),\n \tN_(\"git branch [<options>] [-r | -a] [--points-at]\"),\n+\tN_(\"git branch [<options>] [-r | -a] [--format]\"),\n \tNULL\n };\n \n@@ -364,14 +365,14 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r\n \treturn strbuf_detach(&fmt, NULL);\n }\n \n-static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)\n+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)\n {\n \tint i;\n \tstruct ref_array array;\n \tint maxwidth = 0;\n \tconst char *remote_prefix = \"\";\n \tstruct strbuf out = STRBUF_INIT;\n-\tchar *format;\n+\tchar *to_free = NULL;\n \n \t/*\n \t * If we are listing more than just remote branches,\n@@ -388,7 +389,8 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin\n \tif (filter->verbose)\n \t\tmaxwidth = calc_maxwidth(&array, strlen(remote_prefix));\n \n-\tformat = build_format(filter, maxwidth, remote_prefix);\n+\tif (!format)\n+\t\tformat = to_free = build_format(filter, maxwidth, remote_prefix);\n \tverify_ref_format(format);\n \n \tref_array_sort(sorting, &array);\n@@ -407,7 +409,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin\n \t}\n \n \tref_array_clear(&array);\n-\tfree(format);\n+\tfree(to_free);\n }\n \n static void reject_rebase_or_bisect_branch(const char *target)\n@@ -528,6 +530,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \tstruct ref_filter filter;\n \tint icase = 0;\n \tstatic struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;\n+\tconst char *format = NULL;\n \n \tstruct option options[] = {\n \t\tOPT_GROUP(N_(\"Generic options\")),\n@@ -569,6 +572,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tN_(\"print only branches of the object\"), 0, parse_opt_object_name\n \t\t},\n \t\tOPT_BOOL('i', \"ignore-case\", &icase, N_(\"sorting and filtering are case insensitive\")),\n+\t\tOPT_STRING(  0 , \"format\", &format, N_(\"format\"), N_(\"format to use for the output\")),\n \t\tOPT_END(),\n \t};\n \n@@ -641,7 +645,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tif (!sorting)\n \t\t\tsorting = ref_default_sorting();\n \t\tsorting->ignore_case = icase;\n-\t\tprint_ref_list(&filter, sorting);\n+\t\tprint_ref_list(&filter, sorting, format);\n \t\tprint_columns(&output, colopts, NULL);\n \t\tstring_list_clear(&output, 0);\n \t\treturn 0;\ndiff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh\nindex 4521328..5778c0a 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -225,4 +225,18 @@ test_expect_success 'sort branches, ignore case' '\n \t)\n '\n \n+test_expect_success 'git branch --format option' '\n+\tcat >expect <<-\\EOF &&\n+\tRefname is (HEAD detached from fromtag)\n+\tRefname is refs/heads/ambiguous\n+\tRefname is refs/heads/branch-one\n+\tRefname is refs/heads/branch-two\n+\tRefname is refs/heads/master\n+\tRefname is refs/heads/ref-to-branch\n+\tRefname is refs/heads/ref-to-remote\n+\tEOF\n+\tgit branch --format=\"Refname is %(refname)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"308414","messageId":"xmqqbmvx842i.fsf@gitster.mtv.corp.google.com","threadId":"44771","inReplyTo":"20161227162357.28212-2-Karthik.188@gmail.com","subject":"Re: [PATCH v9 01/20] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-27T20:58:04Z","receivedAt":"2016-12-27T21:18:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n>  \n> +Some atoms like %(align) and %(if) always require a matching %(end).\n> +We call them \"opening atoms\" and sometimes denote them as %($open).\n> +\n> +When a scripting language specific quoting is in effect, everything\n> +between a top-level opening atom and its matching %(end) is evaluated\n> +according to the semantics of the opening atom and its result is\n> +quoted.\n\nWhat is unsaid in the last paragraph is that you assume \"is\nevaluated according to the semantics of the opening atom\" does not\ninvolve quoting and only the result from the top-level is quoted.  I\nam not sure if that is clear to the first-time readers.\n\n>  \n>  EXAMPLES\n>  --------\n> @@ -273,6 +291,22 @@ eval=`git for-each-ref --shell --format=\"$fmt\" \\\n>  eval \"$eval\"\n>  ------------\n> ...\n> +------------\n> +git for-each-ref --format=\"%(refname)%(if)%(authorname)%(then) %(color:red)Authored by: %(authorname)%(end)\"\n> +------------\n\nThis makes readers wonder how \"red\"ness is reset, but that is not\nsomething this example is interested in demonstrating.  Let's drop\nthe %(color:red) bit to avoid distracting readers.\n"},{"id":"308415","messageId":"xmqq60m5842g.fsf@gitster.mtv.corp.google.com","threadId":"44771","inReplyTo":"20161227162357.28212-3-Karthik.188@gmail.com","subject":"Re: [PATCH v9 02/20] ref-filter: include reference to 'used_atom' within 'atom_value'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-27T20:59:36Z","receivedAt":"2016-12-27T21:18:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Ensure that each 'atom_value' has a reference to its corresponding\n> 'used_atom'. This let's us use values within 'used_atom' in the\n\ns/let's us use/lets us use/;\n\n> 'handler' function.\n"},{"id":"308416","messageId":"xmqqzijh6phz.fsf@gitster.mtv.corp.google.com","threadId":"44771","inReplyTo":"20161227162357.28212-4-Karthik.188@gmail.com","subject":"Re: [PATCH v9 03/20] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-27T21:00:26Z","receivedAt":"2016-12-27T21:18:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> This is done by introducing 'if_atom_parser()' which parses the given\n> %(if) atom and then stores the data in used_atom which is later passed\n> on to the used_atom of the %(then) atom, so that it can do the required\n> comparisons.\n>\n> Add tests and Documentation for the same.\n\ns/Documentation/documentation/\n"},{"id":"308417","messageId":"xmqqinq56phu.fsf@gitster.mtv.corp.google.com","threadId":"44771","inReplyTo":"20161227162357.28212-20-Karthik.188@gmail.com","subject":"Re: [PATCH v9 19/20] branch: use ref-filter printing APIs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-27T21:17:04Z","receivedAt":"2016-12-27T21:18:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n>  static char branch_colors[][COLOR_MAXLEN] = {\n> -\tGIT_COLOR_RESET,\n> -\tGIT_COLOR_NORMAL,\t/* PLAIN */\n> -\tGIT_COLOR_RED,\t\t/* REMOTE */\n> -\tGIT_COLOR_NORMAL,\t/* LOCAL */\n> -\tGIT_COLOR_GREEN,\t/* CURRENT */\n> -\tGIT_COLOR_BLUE,\t\t/* UPSTREAM */\n> +\t\"%(color:reset)\",\n> +\t\"%(color:reset)\",\t/* PLAIN */\n> +\t\"%(color:red)\",\t\t/* REMOTE */\n> +\t\"%(color:reset)\",\t/* LOCAL */\n> +\t\"%(color:green)\",\t/* CURRENT */\n> +\t\"%(color:blue)\",\t/* UPSTREAM */\n>  };\n\nThe contents of this table is no longer tied to COLOR_MAXLEN.  The\nabove entries used by default happen to be shorter, but it is\nmisleading.\n\n\tstatic const char *branch_colors[] = {\n\t\"%(color:reset)\",\n\t...\n\t};\n\nperhaps?\n\nMore importantly, does this re-definition of the branch_colors[]\nwork with custom colors filled in git_branch_config() by calling\ne.g. color_parse(\"red\", branch_colors[BRANCH_COLOR_REMOTE]), where\n\"red\" and \"remote\" come from an existing configuration file?\n\n\t[color \"branch\"]\n\t\tremote = red\n\nIt obviously would not work if you changed the type of branch_colors[]\nbecause the color_parse() wants the caller to have allocated space\nof COLOR_MAXLEN.  \n\nBut if filling these ANSI escape sequence into the format works OK,\nthen doesn't it tell us that we do not need to have this change to\nuse \"%(color:reset)\" etc. as the new default values?\n"},{"id":"308418","messageId":"xmqqtw9p6phx.fsf@gitster.mtv.corp.google.com","threadId":"44771","inReplyTo":"20161227162357.28212-12-Karthik.188@gmail.com","subject":"Re: [PATCH v9 11/20] ref-filter: introduce refname_atom_parser()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-27T21:04:49Z","receivedAt":"2016-12-27T21:18:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> +symref::\n> +\tThe ref which the given symbolic ref refers to. If not a\n> +\tsymbolic ref, nothing is printed. Respects the `:short` and\n> +\t`:strip` options in the same way as `refname` above.\n> +\n\nI am slightly unhappy with this name.  If we had an atom that lets\nyou ask \"Is this a symref?\" and yields \"\" or \"->\", it could also be\ncalled symref, and we would name it \"is_symref\" or something to\ndisambiguate it.  Then it is only fair to give this one that lets\nyou ask \"What does this symref point at?\" a bit more specific name,\nlike \"symref_target\" or something.\n\nBut probably I am worried too much.  \"is_symref\", if necessary, can\nbe written as \"%(if:notequals=)%(symref)%(then)...%(else)...%(end)\"\nand it is not likely that it would be used often, so let's keep it\nas-is.\n"},{"id":"308419","messageId":"xmqqo9zx6phw.fsf@gitster.mtv.corp.google.com","threadId":"44771","inReplyTo":"20161227162357.28212-16-Karthik.188@gmail.com","subject":"Re: [PATCH v9 15/20] ref-filter: modify the 'lstrip=<N>' option to work with negative '<N>'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-27T21:11:43Z","receivedAt":"2016-12-27T21:18:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> Currently the 'lstrip=<N>' option only takes a positive value '<N>'\n> and strips '<N>' slash-separated path components from the left. Modify\n> the 'lstrip' option to also take a negative number '<N>' which would\n> only _leave_ behind 'N' slash-separated path components from the left.\n\n\"would only leave behind N components from the left\" sounds as if\nthe result is A/B, when you are given A/B/C/D/E and asked to\nlstrip:-2.  Given these two tests added by the patch ...\n\n> +test_atom head refname:lstrip=-1 master\n> +test_atom head refname:lstrip=-2 heads/master\n\n... I somehow think that is not what you wanted to say.  Instead,\nyou strip from the left as many as necessary and leave -N\ncomponents that appear at the right-most end, no?\n\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -98,7 +98,8 @@ refname::\n>  \tabbreviation mode. If `lstrip=<N>` is appended, strips `<N>`\n>  \tslash-separated path components from the front of the refname\n>  \t(e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n> -\t`<N>` must be a positive integer.\n> +\tif `<N>` is a negative number, then only `<N>` path components\n> +\tare left behind.\n\nI think positive <N> is so obvious not to require an example but it\nis good that you have one.  The negative <N> case needs illustration\nmore than the positive case.  Perhaps something like:\n\n    (e.g. %(refname:lstrip=-1) strips components of refs/tags/frotz \n    from the left to leave only one component, i.e. 'frotz').\n\nWould %(refname:lstrip=-4) attempt to strip components of\nrefs/tags/frotz from the left to leave only four components, and\nbecause the original does not have that many components, it ends\nwith refs/tags/frotz?\n\nI am debating myself if we need something like \"When the ref does\nnot have enough components, the result becomes an empty string if\nstripping with positive <N>, or it becomes the full refname if\nstripping with negative <N>.  Neither is an error.\" is necessary\nhere.  Or is it too obvious?\n"},{"id":"308432","messageId":"CA+P7+xpe6XSDFqX+zLid-1M5P_WOzoeaB+fRkDw6Y+MP7qVA_g@mail.gmail.com","threadId":"44771","inReplyTo":"xmqqo9zx6phw.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v9 15/20] ref-filter: modify the 'lstrip=<N>' option to work with negative '<N>'","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-12-28T03:08:39Z","receivedAt":"2016-12-28T03:09:22Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Dec 27, 2016 at 1:11 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> Currently the 'lstrip=<N>' option only takes a positive value '<N>'\n>> and strips '<N>' slash-separated path components from the left. Modify\n>> the 'lstrip' option to also take a negative number '<N>' which would\n>> only _leave_ behind 'N' slash-separated path components from the left.\n>\n> \"would only leave behind N components from the left\" sounds as if\n> the result is A/B, when you are given A/B/C/D/E and asked to\n> lstrip:-2.  Given these two tests added by the patch ...\n>\n>> +test_atom head refname:lstrip=-1 master\n>> +test_atom head refname:lstrip=-2 heads/master\n>\n> ... I somehow think that is not what you wanted to say.  Instead,\n> you strip from the left as many as necessary and leave -N\n> components that appear at the right-most end, no?\n>\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -98,7 +98,8 @@ refname::\n>>       abbreviation mode. If `lstrip=<N>` is appended, strips `<N>`\n>>       slash-separated path components from the front of the refname\n>>       (e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n>> -     `<N>` must be a positive integer.\n>> +     if `<N>` is a negative number, then only `<N>` path components\n>> +     are left behind.\n>\n> I think positive <N> is so obvious not to require an example but it\n> is good that you have one.  The negative <N> case needs illustration\n> more than the positive case.  Perhaps something like:\n>\n>     (e.g. %(refname:lstrip=-1) strips components of refs/tags/frotz\n>     from the left to leave only one component, i.e. 'frotz').\n>\n> Would %(refname:lstrip=-4) attempt to strip components of\n> refs/tags/frotz from the left to leave only four components, and\n> because the original does not have that many components, it ends\n> with refs/tags/frotz?\n>\n> I am debating myself if we need something like \"When the ref does\n> not have enough components, the result becomes an empty string if\n> stripping with positive <N>, or it becomes the full refname if\n> stripping with negative <N>.  Neither is an error.\" is necessary\n> here.  Or is it too obvious?\n\nI do not think it hurts to have, and makes this obvious.\n\nThanks,\nJake\n"},{"id":"308437","messageId":"CAOLa=ZRbpMhFx_QGPeT9oBpKLq7FZ-fDXt=ueY0TDn6y_-4dfg@mail.gmail.com","threadId":"44771","inReplyTo":"xmqqbmvx842i.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v9 01/20] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-28T06:47:49Z","receivedAt":"2016-12-28T06:48:27Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Dec 28, 2016 at 2:28 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>>\n>> +Some atoms like %(align) and %(if) always require a matching %(end).\n>> +We call them \"opening atoms\" and sometimes denote them as %($open).\n>> +\n>> +When a scripting language specific quoting is in effect, everything\n>> +between a top-level opening atom and its matching %(end) is evaluated\n>> +according to the semantics of the opening atom and its result is\n>> +quoted.\n>\n> What is unsaid in the last paragraph is that you assume \"is\n> evaluated according to the semantics of the opening atom\" does not\n> involve quoting and only the result from the top-level is quoted.  I\n> am not sure if that is clear to the first-time readers.\n>\n\nHow about being a little more explicit about that?\n\nWhen a scripting language specific quoting is in effect, everything\nbetween a top-level opening atom and its matching %(end) is evaluated\naccording to the semantics of the opening atom and only its result\nfrom the top-level is quoted.\n\n>>\n>>  EXAMPLES\n>>  --------\n>> @@ -273,6 +291,22 @@ eval=`git for-each-ref --shell --format=\"$fmt\" \\\n>>  eval \"$eval\"\n>>  ------------\n>> ...\n>> +------------\n>> +git for-each-ref --format=\"%(refname)%(if)%(authorname)%(then) %(color:red)Authored by: %(authorname)%(end)\"\n>> +------------\n>\n> This makes readers wonder how \"red\"ness is reset, but that is not\n> something this example is interested in demonstrating.  Let's drop\n> the %(color:red) bit to avoid distracting readers.\n\nSure, will do :)\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"308439","messageId":"CAOLa=ZTcNj2Wpknim727nAVJzi8SqGqyUCph6uGGfgaJPPnLiw@mail.gmail.com","threadId":"44771","inReplyTo":"xmqq60m5842g.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v9 02/20] ref-filter: include reference to 'used_atom' within 'atom_value'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-28T07:45:27Z","receivedAt":"2016-12-28T07:46:21Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Dec 28, 2016 at 2:29 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> From: Karthik Nayak <karthik.188@gmail.com>\n>>\n>> Ensure that each 'atom_value' has a reference to its corresponding\n>> 'used_atom'. This let's us use values within 'used_atom' in the\n>\n> s/let's us use/lets us use/;\n>\n\nNoted, will change.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"308440","messageId":"CAOLa=ZQPde5Xtthj7UqvZBxxVVSrmO2yvP6uKY-NkSuP71xLZw@mail.gmail.com","threadId":"44771","inReplyTo":"xmqqzijh6phz.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v9 03/20] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-28T07:45:45Z","receivedAt":"2016-12-28T07:46:24Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Dec 28, 2016 at 2:30 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> This is done by introducing 'if_atom_parser()' which parses the given\n>> %(if) atom and then stores the data in used_atom which is later passed\n>> on to the used_atom of the %(then) atom, so that it can do the required\n>> comparisons.\n>>\n>> Add tests and Documentation for the same.\n>\n> s/Documentation/documentation/\n\nThanks, will change.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"308442","messageId":"CAOLa=ZR2VF5mEf83A0pmkdAVLPxhUaBhWo=SVjOAUyjRR=QzjA@mail.gmail.com","threadId":"44771","inReplyTo":"xmqqtw9p6phx.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v9 11/20] ref-filter: introduce refname_atom_parser()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-28T07:56:02Z","receivedAt":"2016-12-28T08:03:21Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Dec 28, 2016 at 2:34 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> +symref::\n>> +     The ref which the given symbolic ref refers to. If not a\n>> +     symbolic ref, nothing is printed. Respects the `:short` and\n>> +     `:strip` options in the same way as `refname` above.\n>> +\n>\n> I am slightly unhappy with this name.  If we had an atom that lets\n> you ask \"Is this a symref?\" and yields \"\" or \"->\", it could also be\n> called symref, and we would name it \"is_symref\" or something to\n> disambiguate it.  Then it is only fair to give this one that lets\n> you ask \"What does this symref point at?\" a bit more specific name,\n> like \"symref_target\" or something.\n>\n> But probably I am worried too much.  \"is_symref\", if necessary, can\n> be written as \"%(if:notequals=)%(symref)%(then)...%(else)...%(end)\"\n> and it is not likely that it would be used often, so let's keep it\n> as-is.\n\nYou're probably right about it not being the right name, since symref doesn't\nindicate that the atom will print the ref being pointed to, but the\nname 'symref'\nis short and I guess its easily understandable.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"308446","messageId":"CAOLa=ZTs7G_fk77j-QxiM7fLncPRtJ8DfNGaw1upyT1CSx3Xyw@mail.gmail.com","threadId":"44771","inReplyTo":"xmqqo9zx6phw.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v9 15/20] ref-filter: modify the 'lstrip=<N>' option to work with negative '<N>'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-28T08:54:19Z","receivedAt":"2016-12-28T08:54:55Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Dec 28, 2016 at 2:41 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> Currently the 'lstrip=<N>' option only takes a positive value '<N>'\n>> and strips '<N>' slash-separated path components from the left. Modify\n>> the 'lstrip' option to also take a negative number '<N>' which would\n>> only _leave_ behind 'N' slash-separated path components from the left.\n>\n> \"would only leave behind N components from the left\" sounds as if\n> the result is A/B, when you are given A/B/C/D/E and asked to\n> lstrip:-2.  Given these two tests added by the patch ...\n>\n>> +test_atom head refname:lstrip=-1 master\n>> +test_atom head refname:lstrip=-2 heads/master\n>\n> ... I somehow think that is not what you wanted to say.  Instead,\n> you strip from the left as many as necessary and leave -N\n> components that appear at the right-most end, no?\n>\n\nYup, you're correct it should be 'leave -N components from the right-most\nend'. Will change that.\n\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -98,7 +98,8 @@ refname::\n>>       abbreviation mode. If `lstrip=<N>` is appended, strips `<N>`\n>>       slash-separated path components from the front of the refname\n>>       (e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n>> -     `<N>` must be a positive integer.\n>> +     if `<N>` is a negative number, then only `<N>` path components\n>> +     are left behind.\n>\n> I think positive <N> is so obvious not to require an example but it\n> is good that you have one.  The negative <N> case needs illustration\n> more than the positive case.  Perhaps something like:\n>\n>     (e.g. %(refname:lstrip=-1) strips components of refs/tags/frotz\n>     from the left to leave only one component, i.e. 'frotz').\n\nGood point, but i'll be using N = -2 rather than -1 since N=-1 can\nalso be obtained\nby using N=2 as shown in the existing documentation. With N=-2 we differentiate\nthe use cases of N= positive and negative numbers.\n\ndiff --git a/Documentation/git-for-each-ref.txt\nb/Documentation/git-for-each-ref.txt\nindex 9123c6f..814d77a 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -99,7 +99,8 @@ refname::\n        slash-separated path components from the front of the refname\n        (e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n        if `<N>` is a negative number, then only `<N>` path components\n-       are left behind.\n+       are left behind. (e.g., `%(refname:lstrip=-2)` turns\n+       `refs/tags/foo` into `tags/foo`).\n\n\n>\n> Would %(refname:lstrip=-4) attempt to strip components of\n> refs/tags/frotz from the left to leave only four components, and\n> because the original does not have that many components, it ends\n> with refs/tags/frotz?\n>\n\nIt ends up with 'refs/tags/frotz' since there are not enough components.\n\n> I am debating myself if we need something like \"When the ref does\n> not have enough components, the result becomes an empty string if\n> stripping with positive <N>, or it becomes the full refname if\n> stripping with negative <N>.  Neither is an error.\" is necessary\n> here.  Or is it too obvious?\n\nI had the same self-debate, and dropped it for being too obvious.\n\nOn Wed, Dec 28, 2016 at 8:38 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n>\n> I do not think it hurts to have, and makes this obvious.\n>\n\nBut as Jacob mentioned, it doesn't hurt to mention the obvious\nsometimes. So i'll\nadd that in :)\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"308449","messageId":"CAOLa=ZTPMbhzv_9mLv63UvPu__JqwQrpd2d8ogXuz5wNKb70Dw@mail.gmail.com","threadId":"44771","inReplyTo":"xmqqinq56phu.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v9 19/20] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-28T09:40:04Z","receivedAt":"2016-12-28T09:41:26Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Dec 28, 2016 at 2:47 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>>  static char branch_colors[][COLOR_MAXLEN] = {\n>> -     GIT_COLOR_RESET,\n>> -     GIT_COLOR_NORMAL,       /* PLAIN */\n>> -     GIT_COLOR_RED,          /* REMOTE */\n>> -     GIT_COLOR_NORMAL,       /* LOCAL */\n>> -     GIT_COLOR_GREEN,        /* CURRENT */\n>> -     GIT_COLOR_BLUE,         /* UPSTREAM */\n>> +     \"%(color:reset)\",\n>> +     \"%(color:reset)\",       /* PLAIN */\n>> +     \"%(color:red)\",         /* REMOTE */\n>> +     \"%(color:reset)\",       /* LOCAL */\n>> +     \"%(color:green)\",       /* CURRENT */\n>> +     \"%(color:blue)\",        /* UPSTREAM */\n>>  };\n>\n> The contents of this table is no longer tied to COLOR_MAXLEN.  The\n> above entries used by default happen to be shorter, but it is\n> misleading.\n>\n>         static const char *branch_colors[] = {\n>         \"%(color:reset)\",\n>         ...\n>         };\n>\n> perhaps?\n>\n> More importantly, does this re-definition of the branch_colors[]\n> work with custom colors filled in git_branch_config() by calling\n> e.g. color_parse(\"red\", branch_colors[BRANCH_COLOR_REMOTE]), where\n> \"red\" and \"remote\" come from an existing configuration file?\n>\n>         [color \"branch\"]\n>                 remote = red\n>\n> It obviously would not work if you changed the type of branch_colors[]\n> because the color_parse() wants the caller to have allocated space\n> of COLOR_MAXLEN.\n>\n> But if filling these ANSI escape sequence into the format works OK,\n> then doesn't it tell us that we do not need to have this change to\n> use \"%(color:reset)\" etc. as the new default values?\n\nGood point, this would overwrite the existing configuration based setup\nexisting in builtin/branch.c.\n\nI think it'd make sense to use the existing branch_colors[] definition without\nany changes. That's mean that instead of using %(color:<color>). We hard\ncode the colors by calling  branch_get_color(). This is ok with me since,\nusers who which to have their own formats will anyways use --format option.\n\n-- \nRegards,\nKarthik Nayak\n"}]}