{"thread":{"id":"44845","subject":"[PATCH v10 00/20] port branch.c to use ref-filter's printing options","startedAt":"2017-01-10T08:48:56Z","lastAt":"2017-01-15T05:51:48Z","messageCount":28,"participants":["Karthik Nayak","Junio C Hamano","Jacob Keller"],"isPatch":true,"patchVersion":10,"patchTotal":20},"messages":[{"id":"309109","messageId":"20170110084953.15890-1-Karthik.188@gmail.com","threadId":"44845","inReplyTo":null,"subject":"[PATCH v10 00/20] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:33Z","receivedAt":"2017-01-10T08:48:56Z","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\nv9 can be found here: http://marc.info/?l=git&m=148285579607683&w=2\n\nChanges in this version:\n1. Changes in commit messages. Made 01/20 a little more clearer.\n2. Better documentation regarding usage of negative refs (15/20) and\nsmall changes to the examples introduced to avoid confusion.\n3. Revert the changes made to 'static char branch_colors[]'.\n(http://marc.info/?t=148285594700011&r=1&w=2)\n\nThanks to Junio and Jacob for their suggestions wrt the previous version.\nInterdiff at the bottom.\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 |  87 +++++--\n builtin/branch.c                   | 288 +++++++---------------\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, 781 insertions(+), 304 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 81db67d74..08be8462c 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -95,13 +95,17 @@ 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. 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+\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` and\n+\t`%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).\n+\tif `<N>` is a negative number, then only `<N>` path components\n+\tare left behind. (e.g., `%(refname:lstrip=-2)` turns\n+\t`refs/tags/foo` into `tags/foo` and `%(refname:rstrip=-1)`\n+\tturns `refs/tags/foo` into `refs`). When the ref does not\n+\thave enough components, the result becomes an empty string if\n+\tstripping with positive <N>, or it becomes the full refname if\n+\tstripping with negative <N>.  Neither is an error.\n\n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\n@@ -222,8 +226,8 @@ 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+according to the semantics of the opening atom and only its result\n+from the top-level is quoted.\n\n\n EXAMPLES\n@@ -325,7 +329,7 @@ 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+git for-each-ref --format=\"%(refname)%(if)%(authorname)%(then) Authored by: %(authorname)%(end)\"\n ------------\n\n SEE ALSO\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 4051a18b6..cbaa6d03c 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -37,12 +37,12 @@ static unsigned char head_sha1[20];\n\n static int branch_use_color = -1;\n static char branch_colors[][COLOR_MAXLEN] = {\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+\tGIT_COLOR_RESET,\n+\tGIT_COLOR_NORMAL,       /* PLAIN */\n+\tGIT_COLOR_RED,          /* REMOTE */\n+\tGIT_COLOR_NORMAL,       /* LOCAL */\n+\tGIT_COLOR_GREEN,        /* CURRENT */\n+\tGIT_COLOR_BLUE,         /* UPSTREAM */\n };\n enum color_branch {\n \tBRANCH_COLOR_RESET = 0,\n\n\n--\n2.11.0\n"},{"id":"309110","messageId":"20170110084953.15890-2-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 01/20] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:34Z","receivedAt":"2017-01-10T08:49:02Z","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 abe13f3be..6b671ae92 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 only its result\n+from 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+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) 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 1a978405e..0a578722d 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 d0ab09f4b..fed30133d 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.11.0\n\n"},{"id":"309111","messageId":"20170110084953.15890-4-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 03/20] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:36Z","receivedAt":"2017-01-10T08:49:07Z","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 6b671ae92..d5be41973 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 f31c4b68b..e002629ff 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 fed30133d..a09a1a46e 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.11.0\n\n"},{"id":"309112","messageId":"20170110084953.15890-5-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 04/20] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:37Z","receivedAt":"2017-01-10T08:49:10Z","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 d5be41973..d7ab4c961 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 e002629ff..385fc04d0 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 aea1dfc71..e67c694c3 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.11.0\n\n"},{"id":"309113","messageId":"20170110084953.15890-3-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 02/20] ref-filter: include reference to 'used_atom' within 'atom_value'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:35Z","receivedAt":"2017-01-10T08:49:11Z","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 lets 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 0a578722d..f31c4b68b 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.11.0\n\n"},{"id":"309114","messageId":"20170110084953.15890-8-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 07/20] ref-filter: make %(upstream:track) prints \"[gone]\" for invalid upstreams","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:40Z","receivedAt":"2017-01-10T08:49:14Z","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 d7ab4c961..94c6b88fa 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 47b521cca..998991873 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 e67c694c3..a2e3f5525 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.11.0\n\n"},{"id":"309115","messageId":"20170110084953.15890-7-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 06/20] ref-filter: introduce format_ref_array_item()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:39Z","receivedAt":"2017-01-10T08:49: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\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 5511a200c..47b521cca 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 f78323de0..630e7c2b9 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.11.0\n\n"},{"id":"309116","messageId":"20170110084953.15890-6-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 05/20] ref-filter: move get_head_description() from branch.c","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:38Z","receivedAt":"2017-01-10T08:49: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\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 9d30f55b0..6423ebce5 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 385fc04d0..5511a200c 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 fc55fa357..f78323de0 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.11.0\n\n"},{"id":"309117","messageId":"20170110084953.15890-9-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 08/20] ref-filter: add support for %(upstream:track,nobracket)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:41Z","receivedAt":"2017-01-10T08:49:22Z","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 94c6b88fa..14240b407 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 998991873..6de0927d1 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 a2e3f5525..af76dc530 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.11.0\n\n"},{"id":"309118","messageId":"20170110084953.15890-11-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 10/20] ref-filter: introduce refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:43Z","receivedAt":"2017-01-10T08:49:25Z","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 e98ef4bb6..c1ebc406c 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.11.0\n\n"},{"id":"309119","messageId":"20170110084953.15890-12-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 11/20] ref-filter: introduce refname_atom_parser()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:44Z","receivedAt":"2017-01-10T08:49: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\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 14240b407..5de22cf64 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 c1ebc406c..7bfd65633 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 7663a3661..18a9e2565 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.11.0\n\n"},{"id":"309120","messageId":"20170110084953.15890-10-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 09/20] ref-filter: make \"%(symref)\" atom work with the ':short' modifier","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:42Z","receivedAt":"2017-01-10T08:49:29Z","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 6de0927d1..e98ef4bb6 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 af76dc530..7663a3661 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.11.0\n\n"},{"id":"309121","messageId":"20170110084953.15890-16-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 15/20] ref-filter: modify the 'lstrip=<N>' option to work with negative '<N>'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:48Z","receivedAt":"2017-01-10T08:49:55Z","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\nstrip from the left as necessary and _leave_ behind only 'N'\nslash-separated path components from the right-most end.\n\nFor e.g. %(refname:lstrip=-1) would make 'foo/goo/abc' into 'abc'.\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 |  7 ++++++-\n ref-filter.c                       | 27 ++++++++++++++++++++++-----\n t/t6300-for-each-ref.sh            | 12 ++++++------\n 3 files changed, 34 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 04ffc3552..331e1fef8 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -98,7 +98,12 @@ 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. (e.g., `%(refname:lstrip=-2)` turns\n+\t`refs/tags/foo` into `tags/foo`). When the ref does not have\n+\tenough components, the result becomes an empty string if\n+\tstripping with positive <N>, or it becomes the full refname if\n+\tstripping with negative <N>.  Neither is an error.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 76553ebc4..1cd92ea4e 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 d3d1a97db..203cfaa15 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.11.0\n\n"},{"id":"309122","messageId":"20170110084953.15890-21-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 20/20] branch: implement '--format' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:53Z","receivedAt":"2017-01-10T08:49:58Z","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 5516a47b5..1fae4eeee 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 f293ee5b0..cbaa6d03c 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 45213280a..5778c0afe 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.11.0\n\n"},{"id":"309123","messageId":"20170110084953.15890-18-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 17/20] ref-filter: allow porcelain to translate messages in the output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:50Z","receivedAt":"2017-01-10T08:49: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\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 dd7e751f2..e478ec6c8 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 630e7c2b9..44b36eded 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.11.0\n\n"},{"id":"309124","messageId":"20170110084953.15890-17-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 16/20] ref-filter: add an 'rstrip=<N>' option to atoms which deal with refnames","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:49Z","receivedAt":"2017-01-10T08:50:01Z","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 | 38 +++++++++++++++++++----------------\n ref-filter.c                       | 41 ++++++++++++++++++++++++++++++++++++--\n t/t6300-for-each-ref.sh            | 19 ++++++++++++++++++\n 3 files changed, 79 insertions(+), 19 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 331e1fef8..08be8462c 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -97,11 +97,13 @@ refname::\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+\t(e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and\n+\t`%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).\n \tif `<N>` is a negative number, then only `<N>` path components\n \tare left behind. (e.g., `%(refname:lstrip=-2)` turns\n-\t`refs/tags/foo` into `tags/foo`). When the ref does not have\n-\tenough components, the result becomes an empty string if\n+\t`refs/tags/foo` into `tags/foo` and `%(refname:rstrip=-1)`\n+\tturns `refs/tags/foo` into `refs`). When the ref does not\n+\thave enough components, the result becomes an empty string if\n \tstripping with positive <N>, or it becomes the full refname if\n \tstripping with negative <N>.  Neither is an error.\n \n@@ -120,22 +122,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@@ -177,8 +180,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 1cd92ea4e..dd7e751f2 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 203cfaa15..25a9973ce 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.11.0\n\n"},{"id":"309125","messageId":"20170110084953.15890-20-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 19/20] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:52Z","receivedAt":"2017-01-10T08:50:03Z","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         | 247 ++++++++++++++++-------------------------------\n t/t3203-branch-output.sh |   2 +-\n t/t6040-tracking-info.sh |   2 +-\n 3 files changed, 87 insertions(+), 164 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 34cd61cd9..f293ee5b0 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -37,11 +37,11 @@ static unsigned char head_sha1[20];\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+\tGIT_COLOR_NORMAL,       /* PLAIN */\n+\tGIT_COLOR_RED,          /* REMOTE */\n+\tGIT_COLOR_NORMAL,       /* LOCAL */\n+\tGIT_COLOR_GREEN,        /* CURRENT */\n+\tGIT_COLOR_BLUE,         /* 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 52283dfc8..45213280a 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 3d5c238c8..97a07655a 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.11.0\n\n"},{"id":"309126","messageId":"20170110084953.15890-19-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 18/20] branch, tag: use porcelain output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:51Z","receivedAt":"2017-01-10T08:50:04Z","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 6423ebce5..34cd61cd9 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 b4789cec4..8a1a476db 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.11.0\n\n"},{"id":"309127","messageId":"20170110084953.15890-15-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 14/20] ref-filter: Do not abruptly die when using the 'lstrip=<N>' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:47Z","receivedAt":"2017-01-10T08:50:24Z","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 b0d94deea..04ffc3552 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 e0015c60f..76553ebc4 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 5eb013ca2..d3d1a97db 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.11.0\n\n"},{"id":"309128","messageId":"20170110084953.15890-13-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 12/20] ref-filter: make remote_ref_atom_parser() use refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:45Z","receivedAt":"2017-01-10T08:50: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\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 5de22cf64..b18eabd69 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 7bfd65633..9140539df 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 18a9e2565..c53808424 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.11.0\n\n"},{"id":"309129","messageId":"20170110084953.15890-14-Karthik.188@gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"[PATCH v10 13/20] ref-filter: rename the 'strip' option to 'lstrip'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-10T08:49:46Z","receivedAt":"2017-01-10T08:50:36Z","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 b18eabd69..b0d94deea 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 73df72811..b4789cec4 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 9140539df..e0015c60f 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 c53808424..5eb013ca2 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.11.0\n\n"},{"id":"309179","messageId":"xmqqfukqr6ep.fsf@gitster.mtv.corp.google.com","threadId":"44845","inReplyTo":"20170110084953.15890-4-Karthik.188@gmail.com","subject":"Re: [PATCH v10 03/20] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-10T20:45:02Z","receivedAt":"2017-01-10T20:45:11Z","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> +\t\t\tif_then_else->condition_satisfied = 1;\n> +\t} else \tif (if_then_else->cmp_status == COMPARE_UNEQUAL) {\n\nPlease, no space before tabs (locally fixed--no need to resend).\n"},{"id":"309181","messageId":"xmqqbmver63s.fsf@gitster.mtv.corp.google.com","threadId":"44845","inReplyTo":"20170110084953.15890-1-Karthik.188@gmail.com","subject":"Re: [PATCH v10 00/20] port branch.c to use ref-filter's printing options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-10T20:51:35Z","receivedAt":"2017-01-10T20:51: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> index 81db67d74..08be8462c 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -95,13 +95,17 @@ 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` and\n> +\t`%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).\n\nI hiccupped while reading this, as the (e.g.) example talks about rstrip\nthat is not mentioned in the main text that is enhanced by the\nexample.\n\n\tIf `lstrip=<N>` (`rstrip=<N>`) is appended, strips `<N>`\n\tslash-separated path components from the front (tail) of the\n\trefname (e.g. `%(refname:lstrip=2)` ...\n\nperhaps?\n\n> +\tif `<N>` is a negative number, then only `<N>` path components\n> +\tare left behind.\n\nBegin the sentence with cap?  I'd rephrase it a bit while at it if I\nwere doing this:\n\n\tIf `<N>` is a negative number, strip as many path components\n\tas necessary from the specified end to leave `-<N>` path\n\tcomponents.\n\nOther than the above, looks very good to me.\n\nThanks.\n"},{"id":"309239","messageId":"CA+P7+xqV+CJwP-0_27V26UZbkDzBqbdstGw_Rq=8c3SkjAq2bA@mail.gmail.com","threadId":"44845","inReplyTo":"20170110084953.15890-20-Karthik.188@gmail.com","subject":"Re: [PATCH v10 19/20] branch: use ref-filter printing APIs","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2017-01-11T23:47:06Z","receivedAt":"2017-01-11T23:47:39Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Jan 10, 2017 at 12:49 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> diff --git a/builtin/branch.c b/builtin/branch.c\n> index 34cd61cd9..f293ee5b0 100644\n> --- a/builtin/branch.c\n> +++ b/builtin/branch.c\n> @@ -37,11 +37,11 @@ static unsigned char head_sha1[20];\n>  static int branch_use_color = -1;\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> +       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>  };\n\n\nWhat's... actually changing here? It looks like just white space? Is\nthere a strong reason for why this is changing?\n\nThanks,\nJake\n"},{"id":"309412","messageId":"CAOLa=ZQR9ksPtRw_9FneN26Mjq1TVYx7o=YOM4cDNgrDbuQtXg@mail.gmail.com","threadId":"44845","inReplyTo":"CA+P7+xqV+CJwP-0_27V26UZbkDzBqbdstGw_Rq=8c3SkjAq2bA@mail.gmail.com","subject":"Re: [PATCH v10 19/20] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-14T10:01:57Z","receivedAt":"2017-01-14T10:02:34Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hello,\n\nOn Thu, Jan 12, 2017 at 5:17 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Jan 10, 2017 at 12:49 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> diff --git a/builtin/branch.c b/builtin/branch.c\n>> index 34cd61cd9..f293ee5b0 100644\n>> --- a/builtin/branch.c\n>> +++ b/builtin/branch.c\n>> @@ -37,11 +37,11 @@ static unsigned char head_sha1[20];\n>>  static int branch_use_color = -1;\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>> +       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>>  };\n>\n>\n> What's... actually changing here? It looks like just white space? Is\n> there a strong reason for why this is changing?\n>\n> Thanks,\n> Jake\n\nNone, I'm not sure how this ended up being added too.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"309413","messageId":"CAOLa=ZRd3eNA_YO7zeALuxWO3F4_=OuH3ZPr-30Jf7m07c4jcw@mail.gmail.com","threadId":"44845","inReplyTo":"xmqqfukqr6ep.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v10 03/20] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-14T10:02:24Z","receivedAt":"2017-01-14T10:03:00Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hello,\n\nOn Wed, Jan 11, 2017 at 2:15 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> +                     if_then_else->condition_satisfied = 1;\n>> +     } else  if (if_then_else->cmp_status == COMPARE_UNEQUAL) {\n>\n> Please, no space before tabs (locally fixed--no need to resend).\n\nThanks.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"309414","messageId":"CAOLa=ZTuk6Qd_LbCFUrV7ChCON8GvQin90JbKP+j0ibi=4iLTQ@mail.gmail.com","threadId":"44845","inReplyTo":"xmqqbmver63s.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v10 00/20] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2017-01-14T10:03:39Z","receivedAt":"2017-01-14T10:04:24Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Jan 11, 2017 at 2:21 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> index 81db67d74..08be8462c 100644\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -95,13 +95,17 @@ 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>` 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` and\n>> +     `%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).\n>\n> I hiccupped while reading this, as the (e.g.) example talks about rstrip\n> that is not mentioned in the main text that is enhanced by the\n> example.\n>\n>         If `lstrip=<N>` (`rstrip=<N>`) is appended, strips `<N>`\n>         slash-separated path components from the front (tail) of the\n>         refname (e.g. `%(refname:lstrip=2)` ...\n>\n> perhaps?\n>\n>> +     if `<N>` is a negative number, then only `<N>` path components\n>> +     are left behind.\n>\n> Begin the sentence with cap?  I'd rephrase it a bit while at it if I\n> were doing this:\n>\n>         If `<N>` is a negative number, strip as many path components\n>         as necessary from the specified end to leave `-<N>` path\n>         components.\n>\n> Other than the above, looks very good to me.\n>\n> Thanks.\n\nI like how you rephrased it.\nSo apart form this and the other change suggested by you and Jacob,\nnothing to be added.\nShould I re-roll?\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"309432","messageId":"CA+P7+xoN12HqaVnYfESrf3-1ZSWkxAz9K+7rRi2gPpGPNSYmHQ@mail.gmail.com","threadId":"44845","inReplyTo":"CAOLa=ZQR9ksPtRw_9FneN26Mjq1TVYx7o=YOM4cDNgrDbuQtXg@mail.gmail.com","subject":"Re: [PATCH v10 19/20] branch: use ref-filter printing APIs","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2017-01-15T05:51:18Z","receivedAt":"2017-01-15T05:51:48Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Sat, Jan 14, 2017 at 2:01 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> Hello,\n>\n> On Thu, Jan 12, 2017 at 5:17 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n>> On Tue, Jan 10, 2017 at 12:49 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>>> diff --git a/builtin/branch.c b/builtin/branch.c\n>>> index 34cd61cd9..f293ee5b0 100644\n>>> --- a/builtin/branch.c\n>>> +++ b/builtin/branch.c\n>>> @@ -37,11 +37,11 @@ static unsigned char head_sha1[20];\n>>>  static int branch_use_color = -1;\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>>> +       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>>>  };\n>>\n>>\n>> What's... actually changing here? It looks like just white space? Is\n>> there a strong reason for why this is changing?\n>>\n>> Thanks,\n>> Jake\n>\n> None, I'm not sure how this ended up being added too.\n>\n> --\n> Regards,\n> Karthik Nayak\n\nIt looks like it might just be reformatting of some spaces or stray\nend-spaces or something. Hard to see in an email.\n\nThanks,\nJake\n"}]}