{"thread":{"id":"44444","subject":"[PATCH v7 00/17] port branch.c to use ref-filter's printing options","startedAt":"2016-11-08T20:12:14Z","lastAt":"2016-11-22T18:35:07Z","messageCount":94,"participants":["Karthik Nayak","Jacob Keller","Junio C Hamano","Jakub Narębski","Matthieu Moy"],"isPatch":true,"patchVersion":7,"patchTotal":17},"messages":[{"id":"305568","messageId":"20161108201211.25213-1-Karthik.188@gmail.com","threadId":"44444","inReplyTo":null,"subject":"[PATCH v7 00/17] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:11:54Z","receivedAt":"2016-11-08T20:12:14Z","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\n\nChanges in this version:\n\n1. Rebased on top of master.\n\nKarthik Nayak (17):\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 symref_atom_parser() and refname_atom_parser()\n  ref-filter: make remote_ref_atom_parser() use\n    refname_atom_parser_internal()\n  ref-filter: add `:dir` and `:base` options for ref printing atoms\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 |  82 ++++++-\n builtin/branch.c                   | 277 +++++++---------------\n builtin/tag.c                      |   2 +\n ref-filter.c                       | 456 +++++++++++++++++++++++++++++++------\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            |  73 +++++-\n t/t6302-for-each-ref-filter.sh     |  94 ++++++++\n 10 files changed, 725 insertions(+), 291 deletions(-)\n\n-- \n2.10.2\n\n"},{"id":"305569","messageId":"20161108201211.25213-2-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 01/17] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:11:55Z","receivedAt":"2016-11-08T20:12: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\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 |  40 +++++++++++\n ref-filter.c                       | 133 +++++++++++++++++++++++++++++++++++--\n t/t6302-for-each-ref-filter.sh     |  76 +++++++++++++++++++++\n 3 files changed, 242 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex f57e69b..fed8126 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -146,6 +146,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@@ -181,6 +191,20 @@ 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 (i.e. one of\n+`--shell`, `--perl`, `--python`, `--tcl` is used), except for opening\n+atoms, replacement from every %(atom) is quoted when and only when it\n+appears at the top-level (that is, when it appears outside\n+%($open)...%(end)).\n+\n+When a scripting language specific quoting is in effect, everything\n+between a top-level opening atom and its matching %(end) is evaluated\n+according to the semantics of the opening atom and its result is\n+quoted.\n+\n \n EXAMPLES\n --------\n@@ -268,6 +292,22 @@ eval=`git for-each-ref --shell --format=\"$fmt\" \\\n eval \"$eval\"\n ------------\n \n+\n+An example to show the usage of %(if)...%(then)...%(else)...%(end).\n+This prefixes the current branch with a star.\n+\n+------------\n+git for-each-ref --format=\"%(if)%(HEAD)%(then)* %(else)  %(end)%(refname:short)\" refs/heads/\n+------------\n+\n+\n+An example to show the usage of %(if)...%(then)...%(end).\n+This prints the authorname, if present.\n+\n+------------\n+git for-each-ref --format=\"%(refname)%(if)%(authorname)%(then) %(color:red)Authored by: %(authorname)%(end)\"\n+------------\n+\n SEE ALSO\n --------\n linkgit:git-show-ref[1]\ndiff --git a/ref-filter.c b/ref-filter.c\nindex d4c2931..8c183a0 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -21,6 +21,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@@ -203,6 +209,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@@ -210,7 +219,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@@ -343,13 +352,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@@ -363,6 +373,103 @@ 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+\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@@ -370,14 +477,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@@ -1029,6 +1139,15 @@ static void populate_value(struct ref_array_item *ref)\n \t\t} else if (!strcmp(name, \"end\")) {\n \t\t\tv->handler = end_atom_handler;\n \t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"if\")) {\n+\t\t\tv->handler = if_atom_handler;\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"then\")) {\n+\t\t\tv->handler = then_atom_handler;\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"else\")) {\n+\t\t\tv->handler = else_atom_handler;\n+\t\t\tcontinue;\n \t\t} else\n \t\t\tcontinue;\n \ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex d0ab09f..fed3013 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -327,4 +327,80 @@ test_expect_success 'reverse version sort' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'improper usage of %(if), %(then), %(else) and %(end) atoms' '\n+\ttest_must_fail git for-each-ref --format=\"%(if)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(then) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(then) %(then) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(then) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(then) %(else)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(else) %(then) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(then) %(else) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(end)\"\n+'\n+\n+test_expect_success 'check %(if)...%(then)...%(end) atoms' '\n+\tgit for-each-ref --format=\"%(refname)%(if)%(authorname)%(then) Author: %(authorname)%(end)\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/master Author: A U Thor\n+\trefs/heads/side Author: A U Thor\n+\trefs/odd/spot Author: A U Thor\n+\trefs/tags/annotated-tag\n+\trefs/tags/doubly-annotated-tag\n+\trefs/tags/doubly-signed-tag\n+\trefs/tags/foo1.10 Author: A U Thor\n+\trefs/tags/foo1.3 Author: A U Thor\n+\trefs/tags/foo1.6 Author: A U Thor\n+\trefs/tags/four Author: A U Thor\n+\trefs/tags/one Author: A U Thor\n+\trefs/tags/signed-tag\n+\trefs/tags/three Author: A U Thor\n+\trefs/tags/two Author: A U Thor\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'check %(if)...%(then)...%(else)...%(end) atoms' '\n+\tgit for-each-ref --format=\"%(if)%(authorname)%(then)%(authorname)%(else)No author%(end): %(refname)\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tA U Thor: refs/heads/master\n+\tA U Thor: refs/heads/side\n+\tA U Thor: refs/odd/spot\n+\tNo author: refs/tags/annotated-tag\n+\tNo author: refs/tags/doubly-annotated-tag\n+\tNo author: refs/tags/doubly-signed-tag\n+\tA U Thor: refs/tags/foo1.10\n+\tA U Thor: refs/tags/foo1.3\n+\tA U Thor: refs/tags/foo1.6\n+\tA U Thor: refs/tags/four\n+\tA U Thor: refs/tags/one\n+\tNo author: refs/tags/signed-tag\n+\tA U Thor: refs/tags/three\n+\tA U Thor: refs/tags/two\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+test_expect_success 'ignore spaces in %(if) atom usage' '\n+\tgit for-each-ref --format=\"%(refname:short): %(if)%(HEAD)%(then)Head ref%(else)Not Head ref%(end)\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tmaster: Head ref\n+\tside: Not Head ref\n+\todd/spot: Not Head ref\n+\tannotated-tag: Not Head ref\n+\tdoubly-annotated-tag: Not Head ref\n+\tdoubly-signed-tag: Not Head ref\n+\tfoo1.10: Not Head ref\n+\tfoo1.3: Not Head ref\n+\tfoo1.6: Not Head ref\n+\tfour: Not Head ref\n+\tone: Not Head ref\n+\tsigned-tag: Not Head ref\n+\tthree: Not Head ref\n+\ttwo: Not Head ref\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"305570","messageId":"20161108201211.25213-3-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 02/17] ref-filter: include reference to 'used_atom' within 'atom_value'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:11:56Z","receivedAt":"2016-11-08T20:12:24Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nEnsure that each 'atom_value' has a reference to its corresponding\n'used_atom'. This let's us use values within 'used_atom' in the\n'handler' function.\n\nHence we can get the %(align) atom's parameters directly from the\n'used_atom' therefore removing the necessity of passing %(align) atom's\nparameters to 'atom_value'.\n\nThis also acts as a preparatory patch for the upcoming patch where we\nintroduce %(if:equals=) and %(if:notequals=).\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n ref-filter.c | 8 +++-----\n 1 file changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 8c183a0..8392303 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -230,11 +230,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@@ -370,7 +368,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@@ -1069,6 +1067,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@@ -1133,7 +1132,6 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\t\tv->s = \" \";\n \t\t\tcontinue;\n \t\t} else if (starts_with(name, \"align\")) {\n-\t\t\tv->u.align = atom->u.align;\n \t\t\tv->handler = align_atom_handler;\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"end\")) {\n-- \n2.10.2\n\n"},{"id":"305571","messageId":"20161108201211.25213-4-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 03/17] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:11:57Z","receivedAt":"2016-11-08T20:12:27Z","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 differnt 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                       | 43 +++++++++++++++++++++++++++++++++-----\n t/t6302-for-each-ref-filter.sh     | 18 ++++++++++++++++\n 3 files changed, 59 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex fed8126..b7b8560 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -155,6 +155,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 8392303..44481c3 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -22,6 +22,8 @@ struct align {\n };\n \n struct if_then_else {\n+\tconst char *if_equals,\n+\t\t*not_equals;\n \tunsigned int then_atom_seen : 1,\n \t\telse_atom_seen : 1,\n \t\tcondition_satisfied : 1;\n@@ -49,6 +51,10 @@ static struct used_atom {\n \t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n \t\t\tunsigned int nlines;\n \t\t} contents;\n+\t\tstruct {\n+\t\t\tconst char *if_equals,\n+\t\t\t\t*not_equals;\n+\t\t} if_then_else;\n \t\tenum { O_FULL, O_SHORT } objectname;\n \t} u;\n } *used_atom;\n@@ -169,6 +175,19 @@ 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\treturn;\n+\telse if (skip_prefix(arg, \"equals=\", &atom->u.if_then_else.if_equals))\n+\t\t ;\n+\telse if (skip_prefix(arg, \"notequals=\", &atom->u.if_then_else.not_equals))\n+\t\t;\n+\telse\n+\t\tdie(_(\"unrecognized %%(if) argument: %s\"), arg);\n+}\n+\n+\n static struct {\n \tconst char *name;\n \tcmp_type cmp_type;\n@@ -209,7 +228,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@@ -410,6 +429,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->if_equals = atomv->atom->u.if_then_else.if_equals;\n+\tif_then_else->not_equals = atomv->atom->u.if_then_else.not_equals;\n+\n \tpush_stack_element(&state->stack);\n \tnew = state->stack;\n \tnew->at_end = if_then_else_handler;\n@@ -441,10 +463,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->if_equals) {\n+\t\tif (!strcmp(if_then_else->if_equals, cur->output.buf))\n+\t\t\tif_then_else->condition_satisfied = 1;\n+\t} else \tif (if_then_else->not_equals) {\n+\t\tif (strcmp(if_then_else->not_equals, 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@@ -1137,7 +1166,11 @@ static void populate_value(struct ref_array_item *ref)\n \t\t} else if (!strcmp(name, \"end\")) {\n \t\t\tv->handler = end_atom_handler;\n \t\t\tcontinue;\n-\t\t} else if (!strcmp(name, \"if\")) {\n+\t\t} else if (starts_with(name, \"if\")) {\n+\t\t\tconst char *s;\n+\n+\t\t\tif (skip_prefix(name, \"if:\", &s))\n+\t\t\t\tv->s = xstrdup(s);\n \t\t\tv->handler = if_atom_handler;\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"then\")) {\ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex fed3013..a09a1a4 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -403,4 +403,22 @@ test_expect_success 'ignore spaces in %(if) atom usage' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'check %(if:equals=<string>)' '\n+\tgit for-each-ref --format=\"%(if:equals=master)%(refname:short)%(then)Found master%(else)Not master%(end)\" refs/heads/ >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tFound master\n+\tNot master\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'check %(if:notequals=<string>)' '\n+\tgit for-each-ref --format=\"%(if:notequals=master)%(refname:short)%(then)Not master%(else)Found master%(end)\" refs/heads/ >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tFound master\n+\tNot master\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"305572","messageId":"20161108201211.25213-5-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 04/17] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:11:58Z","receivedAt":"2016-11-08T20:12:31Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\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 provided\nobject 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 |  4 ++++\n ref-filter.c                       | 25 +++++++++++++++++++------\n t/t6300-for-each-ref.sh            | 10 ++++++++++\n 3 files changed, 33 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex b7b8560..92184c4 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -107,6 +107,10 @@ 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 \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 44481c3..fe4ea2b 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -55,7 +55,10 @@ static struct used_atom {\n \t\t\tconst char *if_equals,\n \t\t\t\t*not_equals;\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@@ -118,10 +121,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@@ -591,12 +601,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 19a2823..2be0a3f 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -60,6 +60,8 @@ test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n test_atom head objectname:short $(git rev-parse --short refs/heads/master)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom head tree $(git rev-parse refs/heads/master^{tree})\n test_atom head parent ''\n test_atom head numparent 0\n@@ -99,6 +101,8 @@ test_atom tag objecttype tag\n test_atom tag objectsize 154\n test_atom tag objectname $(git rev-parse refs/tags/testtag)\n test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom tag tree ''\n test_atom tag parent ''\n test_atom tag numparent ''\n@@ -164,6 +168,12 @@ test_expect_success 'Check invalid format specifiers are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n '\n \n+test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n+\ttest_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n+'\n+\n test_date () {\n \tf=$1 &&\n \tcommitter_date=$2 &&\n-- \n2.10.2\n\n"},{"id":"305573","messageId":"20161108201211.25213-6-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 05/17] ref-filter: move get_head_description() from branch.c","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:11:59Z","receivedAt":"2016-11-08T20:12:34Z","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 d5d93a8..be9773a 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 fe4ea2b..40bdf97 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -13,6 +13,7 @@\n #include \"utf8.h\"\n #include \"git-compat-util.h\"\n #include \"version.h\"\n+#include \"wt-status.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n@@ -1077,6 +1078,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@@ -1116,9 +1148,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 14d435e..4aea594 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -106,5 +106,7 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);\n struct ref_sorting *ref_default_sorting(void);\n /*  Function to parse --merged and --no-merged options */\n int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);\n+/*  Get the current HEAD's description */\n+char *get_head_description(void);\n \n #endif /*  REF_FILTER_H  */\n-- \n2.10.2\n\n"},{"id":"305574","messageId":"20161108201211.25213-7-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 06/17] ref-filter: introduce format_ref_array_item()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:00Z","receivedAt":"2016-11-08T20:12:37Z","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 40bdf97..b8b8a95 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1795,10 +1795,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@@ -1828,9 +1828,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 4aea594..0014b92 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -98,6 +98,9 @@ int parse_ref_filter_atom(const char *atom, const char *ep);\n int verify_ref_format(const char *format);\n /*  Sort the given ref_array as per the ref_sorting provided */\n void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);\n+/*  Based on the given format and quote_style, fill the strbuf */\n+void format_ref_array_item(struct ref_array_item *info, const char *format,\n+\t\t\t   int quote_style, struct strbuf *final_buf);\n /*  Print the ref using the given format and quote_style */\n void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style);\n /*  Callback function for parsing the sort option */\n-- \n2.10.2\n\n"},{"id":"305575","messageId":"20161108201211.25213-9-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 08/17] ref-filter: add support for %(upstream:track,nobracket)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:02Z","receivedAt":"2016-11-08T20:12:39Z","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 |  8 +++--\n ref-filter.c                       | 67 +++++++++++++++++++++++++-------------\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 51 insertions(+), 26 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex fd365eb..3953431 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -118,9 +118,11 @@ 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.\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 6d51b80..4d7e414 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -46,8 +46,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 } option;\n \t\t\tunsigned int nlines;\n@@ -75,16 +77,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@@ -1045,25 +1064,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 a92b36f..2c5f177 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -372,6 +372,8 @@ test_expect_success 'setup for upstream:track[short]' '\n \n test_atom head upstream:track '[ahead 1]'\n test_atom head upstream:trackshort '>'\n+test_atom head upstream:track,nobracket 'ahead 1'\n+test_atom head upstream:nobracket,track 'ahead 1'\n test_atom head push:track '[ahead 1]'\n test_atom head push:trackshort '>'\n \n-- \n2.10.2\n\n"},{"id":"305576","messageId":"20161108201211.25213-8-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 07/17] ref-filter: make %(upstream:track) prints \"[gone]\" for invalid upstreams","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:01Z","receivedAt":"2016-11-08T20:12:42Z","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 92184c4..fd365eb 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -119,7 +119,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 b8b8a95..6d51b80 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1049,8 +1049,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 2be0a3f..a92b36f 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -382,7 +382,7 @@ test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n \n test_expect_success 'Check that :track[short] works when upstream is invalid' '\n \tcat >expected <<-\\EOF &&\n-\n+\t[gone]\n \n \tEOF\n \ttest_when_finished \"git config branch.master.merge refs/heads/master\" &&\n-- \n2.10.2\n\n"},{"id":"305577","messageId":"20161108201211.25213-10-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 09/17] ref-filter: make \"%(symref)\" atom work with the ':short' modifier","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:03Z","receivedAt":"2016-11-08T20:12:43Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\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 using comparing with valid_atom rather than used_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 4d7e414..5666814 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -338,7 +338,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 2c5f177..b06ea1c 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@@ -565,4 +566,27 @@ test_expect_success 'Verify sort with multiple keys' '\n \t\trefs/tags/bogo refs/tags/master > actual &&\n \ttest_cmp expected actual\n '\n+\n+test_expect_success 'Add symbolic ref for the following tests' '\n+\tgit symbolic-ref refs/heads/sym refs/heads/master\n+'\n+\n+cat >expected <<EOF\n+refs/heads/master\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref) atom' '\n+\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+heads/master\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:short) atom' '\n+\tgit for-each-ref --format=\"%(symref:short)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"305578","messageId":"20161108201211.25213-11-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 10/17] ref-filter: introduce refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:04Z","receivedAt":"2016-11-08T20:12:44Z","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 5666814..aad537d 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -30,6 +30,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@@ -62,6 +67,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@@ -75,6 +81,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} \telse\n+\t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n+}\n+\n static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n {\n \tstruct string_list params = STRING_LIST_INIT_DUP;\n-- \n2.10.2\n\n"},{"id":"305579","messageId":"20161108201211.25213-12-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 11/17] ref-filter: introduce symref_atom_parser() and refname_atom_parser()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:05Z","receivedAt":"2016-11-08T20:12:48Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nUsing refname_atom_parser_internal(), introduce symref_atom_parser() and\nrefname_atom_parser() which will parse the atoms %(symref) and\n%(refname) respectively. Store the parsed information into the\n'used_atom' structure based on the modifiers used along 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                       | 78 ++++++++++++++++++++++----------------\n t/t6300-for-each-ref.sh            |  9 +++++\n 3 files changed, 59 insertions(+), 33 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 3953431..a669a32 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -166,6 +166,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 aad537d..f1d27b5 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -176,6 +176,16 @@ 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 symref_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 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@@ -244,7 +254,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@@ -273,7 +283,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, symref_atom_parser },\n \t{ \"flag\" },\n \t{ \"HEAD\" },\n \t{ \"color\", FIELD_STR, color_atom_parser },\n@@ -1058,21 +1068,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@@ -1081,6 +1086,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@@ -1153,6 +1168,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@@ -1181,7 +1211,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@@ -1192,12 +1221,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@@ -1273,21 +1300,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 b06ea1c..3d28234 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -589,4 +589,13 @@ test_expect_success 'Verify usage of %(symref:short) atom' '\n \ttest_cmp expected actual\n '\n \n+cat >expected <<EOF\n+master\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:strip) atom' '\n+\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"305580","messageId":"20161108201211.25213-13-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 12/17] ref-filter: make remote_ref_atom_parser() use refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:06Z","receivedAt":"2016-11-08T20:12:51Z","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 | 27 ++++++++++++++-------------\n ref-filter.c                       | 26 +++++++++++++++-----------\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 31 insertions(+), 24 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex a669a32..600b703 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -114,21 +114,22 @@ 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.\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.\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 f1d27b5..7d3d3a6 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -52,7 +52,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@@ -102,7 +103,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@@ -112,16 +115,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@@ -1100,8 +1104,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@@ -1133,8 +1137,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 3d28234..7ca0a12 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -55,8 +55,10 @@ test_atom head refname:strip=1 heads/master\n test_atom head refname:strip=2 master\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n+test_atom head upstream:strip=2 origin/master\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n+test_atom head push:strip=1 remotes/myfork/master\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n-- \n2.10.2\n\n"},{"id":"305581","messageId":"20161108201211.25213-15-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 14/17] ref-filter: allow porcelain to translate messages in the output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:08Z","receivedAt":"2016-11-08T20:12:54Z","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. This is needed\nas we port branch.c to use ref-filter's printing 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 | 28 ++++++++++++++++++++++++----\n ref-filter.h |  2 ++\n 2 files changed, 26 insertions(+), 4 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex b47b900..944671a 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -15,6 +15,26 @@\n #include \"version.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\"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 \n struct align {\n@@ -1130,15 +1150,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 0014b92..da17145 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -111,5 +111,7 @@ struct ref_sorting *ref_default_sorting(void);\n int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);\n /*  Get the current HEAD's description */\n char *get_head_description(void);\n+/*  Set up translated strings in the output. */\n+void setup_ref_filter_porcelain_msg(void);\n \n #endif /*  REF_FILTER_H  */\n-- \n2.10.2\n\n"},{"id":"305582","messageId":"20161108201211.25213-14-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:07Z","receivedAt":"2016-11-08T20:12:56Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nAdd the options `:dir` and `:base` to all ref printing ('%(refname)',\n'%(symref)', '%(push)' and '%(upstream)') atoms. The `:dir` option gives\nthe directory (the part after $GIT_DIR/) of the ref without the\nrefname. The `:base` option gives the base directory of the given\nref (i.e. the directory following $GIT_DIR/refs/).\n\nAdd tests and documentation for the same.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 34 +++++++++++++++++++---------------\n ref-filter.c                       | 29 +++++++++++++++++++++++++----\n t/t6300-for-each-ref.sh            | 24 ++++++++++++++++++++++++\n 3 files changed, 68 insertions(+), 19 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 600b703..f4ad297 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -96,7 +96,9 @@ refname::\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`<N>` must be a positive integer.  If a displayed ref has fewer\n-\tcomponents than `<N>`, the command aborts with an error.\n+\tcomponents than `<N>`, the command aborts with an error. For the base\n+\tdirectory of the ref (i.e. foo in refs/foo/bar/boz) append\n+\t`:base`. For the entire directory path append `:dir`.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\n@@ -114,22 +116,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 `: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.\n+\tfrom the displayed ref. Respects `:short`, `:strip`, `:base`\n+\tand `:dir` 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.\n \n push::\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+\t`:track`, `:trackshort`, `:base` and `:dir` options as\n+\t`upstream` does. Produces an empty string if no `@{push}` ref\n+\tis configured.\n \n HEAD::\n \t'*' if HEAD matches current ref (the checked out branch), ' '\n@@ -169,8 +172,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`:strip` options in the same way as `refname` above.\n+\tsymbolic ref, nothing is printed. Respects the `:short`,\n+\t`:strip`, `:base` and `:dir` options in the same way as\n+\t`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/ref-filter.c b/ref-filter.c\nindex 7d3d3a6..b47b900 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -31,7 +31,7 @@ struct if_then_else {\n };\n \n struct refname_atom {\n-\tenum { R_NORMAL, R_SHORT, R_STRIP } option;\n+\tenum { R_BASE, R_DIR, R_NORMAL, R_SHORT, R_STRIP } option;\n \tunsigned int strip;\n };\n \n@@ -93,7 +93,11 @@ static void refname_atom_parser_internal(struct refname_atom *atom,\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} \telse\n+\t} else if (!strcmp(arg, \"dir\"))\n+\t\tatom->option = R_DIR;\n+\telse if (!strcmp(arg, \"base\"))\n+\t\tatom->option = R_BASE;\n+\telse\n \t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n }\n \n@@ -252,7 +256,6 @@ static void if_atom_parser(struct used_atom *atom, const char *arg)\n \t\tdie(_(\"unrecognized %%(if) argument: %s\"), arg);\n }\n \n-\n static struct {\n \tconst char *name;\n \tcmp_type cmp_type;\n@@ -1096,7 +1099,25 @@ static const char *show_ref(struct refname_atom *atom, const char *refname)\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+\telse if (atom->option == R_BASE) {\n+\t\tconst char *sp, *ep;\n+\n+\t\tif (skip_prefix(refname, \"refs/\", &sp)) {\n+\t\t\tep = strchr(sp, '/');\n+\t\t\tif (!ep)\n+\t\t\t\treturn \"\";\n+\t\t\treturn xstrndup(sp, ep - sp);\n+\t\t}\n+\t\treturn \"\";\n+\t} else if (atom->option == R_DIR) {\n+\t\tconst char *sp, *ep;\n+\n+\t\tsp = refname;\n+\t\tep = strrchr(sp, '/');\n+\t\tif (!ep)\n+\t\t\treturn \"\";\n+\t\treturn xstrndup(sp, ep - sp);\n+\t} else\n \t\treturn refname;\n }\n \ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 7ca0a12..8ff6568 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -53,12 +53,18 @@ 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:dir refs/heads\n+test_atom head refname:base heads\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:dir refs/remotes/origin\n+test_atom head upstream:base remotes\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:dir refs/remotes/myfork\n+test_atom head push:base remotes\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n@@ -600,4 +606,22 @@ test_expect_success 'Verify usage of %(symref:strip) atom' '\n \ttest_cmp expected actual\n '\n \n+cat >expected <<EOF\n+refs/heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:dir) atom' '\n+\tgit for-each-ref --format=\"%(symref:dir)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:base) atom' '\n+\tgit for-each-ref --format=\"%(symref:base)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"305583","messageId":"20161108201211.25213-17-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 16/17] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:10Z","receivedAt":"2016-11-08T20:13:00Z","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\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>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/branch.c         | 234 ++++++++++++++---------------------------------\n t/t3203-branch-output.sh |   2 +-\n t/t6040-tracking-info.sh |   2 +-\n 3 files changed, 70 insertions(+), 168 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex dead2b8..a19e05d 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -36,12 +36,12 @@ static unsigned char head_sha1[20];\n \n static int branch_use_color = -1;\n static char branch_colors[][COLOR_MAXLEN] = {\n-\tGIT_COLOR_RESET,\n-\tGIT_COLOR_NORMAL,\t/* PLAIN */\n-\tGIT_COLOR_RED,\t\t/* REMOTE */\n-\tGIT_COLOR_NORMAL,\t/* LOCAL */\n-\tGIT_COLOR_GREEN,\t/* CURRENT */\n-\tGIT_COLOR_BLUE,\t\t/* UPSTREAM */\n+\t\"%(color:reset)\",\n+\t\"%(color:reset)\",\t/* PLAIN */\n+\t\"%(color:red)\",\t\t/* REMOTE */\n+\t\"%(color:reset)\",\t/* LOCAL */\n+\t\"%(color:green)\",\t/* CURRENT */\n+\t\"%(color:blue)\",\t/* UPSTREAM */\n };\n enum color_branch {\n \tBRANCH_COLOR_RESET = 0,\n@@ -280,162 +280,6 @@ 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-{\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-\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-\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}\n-\tstrbuf_release(&fancy);\n-\tif (added_decoration)\n-\t\tstrbuf_addch(stat, ' ');\n-\tfree(ref);\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-{\n-\tstruct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;\n-\tconst char *sub = _(\" **** invalid ref ****\");\n-\tstruct commit *commit = item->commit;\n-\n-\tif (!parse_commit(commit)) {\n-\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);\n-\t\tsub = subject.buf;\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-}\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-{\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-\n-\tc = ' ';\n-\tif (current) {\n-\t\tc = '*';\n-\t\tcolor = BRANCH_COLOR_CURRENT;\n-\t}\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\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}\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@@ -446,7 +290,12 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\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+\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\tif (it->kind == FILTER_REFS_REMOTES)\n \t\t\tw += remote_bonus;\n@@ -456,12 +305,52 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n \treturn max;\n }\n \n+static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)\n+{\n+\tstruct strbuf fmt = STRBUF_INIT;\n+\tstruct strbuf local = STRBUF_INIT;\n+\tstruct strbuf remote = STRBUF_INIT;\n+\n+\tstrbuf_addf(&fmt, \"%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)\", branch_get_color(BRANCH_COLOR_CURRENT));\n+\n+\tif (filter->verbose) {\n+\t\tstrbuf_addf(&local, \"%%(align:%d,left)%%(refname:strip=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:strip=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,\n+\t\t\t    remote_prefix, branch_get_color(BRANCH_COLOR_RESET));\n+\t} else {\n+\t\tstrbuf_addf(&local, \"%%(refname:strip=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:strip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), remote_prefix, branch_get_color(BRANCH_COLOR_RESET));\n+\t}\n+\n+\tstrbuf_addf(&fmt, \"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\", local.buf, remote.buf);\n+\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 {\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 \n \t/*\n \t * If we are listing more than just remote branches,\n@@ -473,12 +362,14 @@ 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 \t/*\n \t * If no sorting parameter is given then we default to sorting\n \t * by 'refname'. This would give us an alphabetically sorted\n@@ -490,10 +381,21 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin\n \t\tsorting = ref_default_sorting();\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 c6a3ccb..980c732 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -189,7 +189,7 @@ test_expect_success 'local-branch symrefs shortened properly' '\n \tgit symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one &&\n \tcat >expect <<-\\EOF &&\n \t  ref-to-branch -> branch-one\n-\t  ref-to-remote -> refs/remotes/origin/branch-one\n+\t  ref-to-remote -> origin/branch-one\n \tEOF\n \tgit branch >actual.raw &&\n \tgrep ref-to <actual.raw >actual &&\ndiff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh\nindex 3d5c238..97a0765 100755\n--- a/t/t6040-tracking-info.sh\n+++ b/t/t6040-tracking-info.sh\n@@ -44,7 +44,7 @@ b1 [ahead 1, behind 1] d\n b2 [ahead 1, behind 1] d\n b3 [behind 1] b\n b4 [ahead 2] f\n-b5 g\n+b5 [gone] g\n b6 c\n EOF\n \n-- \n2.10.2\n\n"},{"id":"305584","messageId":"20161108201211.25213-16-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 15/17] branch, tag: use porcelain output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:09Z","receivedAt":"2016-11-08T20:13: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\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 be9773a..dead2b8 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -656,6 +656,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 50e4ae5..a00e9a7 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -373,6 +373,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\tsetup_ref_filter_porcelain_msg();\n+\n \tgit_config(git_tag_config, sorting_tail);\n \n \tmemset(&opt, 0, sizeof(opt));\n-- \n2.10.2\n\n"},{"id":"305585","messageId":"20161108201211.25213-18-Karthik.188@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"[PATCH v7 17/17] branch: implement '--format' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-08T20:12:11Z","receivedAt":"2016-11-08T20:13:06Z","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 1fe7344..e5b6f31 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@@ -246,6 +246,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 a19e05d..acadb99 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@@ -343,14 +344,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@@ -367,7 +368,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 \t/*\n@@ -395,7 +397,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@@ -515,6 +517,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \tenum branch_track track;\n \tstruct ref_filter filter;\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@@ -555,6 +558,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tOPTION_CALLBACK, 0, \"points-at\", &filter.points_at, N_(\"object\"),\n \t\t\tN_(\"print only branches of the object\"), 0, parse_opt_object_name\n \t\t},\n+\t\tOPT_STRING(  0 , \"format\", &format, N_(\"format\"), N_(\"format to use for the output\")),\n \t\tOPT_END(),\n \t};\n \n@@ -615,7 +619,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tif ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)\n \t\t\tfilter.kind |= FILTER_REFS_DETACHED_HEAD;\n \t\tfilter.name_patterns = argv;\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 980c732..d8edaf2 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -196,4 +196,18 @@ test_expect_success 'local-branch symrefs shortened properly' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'git branch --format option' '\n+\tcat >expect <<-\\EOF &&\n+\tRefname is (HEAD detached from fromtag)\n+\tRefname is refs/heads/ambiguous\n+\tRefname is refs/heads/branch-one\n+\tRefname is refs/heads/branch-two\n+\tRefname is refs/heads/master\n+\tRefname is refs/heads/ref-to-branch\n+\tRefname is refs/heads/ref-to-remote\n+\tEOF\n+\tgit branch --format=\"Refname is %(refname)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"305592","messageId":"CA+P7+xoAmYqi0OazZux+rooXW+D_N9L5s+2BzUyJJJXtmLZX-Q@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-2-Karthik.188@gmail.com","subject":"Re: [PATCH v7 01/17] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:13:30Z","receivedAt":"2016-11-08T23:14:20Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Implement %(if), %(then) and %(else) atoms. Used as\n> %(if)...%(then)...%(end) or %(if)...%(then)...%(else)...%(end). If the\n> format string between %(if) and %(then) expands to an empty string, or\n> to only whitespaces, then the whole %(if)...%(end) expands to the string\n> following %(then). Otherwise, it expands to the string following\n> %(else), if any. Nesting of this construct is possible.\n>\n> This is in preparation for porting over `git branch -l` to use\n> ref-filter APIs for printing.\n>\n> Add Documentation and tests regarding the same.\n>\n\nOk, so I have only one minor nit, but otherwise this looks quite good\nto me. A few comments explaining my understanding, but only one\nsuggested\nchange which is really a minor nit and not worth re-rolling just for it.\n\nThanks,\nJake\n\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt |  40 +++++++++++\n>  ref-filter.c                       | 133 +++++++++++++++++++++++++++++++++++--\n>  t/t6302-for-each-ref-filter.sh     |  76 +++++++++++++++++++++\n>  3 files changed, 242 insertions(+), 7 deletions(-)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index f57e69b..fed8126 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -146,6 +146,16 @@ align::\n>         quoted, but if nested then only the topmost level performs\n>         quoting.\n>\n> +if::\n> +       Used as %(if)...%(then)...(%end) or\n> +       %(if)...%(then)...%(else)...%(end).  If there is an atom with\n> +       value or string literal after the %(if) then everything after\n> +       the %(then) is printed, else if the %(else) atom is used, then\n> +       everything after %(else) is printed. We ignore space when\n> +       evaluating the string before %(then), this is useful when we\n> +       use the %(HEAD) atom which prints either \"*\" or \" \" and we\n> +       want 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> @@ -181,6 +191,20 @@ 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 (i.e. one of\n> +`--shell`, `--perl`, `--python`, `--tcl` is used), except for opening\n> +atoms, replacement from every %(atom) is quoted when and only when it\n> +appears at the top-level (that is, when it appears outside\n> +%($open)...%(end)).\n> +\n> +When a scripting language specific quoting is in effect, everything\n> +between a top-level opening atom and its matching %(end) is evaluated\n> +according to the semantics of the opening atom and its result is\n> +quoted.\n> +\n>\n\nNice, I like the explanation above.\n\n>  EXAMPLES\n>  --------\n> @@ -268,6 +292,22 @@ eval=`git for-each-ref --shell --format=\"$fmt\" \\\n>  eval \"$eval\"\n>  ------------\n>\n> +\n> +An example to show the usage of %(if)...%(then)...%(else)...%(end).\n> +This prefixes the current branch with a star.\n> +\n> +------------\n> +git for-each-ref --format=\"%(if)%(HEAD)%(then)* %(else)  %(end)%(refname:short)\" refs/heads/\n> +------------\n> +\n> +\n> +An example to show the usage of %(if)...%(then)...%(end).\n> +This prints the authorname, if present.\n> +\n> +------------\n> +git for-each-ref --format=\"%(refname)%(if)%(authorname)%(then) %(color:red)Authored by: %(authorname)%(end)\"\n> +------------\n> +\n>  SEE ALSO\n>  --------\n>  linkgit:git-show-ref[1]\n> diff --git a/ref-filter.c b/ref-filter.c\n> index d4c2931..8c183a0 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -21,6 +21,12 @@ struct align {\n>         unsigned int width;\n>  };\n>\n> +struct if_then_else {\n> +       unsigned int then_atom_seen : 1,\n> +               else_atom_seen : 1,\n> +               condition_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> @@ -203,6 +209,9 @@ static struct {\n>         { \"color\", FIELD_STR, color_atom_parser },\n>         { \"align\", FIELD_STR, align_atom_parser },\n>         { \"end\" },\n> +       { \"if\" },\n> +       { \"then\" },\n> +       { \"else\" },\n>  };\n>\n>  #define REF_FORMATTING_STATE_INIT  { 0, NULL }\n> @@ -210,7 +219,7 @@ static struct {\n>  struct ref_formatting_stack {\n>         struct ref_formatting_stack *prev;\n>         struct strbuf output;\n> -       void (*at_end)(struct ref_formatting_stack *stack);\n> +       void (*at_end)(struct ref_formatting_stack **stack);\n>         void *at_end_data;\n>  };\n>\n> @@ -343,13 +352,14 @@ static void pop_stack_element(struct ref_formatting_stack **stack)\n>         *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\nSo we now have to pass an array of stacks to the end_align_handler\ninstead? Ok. But for align this is simple since it just expects a\nsingleton.\n\n> -       struct align *align = (struct align *)stack->at_end_data;\n> +       struct ref_formatting_stack *cur = *stack;\n> +       struct align *align = (struct align *)cur->at_end_data;\n>         struct strbuf s = STRBUF_INIT;\n>\n> -       strbuf_utf8_align(&s, align->position, align->width, stack->output.buf);\n> -       strbuf_swap(&stack->output, &s);\n> +       strbuf_utf8_align(&s, align->position, align->width, cur->output.buf);\n> +       strbuf_swap(&cur->output, &s);\n>         strbuf_release(&s);\n>  }\n>\n> @@ -363,6 +373,103 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s\n>         new->at_end_data = &atomv->u.align;\n>  }\n>\n> +static void if_then_else_handler(struct ref_formatting_stack **stack)\n> +{\n> +       struct ref_formatting_stack *cur = *stack;\n> +       struct ref_formatting_stack *prev = cur->prev;\n> +       struct if_then_else *if_then_else = (struct if_then_else *)cur->at_end_data;\n> +\n> +       if (!if_then_else->then_atom_seen)\n> +               die(_(\"format: %%(if) atom used without a %%(then) atom\"));\n> +\n> +       if (if_then_else->else_atom_seen) {\n> +               /*\n> +                * There is an %(else) atom: we need to drop one state from the\n> +                * stack, either the %(else) branch if the condition is satisfied, or\n> +                * the %(then) branch if it isn't.\n> +                */\n> +               if (if_then_else->condition_satisfied) {\n> +                       strbuf_reset(&cur->output);\n> +                       pop_stack_element(&cur);\n\nSo here, once we have a satisfied condition, we just drop the \"else\"\nelement entirely.\n\n> +               } else {\n> +                       strbuf_swap(&cur->output, &prev->output);\n> +                       strbuf_reset(&cur->output);\n> +                       pop_stack_element(&cur);\n\nOtherwise, we swap our current value into the value of the previous\nelement, and then drop the current. This is a bit tricky, but it\nworks.\n\n> +               }\n> +       } else if (!if_then_else->condition_satisfied)\n\nMinor nit. I'm not sure what standard we use here at Git, but\ntraditionally, I prefer to see { } blocks on all sections even if only\none of them needs it. (That is, only drop the braces when every\nsection is one line.) It also looks weird with a comment since it\nappears as multiple lines to the reader. I think the braces improve\nreadability.\n\nI don't know whether that's Git's code base standard or not, however.\nIt's not really worth a re-roll unless something else would need to\nchange.\n\n> +               /*\n> +                * No %(else) atom: just drop the %(then) branch if the\n> +                * condition is not satisfied.\n> +                */\n> +               strbuf_reset(&cur->output);\n\nFinally, if no else element, then we just reset the current pointer.\n\n> +\n> +       *stack = cur;\n> +       free(if_then_else);\n> +}\n> +\n> +static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n> +{\n> +       struct ref_formatting_stack *new;\n> +       struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);\n> +\n> +       push_stack_element(&state->stack);\n> +       new = state->stack;\n> +       new->at_end = if_then_else_handler;\n> +       new->at_end_data = if_then_else;\n> +}\n> +\n\nOk, so the new method is that to handle \"if\"s we push the sets onto\nthe stack and check their values. I like this, it makes things pretty\nstraight forward and simple. Allows for quite a bit of expression.\n\n> +static int is_empty(const char *s)\n> +{\n> +       while (*s != '\\0') {\n> +               if (!isspace(*s))\n> +                       return 0;\n> +               s++;\n> +       }\n> +       return 1;\n> +}\n> +\n> +static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n> +{\n> +       struct ref_formatting_stack *cur = state->stack;\n> +       struct if_then_else *if_then_else = NULL;\n> +\n> +       if (cur->at_end == if_then_else_handler)\n> +               if_then_else = (struct if_then_else *)cur->at_end_data;\n> +       if (!if_then_else)\n> +               die(_(\"format: %%(then) atom used without an %%(if) atom\"));\n> +       if (if_then_else->then_atom_seen)\n> +               die(_(\"format: %%(then) atom used more than once\"));\n> +       if (if_then_else->else_atom_seen)\n> +               die(_(\"format: %%(then) atom used after %%(else)\"));\n> +       if_then_else->then_atom_seen = 1;\n> +       /*\n> +        * If there exists non-empty string between the 'if' and\n> +        * 'then' atom then the 'if' condition is satisfied.\n> +        */\n> +       if (cur->output.len && !is_empty(cur->output.buf))\n> +               if_then_else->condition_satisfied = 1;\n> +       strbuf_reset(&cur->output);\n> +}\n\nSo once we have a \"%(then)\" atom, we reset all the accumulated string\ndata we've gotten so far. Simple.\n\n> +\n> +static void else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n> +{\n> +       struct ref_formatting_stack *prev = state->stack;\n> +       struct if_then_else *if_then_else = NULL;\n> +\n> +       if (prev->at_end == if_then_else_handler)\n> +               if_then_else = (struct if_then_else *)prev->at_end_data;\n> +       if (!if_then_else)\n> +               die(_(\"format: %%(else) atom used without an %%(if) atom\"));\n> +       if (!if_then_else->then_atom_seen)\n> +               die(_(\"format: %%(else) atom used without a %%(then) atom\"));\n> +       if (if_then_else->else_atom_seen)\n> +               die(_(\"format: %%(else) atom used more than once\"));\n> +       if_then_else->else_atom_seen = 1;\n> +       push_stack_element(&state->stack);\n> +       state->stack->at_end_data = prev->at_end_data;\n> +       state->stack->at_end = prev->at_end;\n> +}\n\nSo for an else atom, we basically create another stack element on top\nof the current one. Nice.\n\n> +\n>  static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n>  {\n>         struct ref_formatting_stack *current = state->stack;\n> @@ -370,14 +477,17 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta\n>\n>         if (!current->at_end)\n>                 die(_(\"format: %%(end) atom used without corresponding atom\"));\n> -       current->at_end(current);\n> +       current->at_end(&state->stack);\n> +\n> +       /*  Stack may have been popped within at_end(), hence reset the current pointer */\n> +       current = state->stack;\n>\n>         /*\n>          * Perform quote formatting when the stack element is that of\n>          * a supporting atom. If nested then perform quote formatting\n>          * only on the topmost supporting atom.\n>          */\n> -       if (!state->stack->prev->prev) {\n> +       if (!current->prev->prev) {\n>                 quote_formatting(&s, current->output.buf, state->quote_style);\n>                 strbuf_swap(&current->output, &s);\n>         }\n> @@ -1029,6 +1139,15 @@ static void populate_value(struct ref_array_item *ref)\n>                 } else if (!strcmp(name, \"end\")) {\n>                         v->handler = end_atom_handler;\n>                         continue;\n> +               } else if (!strcmp(name, \"if\")) {\n> +                       v->handler = if_atom_handler;\n> +                       continue;\n> +               } else if (!strcmp(name, \"then\")) {\n> +                       v->handler = then_atom_handler;\n> +                       continue;\n> +               } else if (!strcmp(name, \"else\")) {\n> +                       v->handler = else_atom_handler;\n> +                       continue;\n>                 } else\n>                         continue;\n>\n> diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\n> index d0ab09f..fed3013 100755\n> --- a/t/t6302-for-each-ref-filter.sh\n> +++ b/t/t6302-for-each-ref-filter.sh\n> @@ -327,4 +327,80 @@ test_expect_success 'reverse version sort' '\n>         test_cmp expect actual\n>  '\n>\n> +test_expect_success 'improper usage of %(if), %(then), %(else) and %(end) atoms' '\n> +       test_must_fail git for-each-ref --format=\"%(if)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(then) %(end)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(else) %(end)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(if) %(else) %(end)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(if) %(then) %(then) %(end)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(then) %(else) %(end)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(if) %(else) %(end)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(if) %(then) %(else)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(if) %(else) %(then) %(end)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(if) %(then) %(else) %(else) %(end)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(if) %(end)\"\n> +'\n> +\n> +test_expect_success 'check %(if)...%(then)...%(end) atoms' '\n> +       git for-each-ref --format=\"%(refname)%(if)%(authorname)%(then) Author: %(authorname)%(end)\" >actual &&\n> +       cat >expect <<-\\EOF &&\n> +       refs/heads/master Author: A U Thor\n> +       refs/heads/side Author: A U Thor\n> +       refs/odd/spot Author: A U Thor\n> +       refs/tags/annotated-tag\n> +       refs/tags/doubly-annotated-tag\n> +       refs/tags/doubly-signed-tag\n> +       refs/tags/foo1.10 Author: A U Thor\n> +       refs/tags/foo1.3 Author: A U Thor\n> +       refs/tags/foo1.6 Author: A U Thor\n> +       refs/tags/four Author: A U Thor\n> +       refs/tags/one Author: A U Thor\n> +       refs/tags/signed-tag\n> +       refs/tags/three Author: A U Thor\n> +       refs/tags/two Author: A U Thor\n> +       EOF\n> +       test_cmp expect actual\n> +'\n> +\n> +test_expect_success 'check %(if)...%(then)...%(else)...%(end) atoms' '\n> +       git for-each-ref --format=\"%(if)%(authorname)%(then)%(authorname)%(else)No author%(end): %(refname)\" >actual &&\n> +       cat >expect <<-\\EOF &&\n> +       A U Thor: refs/heads/master\n> +       A U Thor: refs/heads/side\n> +       A U Thor: refs/odd/spot\n> +       No author: refs/tags/annotated-tag\n> +       No author: refs/tags/doubly-annotated-tag\n> +       No author: refs/tags/doubly-signed-tag\n> +       A U Thor: refs/tags/foo1.10\n> +       A U Thor: refs/tags/foo1.3\n> +       A U Thor: refs/tags/foo1.6\n> +       A U Thor: refs/tags/four\n> +       A U Thor: refs/tags/one\n> +       No author: refs/tags/signed-tag\n> +       A U Thor: refs/tags/three\n> +       A U Thor: refs/tags/two\n> +       EOF\n> +       test_cmp expect actual\n> +'\n> +test_expect_success 'ignore spaces in %(if) atom usage' '\n> +       git for-each-ref --format=\"%(refname:short): %(if)%(HEAD)%(then)Head ref%(else)Not Head ref%(end)\" >actual &&\n> +       cat >expect <<-\\EOF &&\n> +       master: Head ref\n> +       side: Not Head ref\n> +       odd/spot: Not Head ref\n> +       annotated-tag: Not Head ref\n> +       doubly-annotated-tag: Not Head ref\n> +       doubly-signed-tag: Not Head ref\n> +       foo1.10: Not Head ref\n> +       foo1.3: Not Head ref\n> +       foo1.6: Not Head ref\n> +       four: Not Head ref\n> +       one: Not Head ref\n> +       signed-tag: Not Head ref\n> +       three: Not Head ref\n> +       two: Not Head ref\n> +       EOF\n> +       test_cmp expect actual\n> +'\n> +\n>  test_done\n> --\n> 2.10.2\n>\n"},{"id":"305593","messageId":"CA+P7+xqVAEuc12eQJ7-Et0CtXc12t5zDUg3Qsh3214cTh_brbA@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-3-Karthik.188@gmail.com","subject":"Re: [PATCH v7 02/17] ref-filter: include reference to 'used_atom' within 'atom_value'","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:16:17Z","receivedAt":"2016-11-08T23:16:42Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Ensure that each 'atom_value' has a reference to its corresponding\n> 'used_atom'. This let's us use values within 'used_atom' in the\n> 'handler' function.\n>\n> Hence we can get the %(align) atom's parameters directly from the\n> 'used_atom' therefore removing the necessity of passing %(align) atom's\n> parameters to 'atom_value'.\n>\n> This also acts as a preparatory patch for the upcoming patch where we\n> introduce %(if:equals=) and %(if:notequals=).\n>\n\nMakes sense.\n\n> Signed-off-by: Karthik Nayak <Karthik.188@gmail.com>\n> ---\n>  ref-filter.c | 8 +++-----\n>  1 file changed, 3 insertions(+), 5 deletions(-)\n>\n> diff --git a/ref-filter.c b/ref-filter.c\n> index 8c183a0..8392303 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -230,11 +230,9 @@ struct ref_formatting_state {\n>\n>  struct atom_value {\n>         const char *s;\n> -       union {\n> -               struct align align;\n> -       } u;\n>         void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);\n>         unsigned long ul; /* used for sorting when not FIELD_STR */\n> +       struct used_atom *atom;\n>  };\n>\n>  /*\n> @@ -370,7 +368,7 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s\n>         push_stack_element(&state->stack);\n>         new = state->stack;\n>         new->at_end = end_align_handler;\n> -       new->at_end_data = &atomv->u.align;\n> +       new->at_end_data = &atomv->atom->u.align;\n\nAt first, this confused me. I was like \"we dropped the union, why are\nwe still referencing it. But I realized that the \"used_atom\" struct\nactually contains the same union and we were copying it.\n\nOk, so this looks good.\n\nThanks,\nJake\n\n>  }\n>\n>  static void if_then_else_handler(struct ref_formatting_stack **stack)\n> @@ -1069,6 +1067,7 @@ static void populate_value(struct ref_array_item *ref)\n>                 struct branch *branch = NULL;\n>\n>                 v->handler = append_atom;\n> +               v->atom = atom;\n>\n>                 if (*name == '*') {\n>                         deref = 1;\n> @@ -1133,7 +1132,6 @@ static void populate_value(struct ref_array_item *ref)\n>                                 v->s = \" \";\n>                         continue;\n>                 } else if (starts_with(name, \"align\")) {\n> -                       v->u.align = atom->u.align;\n>                         v->handler = align_atom_handler;\n>                         continue;\n>                 } else if (!strcmp(name, \"end\")) {\n> --\n> 2.10.2\n>\n"},{"id":"305594","messageId":"CA+P7+xoavgQ=NK8GGGXf90ESuPs+mFwHzEWbsnjWjZ4V7TWpjw@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-4-Karthik.188@gmail.com","subject":"Re: [PATCH v7 03/17] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:22:52Z","receivedAt":"2016-11-08T23:23:18Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Implement %(if:equals=<string>) wherein the if condition is only\n> satisfied if the value obtained between the %(if:...) and %(then) atom\n> is the same as the given '<string>'.\n>\n> Similarly, implement (if:notequals=<string>) wherein the if condition\n> is only satisfied if the value obtained between the %(if:...) and\n> %(then) atom is differnt from the given '<string>'.\n>\n> This is done by introducing 'if_atom_parser()' which parses the given\n> %(if) atom and then stores the data in used_atom which is later passed\n> on to the used_atom of the %(then) atom, so that it can do the required\n> comparisons.\n>\n\nOk. How does this handle whitespace? The previous if implementation\ntreated whitespace as trimming to ignore. Does this require an exact\nwhitespace match? It appears by the code that strings must match\nexactly. Would it make more sense to always trim the value of\nwhitespace first before comparison? Hmm.. I think we should avoid\ndoing that actually.\n\nOtherwise this looks good. I might have implemented the storage as one\nvalue and then a boolean indicating whether to check for equality or\ninequality. But I think the current implementation is ok too, and is a\nbit more elegant on the code savings.\n\nThanks,\nJake\n\n> Add tests and Documentation for the same.\n>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt |  3 +++\n>  ref-filter.c                       | 43 +++++++++++++++++++++++++++++++++-----\n>  t/t6302-for-each-ref-filter.sh     | 18 ++++++++++++++++\n>  3 files changed, 59 insertions(+), 5 deletions(-)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index fed8126..b7b8560 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -155,6 +155,9 @@ if::\n>         evaluating the string before %(then), this is useful when we\n>         use the %(HEAD) atom which prints either \"*\" or \" \" and we\n>         want to apply the 'if' condition only on the 'HEAD' ref.\n> +       Append \":equals=<string>\" or \":notequals=<string>\" to compare\n> +       the value between the %(if:...) and %(then) atoms with the\n> +       given string.\n>\n\nThis seems to imply that it does not ignore whitespace. Ok.\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> diff --git a/ref-filter.c b/ref-filter.c\n> index 8392303..44481c3 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -22,6 +22,8 @@ struct align {\n>  };\n>\n>  struct if_then_else {\n> +       const char *if_equals,\n> +               *not_equals;\n\nOk so we add both if_equals and not_equals values. Could we re-use the\nsame string?\n\n>         unsigned int then_atom_seen : 1,\n>                 else_atom_seen : 1,\n>                 condition_satisfied : 1;\n> @@ -49,6 +51,10 @@ static struct used_atom {\n>                         enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n>                         unsigned int nlines;\n>                 } contents;\n> +               struct {\n> +                       const char *if_equals,\n> +                               *not_equals;\n\n\nSame here, why do we need both strings here stored separately? Could\nwe instead store which state to check and store the string once? I'm\nnot sure that really buys us any storage.\n\n> +               } if_then_else;\n>                 enum { O_FULL, O_SHORT } objectname;\n>         } u;\n>  } *used_atom;\n> @@ -169,6 +175,19 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)\n>         string_list_clear(&params, 0);\n>  }\n>\n> +static void if_atom_parser(struct used_atom *atom, const char *arg)\n> +{\n> +       if (!arg)\n> +               return;\n> +       else if (skip_prefix(arg, \"equals=\", &atom->u.if_then_else.if_equals))\n> +                ;\n> +       else if (skip_prefix(arg, \"notequals=\", &atom->u.if_then_else.not_equals))\n> +               ;\n\nOk so we can't ever have if_equals or not_equals at the same time.\n\n> +       else\n> +               die(_(\"unrecognized %%(if) argument: %s\"), arg);\n> +}\n> +\n> +\n>  static struct {\n>         const char *name;\n>         cmp_type cmp_type;\n> @@ -209,7 +228,7 @@ static struct {\n>         { \"color\", FIELD_STR, color_atom_parser },\n>         { \"align\", FIELD_STR, align_atom_parser },\n>         { \"end\" },\n> -       { \"if\" },\n> +       { \"if\", FIELD_STR, if_atom_parser },\n>         { \"then\" },\n>         { \"else\" },\n>  };\n> @@ -410,6 +429,9 @@ static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_stat\n>         struct ref_formatting_stack *new;\n>         struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);\n>\n> +       if_then_else->if_equals = atomv->atom->u.if_then_else.if_equals;\n> +       if_then_else->not_equals = atomv->atom->u.if_then_else.not_equals;\n> +\n>         push_stack_element(&state->stack);\n>         new = state->stack;\n>         new->at_end = if_then_else_handler;\n> @@ -441,10 +463,17 @@ static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_st\n>                 die(_(\"format: %%(then) atom used after %%(else)\"));\n>         if_then_else->then_atom_seen = 1;\n>         /*\n> -        * If there exists non-empty string between the 'if' and\n> -        * 'then' atom then the 'if' condition is satisfied.\n> +        * If the 'equals' or 'notequals' attribute is used then\n> +        * perform the required comparison. If not, only non-empty\n> +        * strings satisfy the 'if' condition.\n>          */\n> -       if (cur->output.len && !is_empty(cur->output.buf))\n> +       if (if_then_else->if_equals) {\n> +               if (!strcmp(if_then_else->if_equals, cur->output.buf))\n> +                       if_then_else->condition_satisfied = 1;\n\nOk so if somehow we end up with both set, if_equals takes precedence.\nI think I can be ok with the way we handle this.\n\n> +       } else  if (if_then_else->not_equals) {\n> +               if (strcmp(if_then_else->not_equals, cur->output.buf))\n> +                       if_then_else->condition_satisfied = 1;\n> +       } else if (cur->output.len && !is_empty(cur->output.buf))\n>                 if_then_else->condition_satisfied = 1;\n>         strbuf_reset(&cur->output);\n>  }\n> @@ -1137,7 +1166,11 @@ static void populate_value(struct ref_array_item *ref)\n>                 } else if (!strcmp(name, \"end\")) {\n>                         v->handler = end_atom_handler;\n>                         continue;\n> -               } else if (!strcmp(name, \"if\")) {\n> +               } else if (starts_with(name, \"if\")) {\n> +                       const char *s;\n> +\n> +                       if (skip_prefix(name, \"if:\", &s))\n> +                               v->s = xstrdup(s);\n>                         v->handler = if_atom_handler;\n>                         continue;\n>                 } else if (!strcmp(name, \"then\")) {\n> diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\n> index fed3013..a09a1a4 100755\n> --- a/t/t6302-for-each-ref-filter.sh\n> +++ b/t/t6302-for-each-ref-filter.sh\n> @@ -403,4 +403,22 @@ test_expect_success 'ignore spaces in %(if) atom usage' '\n>         test_cmp expect actual\n>  '\n>\n> +test_expect_success 'check %(if:equals=<string>)' '\n> +       git for-each-ref --format=\"%(if:equals=master)%(refname:short)%(then)Found master%(else)Not master%(end)\" refs/heads/ >actual &&\n> +       cat >expect <<-\\EOF &&\n> +       Found master\n> +       Not master\n> +       EOF\n> +       test_cmp expect actual\n> +'\n> +\n> +test_expect_success 'check %(if:notequals=<string>)' '\n> +       git for-each-ref --format=\"%(if:notequals=master)%(refname:short)%(then)Not master%(else)Found master%(end)\" refs/heads/ >actual &&\n> +       cat >expect <<-\\EOF &&\n> +       Found master\n> +       Not master\n> +       EOF\n> +       test_cmp expect actual\n> +'\n> +\n>  test_done\n> --\n> 2.10.2\n>\n"},{"id":"305595","messageId":"CA+P7+xqG5ACT2ZB=M8gLmDaYgSgX202+afJmZJKat6NzbFzL=g@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-5-Karthik.188@gmail.com","subject":"Re: [PATCH v7 04/17] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:27:54Z","receivedAt":"2016-11-08T23:28:21Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Add support for %(objectname:short=<length>) which would print the\n> abbreviated unique objectname of given length. When no length is\n> specified, the length is 'DEFAULT_ABBREV'. The minimum length is\n> 'MINIMUM_ABBREV'. The length may be exceeded to ensure that the provided\n> object name is unique.\n>\n\nOk this makes sense. It may be annoying that the length might go\nbeyond the size that we wanted, but I think it's better than printing\na non-unique short abbreviation.\n\nI have one suggested change, which is to drop O_LENGTH and have\nO_SHORT store the length always, setting it to DEFAULT_ABBREV when no\nlength provided. This allows you to drop some code. I don't think it's\nactually worth a re-roll by itself since the current code is correct.\n\nThanks,\nJake\n\n> Add tests and documentation for the same.\n>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Helped-by: Jacob Keller <jacob.keller@gmail.com>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt |  4 ++++\n>  ref-filter.c                       | 25 +++++++++++++++++++------\n>  t/t6300-for-each-ref.sh            | 10 ++++++++++\n>  3 files changed, 33 insertions(+), 6 deletions(-)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index b7b8560..92184c4 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -107,6 +107,10 @@ objectsize::\n>  objectname::\n>         The object name (aka SHA-1).\n>         For a non-ambiguous abbreviation of the object name append `:short`.\n> +       For an abbreviation of the object name with desired length append\n> +       `:short=<length>`, where the minimum length is MINIMUM_ABBREV. The\n> +       length may be exceeded to ensure unique object names.\n> +\n>\n>  upstream::\n>         The name of a local ref which can be considered ``upstream''\n> diff --git a/ref-filter.c b/ref-filter.c\n> index 44481c3..fe4ea2b 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -55,7 +55,10 @@ static struct used_atom {\n>                         const char *if_equals,\n>                                 *not_equals;\n>                 } if_then_else;\n> -               enum { O_FULL, O_SHORT } objectname;\n> +               struct {\n> +                       enum { O_FULL, O_LENGTH, O_SHORT } option;\n> +                       unsigned int length;\n> +               } objectname;\n>         } u;\n>  } *used_atom;\n>  static int used_atom_cnt, need_tagged, need_symref;\n> @@ -118,10 +121,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>         if (!arg)\n> -               atom->u.objectname = O_FULL;\n> +               atom->u.objectname.option = O_FULL;\n>         else if (!strcmp(arg, \"short\"))\n> -               atom->u.objectname = O_SHORT;\n> -       else\n> +               atom->u.objectname.option = O_SHORT;\n> +       else if (skip_prefix(arg, \"short=\", &arg)) {\n> +               atom->u.objectname.option = O_LENGTH;\n> +               if (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n> +                   atom->u.objectname.length == 0)\n> +                       die(_(\"positive value expected objectname:short=%s\"), arg);\n> +               if (atom->u.objectname.length < MINIMUM_ABBREV)\n> +                       atom->u.objectname.length = MINIMUM_ABBREV;\n\nOne way to reduce some code is to set O_SHORT and O_LENGTH as the same\n(either O_SHORT or O_LENGTH) and when no length is found simply set it\nto the DEFAULT_ABBREV.\n\n> +       } else\n>                 die(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n>  }\n>\n> @@ -591,12 +601,15 @@ static int grab_objectname(const char *name, const unsigned char *sha1,\n>                            struct atom_value *v, struct used_atom *atom)\n>  {\n>         if (starts_with(name, \"objectname\")) {\n> -               if (atom->u.objectname == O_SHORT) {\n> +               if (atom->u.objectname.option == O_SHORT) {\n>                         v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));\n>                         return 1;\n\nThat would allow dropping an entire section here.\n\nI don't think this is worth a re-roll by itself, and I think either\napproach is probably ok.\n\n> -               } else if (atom->u.objectname == O_FULL) {\n> +               } else if (atom->u.objectname.option == O_FULL) {\n>                         v->s = xstrdup(sha1_to_hex(sha1));\n>                         return 1;\n> +               } else if (atom->u.objectname.option == O_LENGTH) {\n> +                       v->s = xstrdup(find_unique_abbrev(sha1, atom->u.objectname.length));\n> +                       return 1;\n>                 } else\n>                         die(\"BUG: unknown %%(objectname) option\");\n>         }\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index 19a2823..2be0a3f 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>         test_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> +       test_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n> +'\n> +\n>  test_date () {\n>         f=$1 &&\n>         committer_date=$2 &&\n> --\n> 2.10.2\n>\n"},{"id":"305596","messageId":"CA+P7+xo4M5g15B7zKzC-zJvd6O0Wfpks142-+C8tAqTfyEJ+nA@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-6-Karthik.188@gmail.com","subject":"Re: [PATCH v7 05/17] ref-filter: move get_head_description() from branch.c","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:31:41Z","receivedAt":"2016-11-08T23:32:06Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Move the implementation of get_head_description() from branch.c to\n> ref-filter.  This gives a description of the HEAD ref if called. This\n> is used as the refname for the HEAD ref whenever the\n> FILTER_REFS_DETACHED_HEAD option is used. Make it public because we\n> need it to calculate the length of the HEAD refs description in\n> branch.c:calc_maxwidth() when we port branch.c to use ref-filter\n> APIs.\n>\n\nMakes sense.\n\n>\n> -               if (starts_with(name, \"refname\"))\n> +               if (starts_with(name, \"refname\")) {\n>                         refname = ref->refname;\n> -               else if (starts_with(name, \"symref\"))\n> +                       if (ref->kind & FILTER_REFS_DETACHED_HEAD)\n> +                               refname = get_head_description();\n\nSince this (I think?) changes behavior of refname would it make sense\nto add a test for this?\n\nThanks,\nJake\n\n> +               } else if (starts_with(name, \"symref\"))\n>                         refname = ref->symref ? ref->symref : \"\";\n>                 else if (starts_with(name, \"upstream\")) {\n>                         const char *branch_name;\n> diff --git a/ref-filter.h b/ref-filter.h\n> index 14d435e..4aea594 100644\n> --- a/ref-filter.h\n> +++ b/ref-filter.h\n> @@ -106,5 +106,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> --\n> 2.10.2\n>\n"},{"id":"305597","messageId":"CA+P7+xo=QSgBPtLaZx1e+GujjtTop7LaTwsRJ5aMD3JJ3bqBMw@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-7-Karthik.188@gmail.com","subject":"Re: [PATCH v7 06/17] ref-filter: introduce format_ref_array_item()","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:32:59Z","receivedAt":"2016-11-08T23:33:24Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> To allow column display, we will need to first render the output in a\n> string list to allow print_columns() to compute the proper size of\n> each column before starting the actual output. Introduce the function\n> format_ref_array_item() that does the formatting of a ref_array_item\n> to an strbuf.\n>\n\nMakes sense.\n\nThanks,\nJake\n"},{"id":"305598","messageId":"CA+P7+xoKxGiNJO4g=Nm+KY=5=u8g2edoLOoe5K0ZAPrvoGj-Ug@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-8-Karthik.188@gmail.com","subject":"Re: [PATCH v7 07/17] ref-filter: make %(upstream:track) prints \"[gone]\" for invalid upstreams","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:37:47Z","receivedAt":"2016-11-08T23:38:13Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Borrowing from branch.c's implementation print \"[gone]\" whenever an\n> unknown upstream ref is encountered instead of just ignoring it.\n>\n\nThis makes sense.\n\n> This makes sure that when branch.c is ported over to using ref-filter\n> APIs for printing, this feature is not lost.\n>\n\nRight.\n\n> Make changes to t/t6300-for-each-ref.sh and\n> Documentation/git-for-each-ref.txt to reflect this change.\n>\n\nThis will change behavior if people were expecting it to remain\nsilent, but I think this could be considered a bug.\n\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Helped-by : Jacob Keller <jacob.keller@gmail.com>\n> Signed-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>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index 92184c4..fd365eb 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -119,7 +119,8 @@ upstream::\n>         \"[ahead N, behind M]\" and `:trackshort` to show the terse\n>         version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n>         or \"=\" (in sync).  Has no effect if the ref does not have\n> -       tracking information associated with it.\n> +       tracking information associated with it. `:track` also prints\n> +       \"[gone]\" whenever unknown upstream ref is encountered.\n>\n\nI think this is poorly worded. If I understand, \"has no effect if the\nref does not have tracking information\" so in that case we still print\nnothing, right? but otherwise we print [gone] only when the upstream\nref no longer actually exists locally? I wonder if there is a better\nwording for this? I don't have one. Any suggestions to avoid confusing\nthese two cases?\n\n>  push::\n>         The name of a local ref which represents the `@{push}` location\n> diff --git a/ref-filter.c b/ref-filter.c\n> index b8b8a95..6d51b80 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -1049,8 +1049,10 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n>                 *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n>         else if (atom->u.remote_ref == RR_TRACK) {\n>                 if (stat_tracking_info(branch, &num_ours,\n> -                                      &num_theirs, NULL))\n> +                                      &num_theirs, NULL)) {\n> +                       *s = \"[gone]\";\n>                         return;\n> +               }\n>\n>                 if (!num_ours && !num_theirs)\n>                         *s = \"\";\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index 2be0a3f..a92b36f 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>         cat >expected <<-\\EOF &&\n> -\n> +       [gone]\n>\n>         EOF\n>         test_when_finished \"git config branch.master.merge refs/heads/master\" &&\n> --\n> 2.10.2\n>\n"},{"id":"305599","messageId":"CA+P7+xrm-MMRa9RuaHUGNWJmH8UcYPj3RZCsy1uvVRMuLeP0ZQ@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-9-Karthik.188@gmail.com","subject":"Re: [PATCH v7 08/17] ref-filter: add support for %(upstream:track,nobracket)","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:45:31Z","receivedAt":"2016-11-08T23:45:57Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Add support for %(upstream:track,nobracket) which will print the\n> tracking information without the brackets (i.e. \"ahead N, behind M\").\n> This is needed when we port branch.c to use ref-filter's printing APIs.\n>\n\nMakes sense. Seems a bit weird that we have the brackets normally\nrather than adding them as an option, but I think this is ok. We don't\nwant to change all previous uses in this case.\n\nMy only suggestion here would be to add code so that the options die()\nwhen we use nobracket along with trackshort or without track. This\nensures that the nobracket option only applies to track mode?\n\n> Add test and documentation for the same.\n>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt |  8 +++--\n>  ref-filter.c                       | 67 +++++++++++++++++++++++++-------------\n>  t/t6300-for-each-ref.sh            |  2 ++\n>  3 files changed, 51 insertions(+), 26 deletions(-)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index fd365eb..3953431 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -118,9 +118,11 @@ upstream::\n>         `refname` above.  Additionally respects `:track` to show\n>         \"[ahead N, behind M]\" and `:trackshort` to show the terse\n>         version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n> -       or \"=\" (in sync).  Has no effect if the ref does not have\n> -       tracking information associated with it. `:track` also prints\n> -       \"[gone]\" whenever unknown upstream ref is encountered.\n> +       or \"=\" (in sync). `:track` also prints \"[gone]\" whenever\n> +       unknown upstream ref is encountered. Append `:track,nobracket`\n> +       to show tracking information without brackets (i.e \"ahead N,\n> +       behind M\").  Has no effect if the ref does not have tracking\n> +       information associated with it.\n>\n\nOk so my comment on the previous patch is fixed here, the new wording\nmakes it much more clear that [gone] is not the same thing as no\ninformation. So I don't think we should bother changing the previous\npatch in the series. This might want to document that nobracket works\neven without track, even if it doesn't actually do anything? Or make\nthe code more strict in that we die() if the values are put together\nthat make no sense?\n\n>  push::\n>         The name of a local ref which represents the `@{push}` location\n> diff --git a/ref-filter.c b/ref-filter.c\n> index 6d51b80..4d7e414 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -46,8 +46,10 @@ static struct used_atom {\n>         union {\n>                 char color[COLOR_MAXLEN];\n>                 struct align align;\n> -               enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }\n> -                       remote_ref;\n> +               struct {\n> +                       enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option;\n> +                       unsigned int nobracket: 1;\n> +               } remote_ref;\n>                 struct {\n>                         enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n>                         unsigned int nlines;\n> @@ -75,16 +77,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> -       if (!arg)\n> -               atom->u.remote_ref = RR_NORMAL;\n> -       else if (!strcmp(arg, \"short\"))\n> -               atom->u.remote_ref = RR_SHORTEN;\n> -       else if (!strcmp(arg, \"track\"))\n> -               atom->u.remote_ref = RR_TRACK;\n> -       else if (!strcmp(arg, \"trackshort\"))\n> -               atom->u.remote_ref = RR_TRACKSHORT;\n> -       else\n> -               die(_(\"unrecognized format: %%(%s)\"), atom->name);\n> +       struct string_list params = STRING_LIST_INIT_DUP;\n> +       int i;\n> +\n> +       if (!arg) {\n> +               atom->u.remote_ref.option = RR_NORMAL;\n> +               return;\n> +       }\n> +\n> +       atom->u.remote_ref.nobracket = 0;\n> +       string_list_split(&params, arg, ',', -1);\n> +\n> +       for (i = 0; i < params.nr; i++) {\n> +               const char *s = params.items[i].string;\n> +\n> +               if (!strcmp(s, \"short\"))\n> +                       atom->u.remote_ref.option = RR_SHORTEN;\n> +               else if (!strcmp(s, \"track\"))\n\nShould we add die()s here to disallow setting the remote_ref option\nmultiple times? Otherwise, we should document that the last one wins?\nNot sure it's really a big deal here, but we could do it to ensure\nconsistency for options which don't make sense together?\n\n> +                       atom->u.remote_ref.option = RR_TRACK;\n> +               else if (!strcmp(s, \"trackshort\"))\n> +                       atom->u.remote_ref.option = RR_TRACKSHORT;\n> +               else if (!strcmp(s, \"nobracket\"))\n> +                       atom->u.remote_ref.nobracket = 1;\n> +               else\n> +                       die(_(\"unrecognized format: %%(%s)\"), atom->name);\n> +       }\n> +\n> +       string_list_clear(&params, 0);\n>  }\n>\n>  static void body_atom_parser(struct used_atom *atom, const char *arg)\n> @@ -1045,25 +1064,27 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n>                                     struct branch *branch, const char **s)\n>  {\n>         int num_ours, num_theirs;\n> -       if (atom->u.remote_ref == RR_SHORTEN)\n> +       if (atom->u.remote_ref.option == RR_SHORTEN)\n>                 *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n> -       else if (atom->u.remote_ref == RR_TRACK) {\n> +       else if (atom->u.remote_ref.option == RR_TRACK) {\n>                 if (stat_tracking_info(branch, &num_ours,\n>                                        &num_theirs, NULL)) {\n> -                       *s = \"[gone]\";\n> -                       return;\n> -               }\n> -\n> -               if (!num_ours && !num_theirs)\n> +                       *s = xstrdup(\"gone\");\n> +               } else if (!num_ours && !num_theirs)\n>                         *s = \"\";\n>                 else if (!num_ours)\n> -                       *s = xstrfmt(\"[behind %d]\", num_theirs);\n> +                       *s = xstrfmt(\"behind %d\", num_theirs);\n>                 else if (!num_theirs)\n> -                       *s = xstrfmt(\"[ahead %d]\", num_ours);\n> +                       *s = xstrfmt(\"ahead %d\", num_ours);\n>                 else\n> -                       *s = xstrfmt(\"[ahead %d, behind %d]\",\n> +                       *s = xstrfmt(\"ahead %d, behind %d\",\n>                                      num_ours, num_theirs);\n> -       } else if (atom->u.remote_ref == RR_TRACKSHORT) {\n> +               if (!atom->u.remote_ref.nobracket && *s[0]) {\n> +                       const char *to_free = *s;\n> +                       *s = xstrfmt(\"[%s]\", *s);\n> +                       free((void *)to_free);\n> +               }\n> +       } else if (atom->u.remote_ref.option == RR_TRACKSHORT) {\n>                 if (stat_tracking_info(branch, &num_ours,\n>                                        &num_theirs, NULL))\n>                         return;\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index a92b36f..2c5f177 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> --\n> 2.10.2\n>\n"},{"id":"305600","messageId":"CA+P7+xoRgHiCxMs1+AT8jHg-mRS8ap3KyKV_jCaoRa_ikaBH4Q@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-10-Karthik.188@gmail.com","subject":"Re: [PATCH v7 09/17] ref-filter: make \"%(symref)\" atom work with the ':short' modifier","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:46:44Z","receivedAt":"2016-11-08T23:47:34Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> The \"%(symref)\" atom doesn't work when used with the ':short' modifier\n> because we strictly match only 'symref' for setting the 'need_symref'\n> indicator. Fix this by using comparing with valid_atom rather than used_atom.\n>\n\nMakes sense.\n\n> Add tests for %(symref) and %(symref:short) while we're here.\n>\n\nNice to see more tests around this.\n\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Signed-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>\n> diff --git a/ref-filter.c b/ref-filter.c\n> index 4d7e414..5666814 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -338,7 +338,7 @@ int parse_ref_filter_atom(const char *atom, const char *ep)\n>                 valid_atom[i].parser(&used_atom[at], arg);\n>         if (*atom == '*')\n>                 need_tagged = 1;\n> -       if (!strcmp(used_atom[at].name, \"symref\"))\n> +       if (!strcmp(valid_atom[i].name, \"symref\"))\n>                 need_symref = 1;\n>         return at;\n>  }\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index 2c5f177..b06ea1c 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>         case \"$1\" in\n>                 head) ref=refs/heads/master ;;\n>                  tag) ref=refs/tags/testtag ;;\n> +                sym) ref=refs/heads/sym ;;\n>                    *) ref=$1 ;;\n>         esac\n>         printf '%s\\n' \"$3\" >expected\n> @@ -565,4 +566,27 @@ test_expect_success 'Verify sort with multiple keys' '\n>                 refs/tags/bogo refs/tags/master > actual &&\n>         test_cmp expected actual\n>  '\n> +\n> +test_expect_success 'Add symbolic ref for the following tests' '\n> +       git 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> +       git for-each-ref --format=\"%(symref)\" refs/heads/sym > actual &&\n> +       test_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> +       git for-each-ref --format=\"%(symref:short)\" refs/heads/sym > actual &&\n> +       test_cmp expected actual\n> +'\n> +\n>  test_done\n> --\n> 2.10.2\n>\n"},{"id":"305601","messageId":"CA+P7+xrHRMZemNfo3gjJryBRR5b4T8VT3t9_CBE5hJJfv+D3PQ@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-12-Karthik.188@gmail.com","subject":"Re: [PATCH v7 11/17] ref-filter: introduce symref_atom_parser() and refname_atom_parser()","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:52:03Z","receivedAt":"2016-11-08T23:52:32Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Using refname_atom_parser_internal(), introduce symref_atom_parser() and\n> refname_atom_parser() which will parse the atoms %(symref) and\n> %(refname) respectively. Store the parsed information into the\n> 'used_atom' structure based on the modifiers used along with the atoms.\n>\n> Now the '%(symref)' atom supports the ':strip' atom modifier. Update the\n> Documentation and tests to reflect this.\n>\n\nOne minor nit is that the first part is actually identical so I wonder\nif it's worth having two separate functions?\n\nThanks,\nJake\n\n> Helped-by: Jeff King <peff@peff.net>\n> Signed-off-by: Karthik Nayak <Karthik.188@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt |  5 +++\n>  ref-filter.c                       | 78 ++++++++++++++++++++++----------------\n>  t/t6300-for-each-ref.sh            |  9 +++++\n>  3 files changed, 59 insertions(+), 33 deletions(-)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index 3953431..a669a32 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -166,6 +166,11 @@ if::\n>         the value between the %(if:...) and %(then) atoms with the\n>         given string.\n>\n> +symref::\n> +       The ref which the given symbolic ref refers to. If not a\n> +       symbolic ref, nothing is printed. Respects the `:short` and\n> +       `:strip` options in the same way as `refname` above.\n> +\n>  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> diff --git a/ref-filter.c b/ref-filter.c\n> index aad537d..f1d27b5 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -176,6 +176,16 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg)\n>                 die(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n>  }\n>\n> +static void symref_atom_parser(struct used_atom *atom, const char *arg)\n> +{\n> +       return refname_atom_parser_internal(&atom->u.refname, arg, atom->name);\n> +}\n> +\n> +static void refname_atom_parser(struct used_atom *atom, const char *arg)\n> +{\n> +       return refname_atom_parser_internal(&atom->u.refname, arg, atom->name);\n> +}\n> +\n\nWhat's the reasoning for using separate functions here if they are\nexactly identical except for name? Do we intend to add separate\noptions for this? I don't really have a problem with separate\nfunctions here since it helps avoid confusion but they are identical\notherwise...\n\n>  static align_type parse_align_position(const char *s)\n>  {\n>         if (!strcmp(s, \"right\"))\n> @@ -244,7 +254,7 @@ static struct {\n>         cmp_type cmp_type;\n>         void (*parser)(struct used_atom *atom, const char *arg);\n>  } valid_atom[] = {\n> -       { \"refname\" },\n> +       { \"refname\" , FIELD_STR, refname_atom_parser },\n>         { \"objecttype\" },\n>         { \"objectsize\", FIELD_ULONG },\n>         { \"objectname\", FIELD_STR, objectname_atom_parser },\n> @@ -273,7 +283,7 @@ static struct {\n>         { \"contents\", FIELD_STR, contents_atom_parser },\n>         { \"upstream\", FIELD_STR, remote_ref_atom_parser },\n>         { \"push\", FIELD_STR, remote_ref_atom_parser },\n> -       { \"symref\" },\n> +       { \"symref\", FIELD_STR, symref_atom_parser },\n>         { \"flag\" },\n>         { \"HEAD\" },\n>         { \"color\", FIELD_STR, color_atom_parser },\n> @@ -1058,21 +1068,16 @@ static inline char *copy_advance(char *dst, const char *src)\n>         return 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> -       char *end;\n> -       long nr = strtol(nr_arg, &end, 10);\n> -       long remaining = nr;\n> +       long remaining = len;\n>         const char *start = refname;\n>\n> -       if (nr < 1 || *end != '\\0')\n> -               die(_(\":strip= requires a positive integer argument\"));\n> -\n>         while (remaining) {\n>                 switch (*start++) {\n>                 case '\\0':\n> -                       die(_(\"ref '%s' does not have %ld components to :strip\"),\n> -                           refname, nr);\n> +                       die(_(\"ref '%s' does not have %ud components to :strip\"),\n> +                           refname, len);\n>                 case '/':\n>                         remaining--;\n>                         break;\n> @@ -1081,6 +1086,16 @@ static const char *strip_ref_components(const char *refname, const char *nr_arg)\n>         return start;\n>  }\n>\n> +static const char *show_ref(struct refname_atom *atom, const char *refname)\n> +{\n> +       if (atom->option == R_SHORT)\n> +               return shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n> +       else if (atom->option == R_STRIP)\n> +               return strip_ref_components(refname, atom->strip);\n> +       else\n> +               return refname;\n> +}\n> +\n>  static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n>                                     struct branch *branch, const char **s)\n>  {\n> @@ -1153,6 +1168,21 @@ char *get_head_description(void)\n>         return strbuf_detach(&desc, NULL);\n>  }\n>\n> +static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref)\n> +{\n> +       if (!ref->symref)\n> +               return \"\";\n> +       else\n> +               return 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> +       if (ref->kind & FILTER_REFS_DETACHED_HEAD)\n> +               return get_head_description();\n> +       return show_ref(&atom->u.refname, ref->refname);\n> +}\n> +\n>  /*\n>   * Parse the object referred by ref, and grab needed value.\n>   */\n> @@ -1181,7 +1211,6 @@ static void populate_value(struct ref_array_item *ref)\n>                 struct atom_value *v = &ref->value[i];\n>                 int deref = 0;\n>                 const char *refname;\n> -               const char *formatp;\n>                 struct branch *branch = NULL;\n>\n>                 v->handler = append_atom;\n> @@ -1192,12 +1221,10 @@ static void populate_value(struct ref_array_item *ref)\n>                         name++;\n>                 }\n>\n> -               if (starts_with(name, \"refname\")) {\n> -                       refname = ref->refname;\n> -                       if (ref->kind & FILTER_REFS_DETACHED_HEAD)\n> -                               refname = get_head_description();\n> -               } else if (starts_with(name, \"symref\"))\n> -                       refname = ref->symref ? ref->symref : \"\";\n> +               if (starts_with(name, \"refname\"))\n> +                       refname = get_refname(atom, ref);\n> +               else if (starts_with(name, \"symref\"))\n> +                       refname = get_symref(atom, ref);\n>                 else if (starts_with(name, \"upstream\")) {\n>                         const char *branch_name;\n>                         /* only local branches may have an upstream */\n> @@ -1273,21 +1300,6 @@ static void populate_value(struct ref_array_item *ref)\n>                 } else\n>                         continue;\n>\n> -               formatp = strchr(name, ':');\n> -               if (formatp) {\n> -                       const char *arg;\n> -\n> -                       formatp++;\n> -                       if (!strcmp(formatp, \"short\"))\n> -                               refname = shorten_unambiguous_ref(refname,\n> -                                                     warn_ambiguous_refs);\n> -                       else if (skip_prefix(formatp, \"strip=\", &arg))\n> -                               refname = strip_ref_components(refname, arg);\n> -                       else\n> -                               die(_(\"unknown %.*s format %s\"),\n> -                                   (int)(formatp - name), name, formatp);\n> -               }\n> -\n>                 if (!deref)\n>                         v->s = refname;\n>                 else\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index b06ea1c..3d28234 100755\n> --- a/t/t6300-for-each-ref.sh\n> +++ b/t/t6300-for-each-ref.sh\n> @@ -589,4 +589,13 @@ test_expect_success 'Verify usage of %(symref:short) atom' '\n>         test_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> +       git for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n> +       test_cmp expected actual\n> +'\n> +\n>  test_done\n> --\n> 2.10.2\n>\n"},{"id":"305602","messageId":"CA+P7+xr1N8W9wS4y+=pQwdPzVpuyrFHzURog1ZhbW7bvy1oxpg@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-13-Karthik.188@gmail.com","subject":"Re: [PATCH v7 12/17] ref-filter: make remote_ref_atom_parser() use refname_atom_parser_internal()","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:54:56Z","receivedAt":"2016-11-08T23:55:23Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Use the recently introduced refname_atom_parser_internal() within\n> remote_ref_atom_parser(), this provides a common base for all the ref\n> printing atoms, allowing %(upstream) and %(push) to also use the\n> ':strip' option.\n>\n> The atoms '%(push)' and '%(upstream)' will retain the ':track' and\n> ':trackshort' atom modifiers to themselves as they have no meaning in\n> context to the '%(refname)' and '%(symref)' atoms.\n>\n> Update the documentation and tests to reflect the same.\n>\n\nNice. Good to have all this become common.\n\n> Signed-off-by: Karthik Nayak <Karthik.188@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt | 27 ++++++++++++++-------------\n>  ref-filter.c                       | 26 +++++++++++++++-----------\n>  t/t6300-for-each-ref.sh            |  2 ++\n>  3 files changed, 31 insertions(+), 24 deletions(-)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index a669a32..600b703 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -114,21 +114,22 @@ objectname::\n>\n>  upstream::\n>         The name of a local ref which can be considered ``upstream''\n> -       from the displayed ref. Respects `:short` in the same way as\n> -       `refname` above.  Additionally respects `:track` to show\n> -       \"[ahead N, behind M]\" and `:trackshort` to show the terse\n> -       version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n> -       or \"=\" (in sync). `:track` also prints \"[gone]\" whenever\n> -       unknown upstream ref is encountered. Append `:track,nobracket`\n> -       to show tracking information without brackets (i.e \"ahead N,\n> -       behind M\").  Has no effect if the ref does not have tracking\n> -       information associated with it.\n> +       from the displayed ref. Respects `:short` and `:strip` in the\n> +       same way as `refname` above.  Additionally respects `:track`\n> +       to show \"[ahead N, behind M]\" and `:trackshort` to show the\n> +       terse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n> +       behind), or \"=\" (in sync). `:track` also prints \"[gone]\"\n> +       whenever unknown upstream ref is encountered. Append\n> +       `:track,nobracket` to show tracking information without\n> +       brackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n> +       does not have tracking information associated with it.\n>\n>  push::\n> -       The name of a local ref which represents the `@{push}` location\n> -       for the displayed ref. Respects `:short`, `:track`, and\n> -       `:trackshort` options as `upstream` does. Produces an empty\n> -       string if no `@{push}` ref is configured.\n> +       The name of a local ref which represents the `@{push}`\n> +       location for the displayed ref. Respects `:short`, `:strip`,\n> +       `:track`, and `:trackshort` options as `upstream`\n> +       does. Produces an empty string if no `@{push}` ref is\n> +       configured.\n>\n>  HEAD::\n>         '*' if HEAD matches current ref (the checked out branch), ' '\n> diff --git a/ref-filter.c b/ref-filter.c\n> index f1d27b5..7d3d3a6 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -52,7 +52,8 @@ static struct used_atom {\n>                 char color[COLOR_MAXLEN];\n>                 struct align align;\n>                 struct {\n> -                       enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option;\n> +                       enum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;\n> +                       struct refname_atom refname;\n>                         unsigned int nobracket: 1;\n>                 } remote_ref;\n>                 struct {\n> @@ -102,7 +103,9 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n>         int i;\n>\n>         if (!arg) {\n> -               atom->u.remote_ref.option = RR_NORMAL;\n> +               atom->u.remote_ref.option = RR_REF;\n> +               refname_atom_parser_internal(&atom->u.remote_ref.refname,\n> +                                            arg, atom->name);\n>                 return;\n>         }\n>\n> @@ -112,16 +115,17 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n>         for (i = 0; i < params.nr; i++) {\n>                 const char *s = params.items[i].string;\n>\n> -               if (!strcmp(s, \"short\"))\n> -                       atom->u.remote_ref.option = RR_SHORTEN;\n> -               else if (!strcmp(s, \"track\"))\n> +               if (!strcmp(s, \"track\"))\n>                         atom->u.remote_ref.option = RR_TRACK;\n>                 else if (!strcmp(s, \"trackshort\"))\n>                         atom->u.remote_ref.option = RR_TRACKSHORT;\n>                 else if (!strcmp(s, \"nobracket\"))\n>                         atom->u.remote_ref.nobracket = 1;\n> -               else\n> -                       die(_(\"unrecognized format: %%(%s)\"), atom->name);\n> +               else {\n> +                       atom->u.remote_ref.option = RR_REF;\n> +                       refname_atom_parser_internal(&atom->u.remote_ref.refname,\n> +                                                    arg, atom->name);\n> +               }\n>         }\n>\n>         string_list_clear(&params, 0);\n> @@ -1100,8 +1104,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n>                                     struct branch *branch, const char **s)\n>  {\n>         int num_ours, num_theirs;\n> -       if (atom->u.remote_ref.option == RR_SHORTEN)\n> -               *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n> +       if (atom->u.remote_ref.option == RR_REF)\n> +               *s = show_ref(&atom->u.remote_ref.refname, refname);\n>         else if (atom->u.remote_ref.option == RR_TRACK) {\n>                 if (stat_tracking_info(branch, &num_ours,\n>                                        &num_theirs, NULL)) {\n> @@ -1133,8 +1137,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n>                         *s = \">\";\n>                 else\n>                         *s = \"<>\";\n> -       } else /* RR_NORMAL */\n> -               *s = refname;\n> +       } else\n> +               die(\"BUG: unhandled RR_* enum\");\n>  }\n>\n>  char *get_head_description(void)\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index 3d28234..7ca0a12 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> --\n> 2.10.2\n>\n"},{"id":"305603","messageId":"CA+P7+xqHKVUfNm+jCsbMNazHpVhB46h60k75JzS35CrkR-d-UQ@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-14-Karthik.188@gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-08T23:58:19Z","receivedAt":"2016-11-08T23:58:44Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Add the options `:dir` and `:base` to all ref printing ('%(refname)',\n> '%(symref)', '%(push)' and '%(upstream)') atoms. The `:dir` option gives\n> the directory (the part after $GIT_DIR/) of the ref without the\n> refname. The `:base` option gives the base directory of the given\n> ref (i.e. the directory following $GIT_DIR/refs/).\n>\n\nNice, this seems useful.\n\n> Add tests and documentation for the same.\n>\n> Signed-off-by: Karthik Nayak <Karthik.188@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt | 34 +++++++++++++++++++---------------\n>  ref-filter.c                       | 29 +++++++++++++++++++++++++----\n>  t/t6300-for-each-ref.sh            | 24 ++++++++++++++++++++++++\n>  3 files changed, 68 insertions(+), 19 deletions(-)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index 600b703..f4ad297 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -96,7 +96,9 @@ refname::\n>         slash-separated path components from the front of the refname\n>         (e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.\n>         `<N>` must be a positive integer.  If a displayed ref has fewer\n> -       components than `<N>`, the command aborts with an error.\n> +       components than `<N>`, the command aborts with an error. For the base\n> +       directory of the ref (i.e. foo in refs/foo/bar/boz) append\n> +       `:base`. For the entire directory path append `:dir`.\n>\n>  objecttype::\n>         The type of the object (`blob`, `tree`, `commit`, `tag`).\n> @@ -114,22 +116,23 @@ objectname::\n>\n>  upstream::\n>         The name of a local ref which can be considered ``upstream''\n> -       from the displayed ref. Respects `:short` and `:strip` in the\n> -       same way as `refname` above.  Additionally respects `:track`\n> -       to show \"[ahead N, behind M]\" and `:trackshort` to show the\n> -       terse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n> -       behind), or \"=\" (in sync). `:track` also prints \"[gone]\"\n> -       whenever unknown upstream ref is encountered. Append\n> -       `:track,nobracket` to show tracking information without\n> -       brackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n> -       does not have tracking information associated with it.\n> +       from the displayed ref. Respects `:short`, `:strip`, `:base`\n> +       and `:dir` in the same way as `refname` above.  Additionally\n> +       respects `:track` to show \"[ahead N, behind M]\" and\n> +       `:trackshort` to show the terse version: \">\" (ahead), \"<\"\n> +       (behind), \"<>\" (ahead and behind), or \"=\" (in sync). `:track`\n> +       also prints \"[gone]\" whenever unknown upstream ref is\n> +       encountered. Append `:track,nobracket` to show tracking\n> +       information without brackets (i.e \"ahead N, behind M\").  Has\n> +       no effect if the ref does not have tracking information\n> +       associated with it.\n>\n>  push::\n>         The name of a local ref which represents the `@{push}`\n>         location for the displayed ref. Respects `:short`, `:strip`,\n> -       `:track`, and `:trackshort` options as `upstream`\n> -       does. Produces an empty string if no `@{push}` ref is\n> -       configured.\n> +       `:track`, `:trackshort`, `:base` and `:dir` options as\n> +       `upstream` does. Produces an empty string if no `@{push}` ref\n> +       is configured.\n>\n\nAt this point would it make more sense to document the extra values\nhere in one block separately? For example, the upstream atom is\ngetting pretty complex with all those options. Additionally, some of\nthe options can be combined, like nobracket, but others cannot be\ncomined so It may be worth documenting how and when those combinations\nwork?\n\n>  HEAD::\n>         '*' if HEAD matches current ref (the checked out branch), ' '\n> @@ -169,8 +172,9 @@ if::\n>\n>  symref::\n>         The ref which the given symbolic ref refers to. If not a\n> -       symbolic ref, nothing is printed. Respects the `:short` and\n> -       `:strip` options in the same way as `refname` above.\n> +       symbolic ref, nothing is printed. Respects the `:short`,\n> +       `:strip`, `:base` and `:dir` options in the same way as\n> +       `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> diff --git a/ref-filter.c b/ref-filter.c\n> index 7d3d3a6..b47b900 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -31,7 +31,7 @@ struct if_then_else {\n>  };\n>\n>  struct refname_atom {\n> -       enum { R_NORMAL, R_SHORT, R_STRIP } option;\n> +       enum { R_BASE, R_DIR, R_NORMAL, R_SHORT, R_STRIP } option;\n>         unsigned int strip;\n>  };\n>\n> @@ -93,7 +93,11 @@ static void refname_atom_parser_internal(struct refname_atom *atom,\n>                 atom->option = R_STRIP;\n>                 if (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0)\n>                         die(_(\"positive value expected refname:strip=%s\"), arg);\n> -       }       else\n> +       } else if (!strcmp(arg, \"dir\"))\n> +               atom->option = R_DIR;\n> +       else if (!strcmp(arg, \"base\"))\n> +               atom->option = R_BASE;\n> +       else\n>                 die(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n>  }\n>\n> @@ -252,7 +256,6 @@ static void if_atom_parser(struct used_atom *atom, const char *arg)\n>                 die(_(\"unrecognized %%(if) argument: %s\"), arg);\n>  }\n>\n> -\n>  static struct {\n>         const char *name;\n>         cmp_type cmp_type;\n> @@ -1096,7 +1099,25 @@ static const char *show_ref(struct refname_atom *atom, const char *refname)\n>                 return shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n>         else if (atom->option == R_STRIP)\n>                 return strip_ref_components(refname, atom->strip);\n> -       else\n> +       else if (atom->option == R_BASE) {\n> +               const char *sp, *ep;\n> +\n> +               if (skip_prefix(refname, \"refs/\", &sp)) {\n> +                       ep = strchr(sp, '/');\n> +                       if (!ep)\n> +                               return \"\";\n> +                       return xstrndup(sp, ep - sp);\n> +               }\n> +               return \"\";\n> +       } else if (atom->option == R_DIR) {\n> +               const char *sp, *ep;\n> +\n> +               sp = refname;\n> +               ep = strrchr(sp, '/');\n> +               if (!ep)\n> +                       return \"\";\n> +               return xstrndup(sp, ep - sp);\n> +       } else\n>                 return refname;\n>  }\n>\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index 7ca0a12..8ff6568 100755\n> --- a/t/t6300-for-each-ref.sh\n> +++ b/t/t6300-for-each-ref.sh\n> @@ -53,12 +53,18 @@ 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:dir refs/heads\n> +test_atom head refname:base heads\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:dir refs/remotes/origin\n> +test_atom head upstream:base remotes\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:dir refs/remotes/myfork\n> +test_atom head push:base remotes\n>  test_atom head objecttype commit\n>  test_atom head objectsize 171\n>  test_atom head objectname $(git rev-parse refs/heads/master)\n> @@ -600,4 +606,22 @@ test_expect_success 'Verify usage of %(symref:strip) atom' '\n>         test_cmp expected actual\n>  '\n>\n> +cat >expected <<EOF\n> +refs/heads\n> +EOF\n> +\n> +test_expect_success 'Verify usage of %(symref:dir) atom' '\n> +       git for-each-ref --format=\"%(symref:dir)\" refs/heads/sym > actual &&\n> +       test_cmp expected actual\n> +'\n> +\n> +cat >expected <<EOF\n> +heads\n> +EOF\n> +\n> +test_expect_success 'Verify usage of %(symref:base) atom' '\n> +       git for-each-ref --format=\"%(symref:base)\" refs/heads/sym > actual &&\n> +       test_cmp expected actual\n> +'\n> +\n>  test_done\n> --\n> 2.10.2\n>\n"},{"id":"305604","messageId":"CA+P7+xo0yrafS0NxtCiDJqrU+Qbdjv7WEcdq9GK44bKt4pYagQ@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-15-Karthik.188@gmail.com","subject":"Re: [PATCH v7 14/17] ref-filter: allow porcelain to translate messages in the output","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-09T00:00:31Z","receivedAt":"2016-11-09T00:00:56Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Introduce setup_ref_filter_porcelain_msg() so that the messages used in\n> the atom %(upstream:track) can be translated if needed. This is needed\n> as we port branch.c to use ref-filter's printing API's.\n>\n\nSo any user that wants these translated calls\nsetup_ref_filter_porcelain_msg but this will impact all callers from\nthat point on. Ok, I think that's ok? Otherwise they get default\nwithout translation.\n\n> Written-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  ref-filter.c | 28 ++++++++++++++++++++++++----\n>  ref-filter.h |  2 ++\n>  2 files changed, 26 insertions(+), 4 deletions(-)\n>\n> diff --git a/ref-filter.c b/ref-filter.c\n> index b47b900..944671a 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -15,6 +15,26 @@\n>  #include \"version.h\"\n>  #include \"wt-status.h\"\n>\n> +static struct ref_msg {\n> +       const char *gone;\n> +       const char *ahead;\n> +       const char *behind;\n> +       const char *ahead_behind;\n> +} msgs = {\n> +       \"gone\",\n> +       \"ahead %d\",\n> +       \"behind %d\",\n> +       \"ahead %d, behind %d\"\n> +};\n> +\n> +void setup_ref_filter_porcelain_msg(void)\n> +{\n> +       msgs.gone = _(\"gone\");\n> +       msgs.ahead = _(\"ahead %d\");\n> +       msgs.behind = _(\"behind %d\");\n> +       msgs.ahead_behind = _(\"ahead %d, behind %d\");\n> +}\n> +\n>  typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n>\n>  struct align {\n> @@ -1130,15 +1150,15 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n>         else if (atom->u.remote_ref.option == RR_TRACK) {\n>                 if (stat_tracking_info(branch, &num_ours,\n>                                        &num_theirs, NULL)) {\n> -                       *s = xstrdup(\"gone\");\n> +                       *s = xstrdup(msgs.gone);\n>                 } else if (!num_ours && !num_theirs)\n>                         *s = \"\";\n>                 else if (!num_ours)\n> -                       *s = xstrfmt(\"behind %d\", num_theirs);\n> +                       *s = xstrfmt(msgs.behind, num_theirs);\n>                 else if (!num_theirs)\n> -                       *s = xstrfmt(\"ahead %d\", num_ours);\n> +                       *s = xstrfmt(msgs.ahead, num_ours);\n>                 else\n> -                       *s = xstrfmt(\"ahead %d, behind %d\",\n> +                       *s = xstrfmt(msgs.ahead_behind,\n>                                      num_ours, num_theirs);\n>                 if (!atom->u.remote_ref.nobracket && *s[0]) {\n>                         const char *to_free = *s;\n> diff --git a/ref-filter.h b/ref-filter.h\n> index 0014b92..da17145 100644\n> --- a/ref-filter.h\n> +++ b/ref-filter.h\n> @@ -111,5 +111,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> --\n> 2.10.2\n>\n"},{"id":"305605","messageId":"CA+P7+xrm22pb2jPmTeBux3TmniWKJEeEVqy_J=DawmW2svHq=w@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-16-Karthik.188@gmail.com","subject":"Re: [PATCH v7 15/17] branch, tag: use porcelain output","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-09T00:01:07Z","receivedAt":"2016-11-09T00:01:49Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Call ref-filter's setup_ref_filter_porcelain_msg() to enable\n> translated messages for the %(upstream:tack) atom. Although branch.c\n> doesn't currently use ref-filter's printing API's, this will ensure\n> that when it does in the future patches, we do not need to worry about\n> translation.\n>\n\nMakes sense.\n\nThanks,\nJake\n\n> Written-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-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>\n> diff --git a/builtin/branch.c b/builtin/branch.c\n> index be9773a..dead2b8 100644\n> --- a/builtin/branch.c\n> +++ b/builtin/branch.c\n> @@ -656,6 +656,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n>                 OPT_END(),\n>         };\n>\n> +       setup_ref_filter_porcelain_msg();\n> +\n>         memset(&filter, 0, sizeof(filter));\n>         filter.kind = FILTER_REFS_BRANCHES;\n>         filter.abbrev = -1;\n> diff --git a/builtin/tag.c b/builtin/tag.c\n> index 50e4ae5..a00e9a7 100644\n> --- a/builtin/tag.c\n> +++ b/builtin/tag.c\n> @@ -373,6 +373,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n>                 OPT_END()\n>         };\n>\n> +       setup_ref_filter_porcelain_msg();\n> +\n>         git_config(git_tag_config, sorting_tail);\n>\n>         memset(&opt, 0, sizeof(opt));\n> --\n> 2.10.2\n>\n"},{"id":"305606","messageId":"CA+P7+xqRjMThZF7u_W6G0sHjFP3j5PMr=TszC6UxL2XCYO+CVA@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"Re: [PATCH v7 00/17] port branch.c to use ref-filter's printing options","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-09T00:15:44Z","receivedAt":"2016-11-09T00:16:10Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> This is part of unification of the commands 'git tag -l, git branch -l\n> and git for-each-ref'. This ports over branch.c to use ref-filter's\n> printing options.\n>\n> Initially posted here: $(gmane/279226). It was decided that this series\n> would follow up after refactoring ref-filter parsing mechanism, which\n> is now merged into master (9606218b32344c5c756f7c29349d3845ef60b80c).\n>\n> v1 can be found here: $(gmane/288342)\n> v2 can be found here: $(gmane/288863)\n> v3 can be found here: $(gmane/290299)\n> v4 can be found here: $(gmane/291106)\n> v5b can be found here: $(gmane/292467)\n> v6 can be found here: http://marc.info/?l=git&m=146330914118766&w=2\n>\n\nI reviewed the full series. I found a few minor things I would have\ndone differently, but overall I think it looks good. Thanks for the\nhard work and the time invested here. I remember seeing this on the\nlist quite some time ago, so it's nice to see it finally come\ntogether.\n\nThanks,\nJake\n\n> Changes in this version:\n>\n> 1. Rebased on top of master.\n>\n> Karthik Nayak (17):\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 symref_atom_parser() and refname_atom_parser()\n>   ref-filter: make remote_ref_atom_parser() use\n>     refname_atom_parser_internal()\n>   ref-filter: add `:dir` and `:base` options for ref printing atoms\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 |  82 ++++++-\n>  builtin/branch.c                   | 277 +++++++---------------\n>  builtin/tag.c                      |   2 +\n>  ref-filter.c                       | 456 +++++++++++++++++++++++++++++++------\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            |  73 +++++-\n>  t/t6302-for-each-ref-filter.sh     |  94 ++++++++\n>  10 files changed, 725 insertions(+), 291 deletions(-)\n>\n> --\n> 2.10.2\n>\n"},{"id":"305607","messageId":"CA+P7+xr20UyKMKUZBpP-SjtEhbow2df+iT6nF67mOAZ8BAaxEg@mail.gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-17-Karthik.188@gmail.com","subject":"Re: [PATCH v7 16/17] branch: use ref-filter printing APIs","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-09T00:14:03Z","receivedAt":"2016-11-09T00:28:51Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> Port branch.c to use ref-filter APIs for printing. This clears out\n> most of the code used in branch.c for printing and replaces them with\n> calls made to the ref-filter library.\n\nNice. This looks correct based on checking against the current\nbranch.c implementation by hand. There was one minor change I\nsuggested but I'm not really sure it buys is that much.\n\nThanks,\nJake\n\n>\n> Introduce build_format() which gets the format required for printing\n> of refs. Make amendments to print_ref_list() to reflect these changes.\n>\n\nOk.\n\n> Change calc_maxwidth() to also account for the length of HEAD ref, by\n> calling ref-filter:get_head_discription().\n>\n> Also change the test in t6040 to reflect the changes.\n\n\nRight.\n\n>\n> Before this patch, all cross-prefix symrefs weren't shortened. Since\n> we're using ref-filter APIs, we shorten all symrefs by default. We also\n> allow the user to change the format if needed with the introduction of\n> the '--format' option in the next patch.\n>\n\nThis also makes sense.\n\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  builtin/branch.c         | 234 ++++++++++++++---------------------------------\n>  t/t3203-branch-output.sh |   2 +-\n>  t/t6040-tracking-info.sh |   2 +-\n>  3 files changed, 70 insertions(+), 168 deletions(-)\n>\n> diff --git a/builtin/branch.c b/builtin/branch.c\n> index dead2b8..a19e05d 100644\n> --- a/builtin/branch.c\n> +++ b/builtin/branch.c\n> @@ -36,12 +36,12 @@ static unsigned char head_sha1[20];\n>\n>  static int branch_use_color = -1;\n>  static char branch_colors[][COLOR_MAXLEN] = {\n> -       GIT_COLOR_RESET,\n> -       GIT_COLOR_NORMAL,       /* PLAIN */\n> -       GIT_COLOR_RED,          /* REMOTE */\n> -       GIT_COLOR_NORMAL,       /* LOCAL */\n> -       GIT_COLOR_GREEN,        /* CURRENT */\n> -       GIT_COLOR_BLUE,         /* UPSTREAM */\n> +       \"%(color:reset)\",\n> +       \"%(color:reset)\",       /* PLAIN */\n> +       \"%(color:red)\",         /* REMOTE */\n> +       \"%(color:reset)\",       /* LOCAL */\n> +       \"%(color:green)\",       /* CURRENT */\n> +       \"%(color:blue)\",        /* UPSTREAM */\n>  };\n>  enum color_branch {\n>         BRANCH_COLOR_RESET = 0,\n> @@ -280,162 +280,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,\n>         return(ret);\n>  }\n>\n> -static void fill_tracking_info(struct strbuf *stat, const char *branch_name,\n> -               int show_upstream_ref)\n> -{\n> -       int ours, theirs;\n> -       char *ref = NULL;\n> -       struct branch *branch = branch_get(branch_name);\n> -       const char *upstream;\n> -       struct strbuf fancy = STRBUF_INIT;\n> -       int upstream_is_gone = 0;\n> -       int added_decoration = 1;\n> -\n> -       if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {\n> -               if (!upstream)\n> -                       return;\n> -               upstream_is_gone = 1;\n> -       }\n> -\n> -       if (show_upstream_ref) {\n> -               ref = shorten_unambiguous_ref(upstream, 0);\n> -               if (want_color(branch_use_color))\n> -                       strbuf_addf(&fancy, \"%s%s%s\",\n> -                                       branch_get_color(BRANCH_COLOR_UPSTREAM),\n> -                                       ref, branch_get_color(BRANCH_COLOR_RESET));\n> -               else\n> -                       strbuf_addstr(&fancy, ref);\n> -       }\n> -\n> -       if (upstream_is_gone) {\n> -               if (show_upstream_ref)\n> -                       strbuf_addf(stat, _(\"[%s: gone]\"), fancy.buf);\n> -               else\n> -                       added_decoration = 0;\n> -       } else if (!ours && !theirs) {\n> -               if (show_upstream_ref)\n> -                       strbuf_addf(stat, _(\"[%s]\"), fancy.buf);\n> -               else\n> -                       added_decoration = 0;\n> -       } else if (!ours) {\n> -               if (show_upstream_ref)\n> -                       strbuf_addf(stat, _(\"[%s: behind %d]\"), fancy.buf, theirs);\n> -               else\n> -                       strbuf_addf(stat, _(\"[behind %d]\"), theirs);\n> -\n> -       } else if (!theirs) {\n> -               if (show_upstream_ref)\n> -                       strbuf_addf(stat, _(\"[%s: ahead %d]\"), fancy.buf, ours);\n> -               else\n> -                       strbuf_addf(stat, _(\"[ahead %d]\"), ours);\n> -       } else {\n> -               if (show_upstream_ref)\n> -                       strbuf_addf(stat, _(\"[%s: ahead %d, behind %d]\"),\n> -                                   fancy.buf, ours, theirs);\n> -               else\n> -                       strbuf_addf(stat, _(\"[ahead %d, behind %d]\"),\n> -                                   ours, theirs);\n> -       }\n> -       strbuf_release(&fancy);\n> -       if (added_decoration)\n> -               strbuf_addch(stat, ' ');\n> -       free(ref);\n> -}\n> -\n> -static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,\n> -                            struct ref_filter *filter, const char *refname)\n> -{\n> -       struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;\n> -       const char *sub = _(\" **** invalid ref ****\");\n> -       struct commit *commit = item->commit;\n> -\n> -       if (!parse_commit(commit)) {\n> -               pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);\n> -               sub = subject.buf;\n> -       }\n> -\n> -       if (item->kind == FILTER_REFS_BRANCHES)\n> -               fill_tracking_info(&stat, refname, filter->verbose > 1);\n> -\n> -       strbuf_addf(out, \" %s %s%s\",\n> -               find_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),\n> -               stat.buf, sub);\n> -       strbuf_release(&stat);\n> -       strbuf_release(&subject);\n> -}\n> -\n> -static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,\n> -                                     struct ref_filter *filter, const char *remote_prefix)\n> -{\n> -       char c;\n> -       int current = 0;\n> -       int color;\n> -       struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;\n> -       const char *prefix_to_show = \"\";\n> -       const char *prefix_to_skip = NULL;\n> -       const char *desc = item->refname;\n> -       char *to_free = NULL;\n> -\n> -       switch (item->kind) {\n> -       case FILTER_REFS_BRANCHES:\n> -               prefix_to_skip = \"refs/heads/\";\n> -               skip_prefix(desc, prefix_to_skip, &desc);\n> -               if (!filter->detached && !strcmp(desc, head))\n> -                       current = 1;\n> -               else\n> -                       color = BRANCH_COLOR_LOCAL;\n> -               break;\n> -       case FILTER_REFS_REMOTES:\n> -               prefix_to_skip = \"refs/remotes/\";\n> -               skip_prefix(desc, prefix_to_skip, &desc);\n> -               color = BRANCH_COLOR_REMOTE;\n> -               prefix_to_show = remote_prefix;\n> -               break;\n> -       case FILTER_REFS_DETACHED_HEAD:\n> -               desc = to_free = get_head_description();\n> -               current = 1;\n> -               break;\n> -       default:\n> -               color = BRANCH_COLOR_PLAIN;\n> -               break;\n> -       }\n> -\n> -       c = ' ';\n> -       if (current) {\n> -               c = '*';\n> -               color = BRANCH_COLOR_CURRENT;\n> -       }\n> -\n> -       strbuf_addf(&name, \"%s%s\", prefix_to_show, desc);\n> -       if (filter->verbose) {\n> -               int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);\n> -               strbuf_addf(&out, \"%c %s%-*s%s\", c, branch_get_color(color),\n> -                           maxwidth + utf8_compensation, name.buf,\n> -                           branch_get_color(BRANCH_COLOR_RESET));\n> -       } else\n> -               strbuf_addf(&out, \"%c %s%s%s\", c, branch_get_color(color),\n> -                           name.buf, branch_get_color(BRANCH_COLOR_RESET));\n> -\n> -       if (item->symref) {\n> -               const char *symref = item->symref;\n> -               if (prefix_to_skip)\n> -                       skip_prefix(symref, prefix_to_skip, &symref);\n> -               strbuf_addf(&out, \" -> %s\", symref);\n> -       }\n> -       else if (filter->verbose)\n> -               /* \" f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h\" */\n> -               add_verbose_info(&out, item, filter, desc);\n> -       if (column_active(colopts)) {\n> -               assert(!filter->verbose && \"--column and --verbose are incompatible\");\n> -               string_list_append(&output, out.buf);\n> -       } else {\n> -               printf(\"%s\\n\", out.buf);\n> -       }\n> -       strbuf_release(&name);\n> -       strbuf_release(&out);\n> -       free(to_free);\n> -}\n> -\n>  static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n>  {\n>         int i, max = 0;\n> @@ -446,7 +290,12 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n>\n>                 skip_prefix(it->refname, \"refs/heads/\", &desc);\n>                 skip_prefix(it->refname, \"refs/remotes/\", &desc);\n> -               w = utf8_strwidth(desc);\n> +               if (it->kind == FILTER_REFS_DETACHED_HEAD) {\n> +                       char *head_desc = get_head_description();\n> +                       w = utf8_strwidth(head_desc);\n> +                       free(head_desc);\n> +               } else\n> +                       w = utf8_strwidth(desc);\n>\n>                 if (it->kind == FILTER_REFS_REMOTES)\n>                         w += remote_bonus;\n> @@ -456,12 +305,52 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n>         return max;\n>  }\n>\n> +static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)\n> +{\n> +       struct strbuf fmt = STRBUF_INIT;\n> +       struct strbuf local = STRBUF_INIT;\n> +       struct strbuf remote = STRBUF_INIT;\n> +\n\nOk, so here we go for reviewing the new formats.\n\n> +       strbuf_addf(&fmt, \"%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)\", branch_get_color(BRANCH_COLOR_CURRENT));\n> +\n\nThe first thing we print, is the * when it's HEAD or two spaces,\nfollowed by getting the current color scheme. Ok, this matches what we\ndo with branch today.\n\n> +       if (filter->verbose) {\n> +               strbuf_addf(&local, \"%%(align:%d,left)%%(refname:strip=2)%%(end)\", maxwidth);\n> +               strbuf_addf(&local, \"%s\", branch_get_color(BRANCH_COLOR_RESET));\n> +               strbuf_addf(&local, \" %%(objectname:short=7) \");\n> +\n\nNow, for verbose mode we setup local to be left aligned to a maximum\nwidth, with the refname stripped by 2 fields.\n\nThen, we reset the color, and print the object name shorted to 7 characters. Ok.\n\n> +               if (filter->verbose > 1)\n> +                       strbuf_addf(&local, \"%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)\"\n> +                                   \"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)\",\n> +                                   branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));\n\nWhen we have extra verbose, we check whether we have an upstream, and\nif so, we print the short name of that upstream inside brackets. If we\nhave tracking information, we print that without brackets, and then we\nend this section. Finally we print the subject.\n\nWe could almost re-use the code for the subject bits, but I'm not sure\nit's worth it. Maybe drop the %contents:subject part and add it\nafterwards since we always want it? It would remove some duplication\nbut overall not sure it's actually worth it.\n\n\n> +               else\n> +                       strbuf_addf(&local, \"%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)\");\n> +\n> +               strbuf_addf(&remote, \"%s%%(align:%d,left)%s%%(refname:strip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)\"\n> +                           \"%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)\",\n> +                           branch_get_color(BRANCH_COLOR_REMOTE), maxwidth,\n> +                           remote_prefix, branch_get_color(BRANCH_COLOR_RESET));\n\nHere we handle the remote value, which is always the same for verbose\nwith either one or two value.\n\n> +       } else {\n> +               strbuf_addf(&local, \"%%(refname:strip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n> +                           branch_get_color(BRANCH_COLOR_RESET));\n> +               strbuf_addf(&remote, \"%s%s%%(refname:strip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n> +                           branch_get_color(BRANCH_COLOR_REMOTE), remote_prefix, branch_get_color(BRANCH_COLOR_RESET));\n> +       }\n> +\n> +       strbuf_addf(&fmt, \"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\", local.buf, remote.buf);\n> +\n\nFinally we check to see whether the base is remotes and print the\nlocal vs the remote buf. Neat trick.\n\n> +       strbuf_release(&local);\n> +       strbuf_release(&remote);\n> +       return strbuf_detach(&fmt, NULL);\n> +}\n> +\n>  static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)\n>  {\n>         int i;\n>         struct ref_array array;\n>         int maxwidth = 0;\n>         const char *remote_prefix = \"\";\n> +       struct strbuf out = STRBUF_INIT;\n> +       char *format;\n>\n>         /*\n>          * If we are listing more than just remote branches,\n> @@ -473,12 +362,14 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin\n>\n>         memset(&array, 0, sizeof(array));\n>\n> -       verify_ref_format(\"%(refname)%(symref)\");\n>         filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);\n>\n>         if (filter->verbose)\n>                 maxwidth = calc_maxwidth(&array, strlen(remote_prefix));\n>\n> +       format = build_format(filter, maxwidth, remote_prefix);\n> +       verify_ref_format(format);\n> +\n>         /*\n>          * If no sorting parameter is given then we default to sorting\n>          * by 'refname'. This would give us an alphabetically sorted\n> @@ -490,10 +381,21 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin\n>                 sorting = ref_default_sorting();\n>         ref_array_sort(sorting, &array);\n>\n> -       for (i = 0; i < array.nr; i++)\n> -               format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);\n> +       for (i = 0; i < array.nr; i++) {\n> +               format_ref_array_item(array.items[i], format, 0, &out);\n> +               if (column_active(colopts)) {\n> +                       assert(!filter->verbose && \"--column and --verbose are incompatible\");\n> +                        /* format to a string_list to let print_columns() do its job */\n> +                       string_list_append(&output, out.buf);\n> +               } else {\n> +                       fwrite(out.buf, 1, out.len, stdout);\n> +                       putchar('\\n');\n> +               }\n> +               strbuf_release(&out);\n> +       }\n>\n>         ref_array_clear(&array);\n> +       free(format);\n>  }\n>\n>  static void reject_rebase_or_bisect_branch(const char *target)\n> diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh\n> index c6a3ccb..980c732 100755\n> --- a/t/t3203-branch-output.sh\n> +++ b/t/t3203-branch-output.sh\n> @@ -189,7 +189,7 @@ test_expect_success 'local-branch symrefs shortened properly' '\n>         git symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one &&\n>         cat >expect <<-\\EOF &&\n>           ref-to-branch -> branch-one\n> -         ref-to-remote -> refs/remotes/origin/branch-one\n> +         ref-to-remote -> origin/branch-one\n>         EOF\n>         git branch >actual.raw &&\n>         grep ref-to <actual.raw >actual &&\n> diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh\n> index 3d5c238..97a0765 100755\n> --- a/t/t6040-tracking-info.sh\n> +++ b/t/t6040-tracking-info.sh\n> @@ -44,7 +44,7 @@ b1 [ahead 1, behind 1] d\n>  b2 [ahead 1, behind 1] d\n>  b3 [behind 1] b\n>  b4 [ahead 2] f\n> -b5 g\n> +b5 [gone] g\n>  b6 c\n>  EOF\n>\n> --\n> 2.10.2\n>\n"},{"id":"305681","messageId":"CAOLa=ZS_V5SNSbiC_sT6E9rJMbxCMm=BRaoPL44xBNXAG2pgbA@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xoAmYqi0OazZux+rooXW+D_N9L5s+2BzUyJJJXtmLZX-Q@mail.gmail.com","subject":"Re: [PATCH v7 01/17] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-10T17:11:46Z","receivedAt":"2016-11-10T17:12:23Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hey,\n\nOn Wed, Nov 9, 2016 at 4:43 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> From: Karthik Nayak <karthik.188@gmail.com>\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 (i.e. one of\n>> +`--shell`, `--perl`, `--python`, `--tcl` is used), except for opening\n>> +atoms, replacement from every %(atom) is quoted when and only when it\n>> +appears at the top-level (that is, when it appears outside\n>> +%($open)...%(end)).\n>> +\n>> +When a scripting language specific quoting is in effect, everything\n>> +between a top-level opening atom and its matching %(end) is evaluated\n>> +according to the semantics of the opening atom and its result is\n>> +quoted.\n>> +\n>>\n>\n> Nice, I like the explanation above.\n>\n\nAll thanks to Eric, Junio, Christian, Matthieu and everyone else who\nhelped me phrase\nthese.\n\n>>\n>> +               }\n>> +       } else if (!if_then_else->condition_satisfied)\n>\n> Minor nit. I'm not sure what standard we use here at Git, but\n> traditionally, I prefer to see { } blocks on all sections even if only\n> one of them needs it. (That is, only drop the braces when every\n> section is one line.) It also looks weird with a comment since it\n> appears as multiple lines to the reader. I think the braces improve\n> readability.\n>\n> I don't know whether that's Git's code base standard or not, however.\n> It's not really worth a re-roll unless something else would need to\n> change.\n>\n\nI believe this is the syntax followed in Git, xdiff/xmerge.c:173 and so on.\n\nYou're comments are right on though.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305682","messageId":"CAOLa=ZT-RZw1RoKHVXMu9syrTp67iMVCDv1LtyysekJuhJQSpA@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xqVAEuc12eQJ7-Et0CtXc12t5zDUg3Qsh3214cTh_brbA@mail.gmail.com","subject":"Re: [PATCH v7 02/17] ref-filter: include reference to 'used_atom' within 'atom_value'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-10T17:16:03Z","receivedAt":"2016-11-10T17:16:45Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":">>\n>>  /*\n>> @@ -370,7 +368,7 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s\n>>         push_stack_element(&state->stack);\n>>         new = state->stack;\n>>         new->at_end = end_align_handler;\n>> -       new->at_end_data = &atomv->u.align;\n>> +       new->at_end_data = &atomv->atom->u.align;\n>\n> At first, this confused me. I was like \"we dropped the union, why are\n> we still referencing it. But I realized that the \"used_atom\" struct\n> actually contains the same union and we were copying it.\n>\n> Ok, so this looks good.\n>\n\nIt is confusing if one only looks at the patch without actually going\nthrough ref-filter.c. I'm sure your comment will help\nanyone going through these patches.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305683","messageId":"CAOLa=ZRDNGGc-OA+d+T1=9JvoVe2eBZt0DYQEHmeNKhDpxQbdg@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xqG5ACT2ZB=M8gLmDaYgSgX202+afJmZJKat6NzbFzL=g@mail.gmail.com","subject":"Re: [PATCH v7 04/17] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-10T17:36:32Z","receivedAt":"2016-11-10T17:37:23Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Nov 9, 2016 at 4:57 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> From: Karthik Nayak <karthik.188@gmail.com>\n>>\n>> Add support for %(objectname:short=<length>) which would print the\n>> abbreviated unique objectname of given length. When no length is\n>> specified, the length is 'DEFAULT_ABBREV'. The minimum length is\n>> 'MINIMUM_ABBREV'. The length may be exceeded to ensure that the provided\n>> object name is unique.\n>>\n>\n> Ok this makes sense. It may be annoying that the length might go\n> beyond the size that we wanted, but I think it's better than printing\n> a non-unique short abbreviation.\n>\n> I have one suggested change, which is to drop O_LENGTH and have\n> O_SHORT store the length always, setting it to DEFAULT_ABBREV when no\n> length provided. This allows you to drop some code. I don't think it's\n> actually worth a re-roll by itself since the current code is correct.\n>\n> Thanks,\n> Jake\n>\n\nThat does make sense, It would also not error out when we use\n%(objectname:short=) and\nnot specify the length. Idk, if that's desirable or not. But it does\nmake the code a little more\nconfusing to read at the same time.\n\nSo since its a small change, I'd be okay going either ways with this.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305684","messageId":"CAOLa=ZQCP2KtgLQie5p+PBiFjiNVFduUh=AmuLWcOKX5QA_BXA@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xoavgQ=NK8GGGXf90ESuPs+mFwHzEWbsnjWjZ4V7TWpjw@mail.gmail.com","subject":"Re: [PATCH v7 03/17] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-10T17:31:05Z","receivedAt":"2016-11-10T17:46:23Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Nov 9, 2016 at 4:52 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>\n> Ok. How does this handle whitespace? The previous if implementation\n> treated whitespace as trimming to ignore. Does this require an exact\n> whitespace match? It appears by the code that strings must match\n> exactly. Would it make more sense to always trim the value of\n> whitespace first before comparison? Hmm.. I think we should avoid\n> doing that actually.\n>\n\nThis does not trim whitespace what so ever and requires an exact match.\nI don't see the reason behind trimming whitespace though.\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>> diff --git a/ref-filter.c b/ref-filter.c\n>> index 8392303..44481c3 100644\n>> --- a/ref-filter.c\n>> +++ b/ref-filter.c\n>> @@ -22,6 +22,8 @@ struct align {\n>>  };\n>>\n>>  struct if_then_else {\n>> +       const char *if_equals,\n>> +               *not_equals;\n>\n> Ok so we add both if_equals and not_equals values. Could we re-use the\n> same string?\n\nIf we use an union then we'll be saving space, but that comes at the\ndisadvantage that\nwe'd need an indicator to show which subatom we're using (i.e\nif_equals or not_equals)\nfor the checks made in the code below.\n\nAt the expense of keeping two pointers we avoid the extra indicator\nwhile keeping the code\nelegant. Either ways not too keen on this, up for discussion :)\n\n>\n>>         unsigned int then_atom_seen : 1,\n>>                 else_atom_seen : 1,\n>>                 condition_satisfied : 1;\n>> @@ -49,6 +51,10 @@ static struct used_atom {\n>>                         enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n>>                         unsigned int nlines;\n>>                 } contents;\n>> +               struct {\n>> +                       const char *if_equals,\n>> +                               *not_equals;\n>\n>\n> Same here, why do we need both strings here stored separately? Could\n> we instead store which state to check and store the string once? I'm\n> not sure that really buys us any storage.\n>\n\nsame as above, we utilize the fact that its easier to check the strings like\n\n    if (if_equals)\n        ...\n    else (not_equals)\n        ...\n\nelse we'd have to have an indicator to know which to check.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305685","messageId":"CAOLa=ZQk9kKcYAQ=CXFwDhvg=pyeSaZk+s1pHafY8cvuxzWeEA@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xo4M5g15B7zKzC-zJvd6O0Wfpks142-+C8tAqTfyEJ+nA@mail.gmail.com","subject":"Re: [PATCH v7 05/17] ref-filter: move get_head_description() from branch.c","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-10T19:01:57Z","receivedAt":"2016-11-10T19:02:34Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Nov 9, 2016 at 5:01 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>\n>>\n>> -               if (starts_with(name, \"refname\"))\n>> +               if (starts_with(name, \"refname\")) {\n>>                         refname = ref->refname;\n>> -               else if (starts_with(name, \"symref\"))\n>> +                       if (ref->kind & FILTER_REFS_DETACHED_HEAD)\n>> +                               refname = get_head_description();\n>\n> Since this (I think?) changes behavior of refname would it make sense\n> to add a test for this?\n>\n\nAt the moment there is no way to check this, since this option is not used by\ngit for-each-ref or git tag (both of which completely use ref-filter\nATM). This is\nhowever tested as eventually git branch uses ref-filter.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305740","messageId":"xmqqwpgb3p3g.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"CA+P7+xoAmYqi0OazZux+rooXW+D_N9L5s+2BzUyJJJXtmLZX-Q@mail.gmail.com","subject":"Re: [PATCH v7 01/17] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-10T23:13:55Z","receivedAt":"2016-11-10T23:14:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.keller@gmail.com> writes:\n\n> Ok, so I have only one minor nit, but otherwise this looks quite good\n> to me. A few comments explaining my understanding, but only one\n> suggested\n> change which is really a minor nit and not worth re-rolling just for it.\n\nAs you didn't snip parts you didn't comment, I'll use this to add my\nown for convenience ;-)\n\n>> +if::\n>> +       Used as %(if)...%(then)...(%end) or\n>> +       %(if)...%(then)...%(else)...%(end).  If there is an atom with\n>> +       value or string literal after the %(if) then everything after\n>> +       the %(then) is printed, else if the %(else) atom is used, then\n>> +       everything after %(else) is printed. We ignore space when\n>> +       evaluating the string before %(then), this is useful when we\n>> +       use the %(HEAD) atom which prints either \"*\" or \" \" and we\n>> +       want 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\nI see a few instances of (%end) that were meant to be %(end).\n\nAren't the following two paragraphs ...\n\n>> +When a scripting language specific quoting is in effect (i.e. one of\n>> +`--shell`, `--perl`, `--python`, `--tcl` is used), except for opening\n>> +atoms, replacement from every %(atom) is quoted when and only when it\n>> +appears at the top-level (that is, when it appears outside\n>> +%($open)...%(end)).\n\n>> +When a scripting language specific quoting is in effect, everything\n>> +between a top-level opening atom and its matching %(end) is evaluated\n>> +according to the semantics of the opening atom and its result is\n>> +quoted.\n\n... saying the same thing?\n\n\n>> +               }\n>> +       } else if (!if_then_else->condition_satisfied)\n>\n> Minor nit. I'm not sure what standard we use here at Git, but\n> traditionally, I prefer to see { } blocks on all sections even if only\n> one of them needs it. (That is, only drop the braces when every\n> section is one line.) It also looks weird with a comment since it\n> appears as multiple lines to the reader. I think the braces improve\n> readability.\n>\n> I don't know whether that's Git's code base standard or not, however.\n> It's not really worth a re-roll unless something else would need to\n> change.\n>\n\nIn principle, we mimick the kernel style of using {} block even on a\nsingle-liner body in if/else if/else cascade when any one of them is\nnot a single-liner and requires {}.  But we often ignore that when a\ntruly trivial single liner follows if() even if its else clause is a\nbig block, e.g.\n\n\tif (cond)\n\t\tsingle;\n\telse {\n\t\tbig;\n\t\tblock;\n\t}\n\nI agree with you that this case should just use {} for the following\nparagraph, because it is technically a single-liner, but comes with\na big comment block and is very much easier to read with {} around\nit.\n\n>> +               /*\n>> +                * No %(else) atom: just drop the %(then) branch if the\n>> +                * condition is not satisfied.\n>> +                */\n>> +               strbuf_reset(&cur->output);\n\nThanks.\n"},{"id":"305741","messageId":"xmqqshqy53cj.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"CAOLa=ZS_V5SNSbiC_sT6E9rJMbxCMm=BRaoPL44xBNXAG2pgbA@mail.gmail.com","subject":"Re: [PATCH v7 01/17] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-10T23:20:44Z","receivedAt":"2016-11-10T23:20:51Z","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>> Minor nit. I'm not sure what standard we use here at Git, but\n>> traditionally, I prefer to see { } blocks on all sections even if only\n>> one of them needs it. (That is, only drop the braces when every\n>> section is one line.) It also looks weird with a comment since it\n>> appears as multiple lines to the reader. I think the braces improve\n>> readability.\n>>\n>> I don't know whether that's Git's code base standard or not, however.\n>> It's not really worth a re-roll unless something else would need to\n>> change.\n>>\n> I believe this is the syntax followed in Git, xdiff/xmerge.c:173 and so on.\n\nThat is a bad example for two reasons, if you mean this part:\n\n        if ((size = file->recs[i]->size) &&\n                        file->recs[i]->ptr[size - 1] == '\\n')\n                /* Last line; ends in LF; Is it CR/LF? */\n                return size > 1 &&\n                        file->recs[i]->ptr[size - 2] == '\\r';\n*       if (!i)\n                /* The only line has no eol */\n                return -1;\n        /* Determine eol from second-to-last line */\n\n\nWhat Jacob prefers is this:\n\n---------------------------------------------\n        if (cond)\n                simple;\n        else if (cond)\n                simple;\n        else\n                simple;\n---------------------------------------------\n        if (cond) {\n                simple;\n        } else if (cond) {\n                no;\n                longer;\n                simple;\n        } else {\n                simple;\n        }\n---------------------------------------------\n\nThat is, as long as all arms of if/else if/.../else cascade is\nsimple, {} is used nowhere in the cascade, but once even one of them\nrequires {} then all others gain {}.\n"},{"id":"305742","messageId":"xmqqoa1m5335.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"CA+P7+xoavgQ=NK8GGGXf90ESuPs+mFwHzEWbsnjWjZ4V7TWpjw@mail.gmail.com","subject":"Re: [PATCH v7 03/17] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-10T23:26:22Z","receivedAt":"2016-11-10T23:26:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.keller@gmail.com> writes:\n\n>> @@ -49,6 +51,10 @@ static struct used_atom {\n>>                         enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n>>                         unsigned int nlines;\n>>                 } contents;\n>> +               struct {\n>> +                       const char *if_equals,\n>> +                               *not_equals;\n>\n>\n> Same here, why do we need both strings here stored separately? Could\n> we instead store which state to check and store the string once? I'm\n> not sure that really buys us any storage.\n\nI am not sure if storage is an issue, but I tend to agree that it\nwould be semantically cleaner if this was done as a pair of <what\noperation uses this string constant?, the string constant>, and the\nformer would be enum { COMPARE_EQUAL, COMPARE_UNEQUAL}.\n\nYou could later enhance the comparison operator more easily with\nsuch an arrangement (e.g. if-equals-case-insensitively).\n"},{"id":"305743","messageId":"xmqqk2ca52td.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"20161108201211.25213-5-Karthik.188@gmail.com","subject":"Re: [PATCH v7 04/17] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-10T23:32:14Z","receivedAt":"2016-11-10T23:32:21Z","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>  \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\nUsers who want to use the default-abbrev, i.e. the autoscaling one\nintroduced recently, must use \"short\", not \"short=-1\", with this\ncode (especially with the \"must be at least MINIMUM_ABBREV\" logic),\nbut I do not think it is a problem, so I think this is good.\n\n\n> @@ -591,12 +601,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\n"},{"id":"305750","messageId":"CA+P7+xr8bhUga3eByh2nrt+pUtPUaaXq8wScRjwZ33bY=xFEDQ@mail.gmail.com","threadId":"44444","inReplyTo":"xmqqoa1m5335.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 03/17] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-11T05:25:37Z","receivedAt":"2016-11-11T05:27:40Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Thu, Nov 10, 2016 at 3:26 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jacob Keller <jacob.keller@gmail.com> writes:\n>\n>>> @@ -49,6 +51,10 @@ static struct used_atom {\n>>>                         enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n>>>                         unsigned int nlines;\n>>>                 } contents;\n>>> +               struct {\n>>> +                       const char *if_equals,\n>>> +                               *not_equals;\n>>\n>>\n>> Same here, why do we need both strings here stored separately? Could\n>> we instead store which state to check and store the string once? I'm\n>> not sure that really buys us any storage.\n>\n> I am not sure if storage is an issue, but I tend to agree that it\n> would be semantically cleaner if this was done as a pair of <what\n> operation uses this string constant?, the string constant>, and the\n> former would be enum { COMPARE_EQUAL, COMPARE_UNEQUAL}.\n>\n> You could later enhance the comparison operator more easily with\n> such an arrangement (e.g. if-equals-case-insensitively).\n\nThe main advantage I see is that it ensures in the API that there is\nno way for it to be both \"equal\" and \"not equal\" at the same time,\nwhere as two separate pointers while not actually done in practice\ncould both be set somehow, leading to potential questions.\n\nThanks,\nJake\n"},{"id":"305751","messageId":"CA+P7+xomLnpgM65W0TCUgO_n6XUu05jKkmQKgbUS9XxJu_46RQ@mail.gmail.com","threadId":"44444","inReplyTo":"CAOLa=ZQCP2KtgLQie5p+PBiFjiNVFduUh=AmuLWcOKX5QA_BXA@mail.gmail.com","subject":"Re: [PATCH v7 03/17] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-11T05:27:11Z","receivedAt":"2016-11-11T05:27:43Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Thu, Nov 10, 2016 at 9:31 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> On Wed, Nov 9, 2016 at 4:52 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n>> On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>>\n>> Ok. How does this handle whitespace? The previous if implementation\n>> treated whitespace as trimming to ignore. Does this require an exact\n>> whitespace match? It appears by the code that strings must match\n>> exactly. Would it make more sense to always trim the value of\n>> whitespace first before comparison? Hmm.. I think we should avoid\n>> doing that actually.\n>>\n>\n> This does not trim whitespace what so ever and requires an exact match.\n> I don't see the reason behind trimming whitespace though.\n>\n\nThe comment was made in reference to the fact that %(if)<atom>%(then)\nessentially trims whitespace, since if the <atom> prints only\nwhitespace it does not match.\n\nThus, it might make sense to keep it all the same and have this trim\nas well. However, I think there is no benefit or reasoning to do so in\nthis case.\n\nThanks,\nJake\n"},{"id":"305752","messageId":"CA+P7+xqXY6XvUm8Kna_GwY4XOSSGPM=-ezZECgkGcbnX_Penmg@mail.gmail.com","threadId":"44444","inReplyTo":"CAOLa=ZRDNGGc-OA+d+T1=9JvoVe2eBZt0DYQEHmeNKhDpxQbdg@mail.gmail.com","subject":"Re: [PATCH v7 04/17] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-11T05:29:13Z","receivedAt":"2016-11-11T05:29:38Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Thu, Nov 10, 2016 at 9:36 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> On Wed, Nov 9, 2016 at 4:57 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n>> On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>>> From: Karthik Nayak <karthik.188@gmail.com>\n>>>\n>>> Add support for %(objectname:short=<length>) which would print the\n>>> abbreviated unique objectname of given length. When no length is\n>>> specified, the length is 'DEFAULT_ABBREV'. The minimum length is\n>>> 'MINIMUM_ABBREV'. The length may be exceeded to ensure that the provided\n>>> object name is unique.\n>>>\n>>\n>> Ok this makes sense. It may be annoying that the length might go\n>> beyond the size that we wanted, but I think it's better than printing\n>> a non-unique short abbreviation.\n>>\n>> I have one suggested change, which is to drop O_LENGTH and have\n>> O_SHORT store the length always, setting it to DEFAULT_ABBREV when no\n>> length provided. This allows you to drop some code. I don't think it's\n>> actually worth a re-roll by itself since the current code is correct.\n>>\n>> Thanks,\n>> Jake\n>>\n>\n> That does make sense, It would also not error out when we use\n> %(objectname:short=) and\n> not specify the length. Idk, if that's desirable or not. But it does\n> make the code a little more\n> confusing to read at the same time.\n>\n\nI am not sure that would be the case. If you see \"objectname:short\"\nyou trreat this as if they had passed \"objectname:short=<default\nabbrev>\" but if you see \"objectname:short=\" you die, no?\n\n> So since its a small change, I'd be okay going either ways with this.\n>\n> --\n> Regards,\n> Karthik Nayak\n"},{"id":"305761","messageId":"CAOLa=ZSrhN0DAKN2aex5EThPQJ4C8GULLvckhct3n6NgXJge9w@mail.gmail.com","threadId":"44444","inReplyTo":"xmqqwpgb3p3g.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 01/17] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-11T09:10:10Z","receivedAt":"2016-11-11T09:10:49Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Fri, Nov 11, 2016 at 4:43 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jacob Keller <jacob.keller@gmail.com> writes:\n>\n>> Ok, so I have only one minor nit, but otherwise this looks quite good\n>> to me. A few comments explaining my understanding, but only one\n>> suggested\n>> change which is really a minor nit and not worth re-rolling just for it.\n>\n> As you didn't snip parts you didn't comment, I'll use this to add my\n> own for convenience ;-)\n>\n>>> +if::\n>>> +       Used as %(if)...%(then)...(%end) or\n>>> +       %(if)...%(then)...%(else)...%(end).  If there is an atom with\n>>> +       value or string literal after the %(if) then everything after\n>>> +       the %(then) is printed, else if the %(else) atom is used, then\n>>> +       everything after %(else) is printed. We ignore space when\n>>> +       evaluating the string before %(then), this is useful when we\n>>> +       use the %(HEAD) atom which prints either \"*\" or \" \" and we\n>>> +       want 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>\n> I see a few instances of (%end) that were meant to be %(end).\n>\n\nWill change that.\n\n> Aren't the following two paragraphs ...\n>\n>>> +When a scripting language specific quoting is in effect (i.e. one of\n>>> +`--shell`, `--perl`, `--python`, `--tcl` is used), except for opening\n>>> +atoms, replacement from every %(atom) is quoted when and only when it\n>>> +appears at the top-level (that is, when it appears outside\n>>> +%($open)...%(end)).\n>\n>>> +When a scripting language specific quoting is in effect, everything\n>>> +between a top-level opening atom and its matching %(end) is evaluated\n>>> +according to the semantics of the opening atom and its result is\n>>> +quoted.\n>\n> ... saying the same thing?\n>\n\nYes. I'm not sure of what the context even was, but I shall remove the\nfirst paragraph,\nthe second one seems to notify the same thing in simpler terms.\n\n>\n>>> +               }\n>>> +       } else if (!if_then_else->condition_satisfied)\n>>\n>> Minor nit. I'm not sure what standard we use here at Git, but\n>> traditionally, I prefer to see { } blocks on all sections even if only\n>> one of them needs it. (That is, only drop the braces when every\n>> section is one line.) It also looks weird with a comment since it\n>> appears as multiple lines to the reader. I think the braces improve\n>> readability.\n>>\n>> I don't know whether that's Git's code base standard or not, however.\n>> It's not really worth a re-roll unless something else would need to\n>> change.\n>>\n>\n> In principle, we mimick the kernel style of using {} block even on a\n> single-liner body in if/else if/else cascade when any one of them is\n> not a single-liner and requires {}.  But we often ignore that when a\n> truly trivial single liner follows if() even if its else clause is a\n> big block, e.g.\n>\n>         if (cond)\n>                 single;\n>         else {\n>                 big;\n>                 block;\n>         }\n>\n> I agree with you that this case should just use {} for the following\n> paragraph, because it is technically a single-liner, but comes with\n> a big comment block and is very much easier to read with {} around\n> it.\n>\n\nAh! I see, sure I'll change it :)\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305763","messageId":"CAOLa=ZQ_Z+AJBf-=i3hW9M2ng5+HfR5AbdWGJp+_z5xthoqdXA@mail.gmail.com","threadId":"44444","inReplyTo":"xmqqshqy53cj.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 01/17] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-11T09:13:14Z","receivedAt":"2016-11-11T09:13:53Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Fri, Nov 11, 2016 at 4:50 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>>> Minor nit. I'm not sure what standard we use here at Git, but\n>>> traditionally, I prefer to see { } blocks on all sections even if only\n>>> one of them needs it. (That is, only drop the braces when every\n>>> section is one line.) It also looks weird with a comment since it\n>>> appears as multiple lines to the reader. I think the braces improve\n>>> readability.\n>>>\n>>> I don't know whether that's Git's code base standard or not, however.\n>>> It's not really worth a re-roll unless something else would need to\n>>> change.\n>>>\n>> I believe this is the syntax followed in Git, xdiff/xmerge.c:173 and so on.\n>\n> That is a bad example for two reasons, if you mean this part:\n>\n>         if ((size = file->recs[i]->size) &&\n>                         file->recs[i]->ptr[size - 1] == '\\n')\n>                 /* Last line; ends in LF; Is it CR/LF? */\n>                 return size > 1 &&\n>                         file->recs[i]->ptr[size - 2] == '\\r';\n> *       if (!i)\n>                 /* The only line has no eol */\n>                 return -1;\n>         /* Determine eol from second-to-last line */\n>\n>\n> What Jacob prefers is this:\n>\n> ---------------------------------------------\n>         if (cond)\n>                 simple;\n>         else if (cond)\n>                 simple;\n>         else\n>                 simple;\n> ---------------------------------------------\n>         if (cond) {\n>                 simple;\n>         } else if (cond) {\n>                 no;\n>                 longer;\n>                 simple;\n>         } else {\n>                 simple;\n>         }\n> ---------------------------------------------\n>\n> That is, as long as all arms of if/else if/.../else cascade is\n> simple, {} is used nowhere in the cascade, but once even one of them\n> requires {} then all others gain {}.\n\nI see, I mistook Jacob then.\n\nI was talking about if we need to use {} whenever there's only a\nsingle line of code\nbut followed by a comment which extends the if/else if/.../else block.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305854","messageId":"CAOLa=ZQqe3vEj_428d41vd_4kfjzsm87Wam6Zm2dhXWkPdJ8Rw@mail.gmail.com","threadId":"44444","inReplyTo":"xmqqoa1m5335.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 03/17] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-12T09:19:19Z","receivedAt":"2016-11-12T09:19:56Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hello,\n\nOn Fri, Nov 11, 2016 at 4:56 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jacob Keller <jacob.keller@gmail.com> writes:\n>\n>>> @@ -49,6 +51,10 @@ static struct used_atom {\n>>>                         enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n>>>                         unsigned int nlines;\n>>>                 } contents;\n>>> +               struct {\n>>> +                       const char *if_equals,\n>>> +                               *not_equals;\n>>\n>>\n>> Same here, why do we need both strings here stored separately? Could\n>> we instead store which state to check and store the string once? I'm\n>> not sure that really buys us any storage.\n>\n> I am not sure if storage is an issue, but I tend to agree that it\n> would be semantically cleaner if this was done as a pair of <what\n> operation uses this string constant?, the string constant>, and the\n> former would be enum { COMPARE_EQUAL, COMPARE_UNEQUAL}.\n>\n> You could later enhance the comparison operator more easily with\n> such an arrangement (e.g. if-equals-case-insensitively).\n\nThat's a rather good point you make there, I think using an enum with a string\nconstant would be ideal for future extensibility too. Thanks for the\nexplanation,\nI shall change it.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305855","messageId":"CAOLa=ZQ1L2-yGH_1E6p0-UdbXpyujEzF6V-znyw3H0ZLbbjW+w@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xqXY6XvUm8Kna_GwY4XOSSGPM=-ezZECgkGcbnX_Penmg@mail.gmail.com","subject":"Re: [PATCH v7 04/17] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-12T09:56:27Z","receivedAt":"2016-11-12T09:57:16Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hello,\n\nOn Fri, Nov 11, 2016 at 10:59 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Thu, Nov 10, 2016 at 9:36 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> On Wed, Nov 9, 2016 at 4:57 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n>>\n>> That does make sense, It would also not error out when we use\n>> %(objectname:short=) and\n>> not specify the length. Idk, if that's desirable or not. But it does\n>> make the code a little more\n>> confusing to read at the same time.\n>>\n>\n> I am not sure that would be the case. If you see \"objectname:short\"\n> you trreat this as if they had passed \"objectname:short=<default\n> abbrev>\" but if you see \"objectname:short=\" you die, no?\n>\n\nSorry, my bad.\n\n\n\nOn Fri, Nov 11, 2016 at 5:02 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>>       else if (!strcmp(arg, \"short\"))\n>> -             atom->u.objectname = O_SHORT;\n>> -     else\n>> +             atom->u.objectname.option = O_SHORT;\n>> +     else if (skip_prefix(arg, \"short=\", &arg)) {\n>> +             atom->u.objectname.option = O_LENGTH;\n>> +             if (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n>> +                 atom->u.objectname.length == 0)\n>> +                     die(_(\"positive value expected objectname:short=%s\"), arg);\n>> +             if (atom->u.objectname.length < MINIMUM_ABBREV)\n>> +                     atom->u.objectname.length = MINIMUM_ABBREV;\n>> +     } else\n>>               die(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n>>  }\n>\n> Users who want to use the default-abbrev, i.e. the autoscaling one\n> introduced recently, must use \"short\", not \"short=-1\", with this\n> code (especially with the \"must be at least MINIMUM_ABBREV\" logic),\n> but I do not think it is a problem, so I think this is good.\n>\n\nI think I'll leave this as it is. If that's okay\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305863","messageId":"CAOLa=ZQJ9jTN7G5ZgLqs0ZzcPouQcf_4xateTaM=2=Ckbo4mGA@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xoKxGiNJO4g=Nm+KY=5=u8g2edoLOoe5K0ZAPrvoGj-Ug@mail.gmail.com","subject":"Re: [PATCH v7 07/17] ref-filter: make %(upstream:track) prints \"[gone]\" for invalid upstreams","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-12T18:48:30Z","receivedAt":"2016-11-12T18:49:09Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Nov 9, 2016 at 5:07 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n>> Make changes to t/t6300-for-each-ref.sh and\n>> Documentation/git-for-each-ref.txt to reflect this change.\n>>\n>\n> This will change behavior if people were expecting it to remain\n> silent, but I think this could be considered a bug.\n>\n\nDidn't get you.\n\n>> Mentored-by: Christian Couder <christian.couder@gmail.com>\n>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>> Helped-by : Jacob Keller <jacob.keller@gmail.com>\n>> Signed-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>>\n>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n>> index 92184c4..fd365eb 100644\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -119,7 +119,8 @@ upstream::\n>>         \"[ahead N, behind M]\" and `:trackshort` to show the terse\n>>         version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n>>         or \"=\" (in sync).  Has no effect if the ref does not have\n>> -       tracking information associated with it.\n>> +       tracking information associated with it. `:track` also prints\n>> +       \"[gone]\" whenever unknown upstream ref is encountered.\n>>\n>\n> I think this is poorly worded. If I understand, \"has no effect if the\n> ref does not have tracking information\" so in that case we still print\n> nothing, right? but otherwise we print [gone] only when the upstream\n> ref no longer actually exists locally? I wonder if there is a better\n> wording for this? I don't have one. Any suggestions to avoid confusing\n> these two cases?\n>\n\nDropping this, since its taken care of in the next patch.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305864","messageId":"CAOLa=ZSYNbsm9vvQwBtC0RwO3DYZTiq0pdeF=G8wNgf5rba00A@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xrm-MMRa9RuaHUGNWJmH8UcYPj3RZCsy1uvVRMuLeP0ZQ@mail.gmail.com","subject":"Re: [PATCH v7 08/17] ref-filter: add support for %(upstream:track,nobracket)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-12T20:01:24Z","receivedAt":"2016-11-12T20:03:09Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Nov 9, 2016 at 5:15 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> From: Karthik Nayak <karthik.188@gmail.com>\n>>\n>> Add support for %(upstream:track,nobracket) which will print the\n>> tracking information without the brackets (i.e. \"ahead N, behind M\").\n>> This is needed when we port branch.c to use ref-filter's printing APIs.\n>>\n>\n> Makes sense. Seems a bit weird that we have the brackets normally\n> rather than adding them as an option, but I think this is ok. We don't\n> want to change all previous uses in this case.\n>\n> My only suggestion here would be to add code so that the options die()\n> when we use nobracket along with trackshort or without track. This\n> ensures that the nobracket option only applies to track mode?\n>\n\nSure, seems reasonable. There's also the fact that even though nobracket\nactually only supports the 'track' option. It might make sense to\nleave it as it\nis, i.e not die() when used with the other options cause, it kinda stays true to\nit being used. e.g. %(upstream:nobracket,trackshort) does give 'trackshort'\nwithout brackets, even though that's the default option and only way we print\n'trackshort' information, it seems to make sense from the users point of view.\n\n>> Add test and documentation for the same.\n>>\n>> Mentored-by: Christian Couder <christian.couder@gmail.com>\n>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>> ---\n>>  Documentation/git-for-each-ref.txt |  8 +++--\n>>  ref-filter.c                       | 67 +++++++++++++++++++++++++-------------\n>>  t/t6300-for-each-ref.sh            |  2 ++\n>>  3 files changed, 51 insertions(+), 26 deletions(-)\n>>\n>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n>> index fd365eb..3953431 100644\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -118,9 +118,11 @@ upstream::\n>>         `refname` above.  Additionally respects `:track` to show\n>>         \"[ahead N, behind M]\" and `:trackshort` to show the terse\n>>         version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n>> -       or \"=\" (in sync).  Has no effect if the ref does not have\n>> -       tracking information associated with it. `:track` also prints\n>> -       \"[gone]\" whenever unknown upstream ref is encountered.\n>> +       or \"=\" (in sync). `:track` also prints \"[gone]\" whenever\n>> +       unknown upstream ref is encountered. Append `:track,nobracket`\n>> +       to show tracking information without brackets (i.e \"ahead N,\n>> +       behind M\").  Has no effect if the ref does not have tracking\n>> +       information associated with it.\n>>\n>\n> Ok so my comment on the previous patch is fixed here, the new wording\n> makes it much more clear that [gone] is not the same thing as no\n> information. So I don't think we should bother changing the previous\n> patch in the series. This might want to document that nobracket works\n> even without track, even if it doesn't actually do anything? Or make\n> the code more strict in that we die() if the values are put together\n> that make no sense?\n>\n\nSpeaking from what I said above, maybe it makes sense to leave it like it is?\n\n>>  push::\n>>         The name of a local ref which represents the `@{push}` location\n>> diff --git a/ref-filter.c b/ref-filter.c\n>> index 6d51b80..4d7e414 100644\n>> --- a/ref-filter.c\n>> +++ b/ref-filter.c\n>> @@ -46,8 +46,10 @@ static struct used_atom {\n>>         union {\n>>                 char color[COLOR_MAXLEN];\n>>                 struct align align;\n>> -               enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }\n>> -                       remote_ref;\n>> +               struct {\n>> +                       enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option;\n>> +                       unsigned int nobracket: 1;\n>> +               } remote_ref;\n>>                 struct {\n>>                         enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n>>                         unsigned int nlines;\n>> @@ -75,16 +77,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>> -       if (!arg)\n>> -               atom->u.remote_ref = RR_NORMAL;\n>> -       else if (!strcmp(arg, \"short\"))\n>> -               atom->u.remote_ref = RR_SHORTEN;\n>> -       else if (!strcmp(arg, \"track\"))\n>> -               atom->u.remote_ref = RR_TRACK;\n>> -       else if (!strcmp(arg, \"trackshort\"))\n>> -               atom->u.remote_ref = RR_TRACKSHORT;\n>> -       else\n>> -               die(_(\"unrecognized format: %%(%s)\"), atom->name);\n>> +       struct string_list params = STRING_LIST_INIT_DUP;\n>> +       int i;\n>> +\n>> +       if (!arg) {\n>> +               atom->u.remote_ref.option = RR_NORMAL;\n>> +               return;\n>> +       }\n>> +\n>> +       atom->u.remote_ref.nobracket = 0;\n>> +       string_list_split(&params, arg, ',', -1);\n>> +\n>> +       for (i = 0; i < params.nr; i++) {\n>> +               const char *s = params.items[i].string;\n>> +\n>> +               if (!strcmp(s, \"short\"))\n>> +                       atom->u.remote_ref.option = RR_SHORTEN;\n>> +               else if (!strcmp(s, \"track\"))\n>\n> Should we add die()s here to disallow setting the remote_ref option\n> multiple times? Otherwise, we should document that the last one wins?\n> Not sure it's really a big deal here, but we could do it to ensure\n> consistency for options which don't make sense together?\n>\n\nIt makes sense to add a small note that the last option wins, I suppose\n\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305865","messageId":"CAOLa=ZTcBf4MGATxXshzFOMa-M_aEm1zf_bpNnFydbfgdRcEHw@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xrHRMZemNfo3gjJryBRR5b4T8VT3t9_CBE5hJJfv+D3PQ@mail.gmail.com","subject":"Re: [PATCH v7 11/17] ref-filter: introduce symref_atom_parser() and refname_atom_parser()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-12T20:12:35Z","receivedAt":"2016-11-12T20:13:12Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Nov 9, 2016 at 5:22 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> From: Karthik Nayak <karthik.188@gmail.com>\n>\n> What's the reasoning for using separate functions here if they are\n> exactly identical except for name? Do we intend to add separate\n> options for this? I don't really have a problem with separate\n> functions here since it helps avoid confusion but they are identical\n> otherwise...\n>\n\nI see no need to have separate functions. Maybe in the future when we\nplan on adding\nspecific options we could split them. I'll drop the\nsymref_atom_parser() function and use\nrefname_atom_parser() both places.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305875","messageId":"CAOLa=ZTWFuzWBjGUX_nV4rVVDRpaabmj0-M6S7aJkX3w+dK2Jw@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xqHKVUfNm+jCsbMNazHpVhB46h60k75JzS35CrkR-d-UQ@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-13T14:07:41Z","receivedAt":"2016-11-13T14:08:19Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hello,\n\nOn Wed, Nov 9, 2016 at 5:28 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> From: Karthik Nayak <karthik.188@gmail.com>\n>> Add tests and documentation for the same.\n>>\n>> Signed-off-by: Karthik Nayak <Karthik.188@gmail.com>\n>> ---\n>>  Documentation/git-for-each-ref.txt | 34 +++++++++++++++++++---------------\n>>  ref-filter.c                       | 29 +++++++++++++++++++++++++----\n>>  t/t6300-for-each-ref.sh            | 24 ++++++++++++++++++++++++\n>>  3 files changed, 68 insertions(+), 19 deletions(-)\n>>\n>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n>> index 600b703..f4ad297 100644\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -96,7 +96,9 @@ refname::\n>>         slash-separated path components from the front of the refname\n>>         (e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.\n>>         `<N>` must be a positive integer.  If a displayed ref has fewer\n>> -       components than `<N>`, the command aborts with an error.\n>> +       components than `<N>`, the command aborts with an error. For the base\n>> +       directory of the ref (i.e. foo in refs/foo/bar/boz) append\n>> +       `:base`. For the entire directory path append `:dir`.\n>>\n>>  objecttype::\n>>         The type of the object (`blob`, `tree`, `commit`, `tag`).\n>> @@ -114,22 +116,23 @@ objectname::\n>>\n>>  upstream::\n>>         The name of a local ref which can be considered ``upstream''\n>> -       from the displayed ref. Respects `:short` and `:strip` in the\n>> -       same way as `refname` above.  Additionally respects `:track`\n>> -       to show \"[ahead N, behind M]\" and `:trackshort` to show the\n>> -       terse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n>> -       behind), or \"=\" (in sync). `:track` also prints \"[gone]\"\n>> -       whenever unknown upstream ref is encountered. Append\n>> -       `:track,nobracket` to show tracking information without\n>> -       brackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n>> -       does not have tracking information associated with it.\n>> +       from the displayed ref. Respects `:short`, `:strip`, `:base`\n>> +       and `:dir` in the same way as `refname` above.  Additionally\n>> +       respects `:track` to show \"[ahead N, behind M]\" and\n>> +       `:trackshort` to show the terse version: \">\" (ahead), \"<\"\n>> +       (behind), \"<>\" (ahead and behind), or \"=\" (in sync). `:track`\n>> +       also prints \"[gone]\" whenever unknown upstream ref is\n>> +       encountered. Append `:track,nobracket` to show tracking\n>> +       information without brackets (i.e \"ahead N, behind M\").  Has\n>> +       no effect if the ref does not have tracking information\n>> +       associated with it.\n>>\n>>  push::\n>>         The name of a local ref which represents the `@{push}`\n>>         location for the displayed ref. Respects `:short`, `:strip`,\n>> -       `:track`, and `:trackshort` options as `upstream`\n>> -       does. Produces an empty string if no `@{push}` ref is\n>> -       configured.\n>> +       `:track`, `:trackshort`, `:base` and `:dir` options as\n>> +       `upstream` does. Produces an empty string if no `@{push}` ref\n>> +       is configured.\n>>\n>\n> At this point would it make more sense to document the extra values\n> here in one block separately? For example, the upstream atom is\n> getting pretty complex with all those options. Additionally, some of\n> the options can be combined, like nobracket, but others cannot be\n> comined so It may be worth documenting how and when those combinations\n> work?\n>\n\nI do agree. The documentation seems cluttered. It'd be nice to clean\nit up. Maybe\nafter this series? Or any suggestions on how to go about it?\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305879","messageId":"xmqq60nqzuye.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"CAOLa=ZTWFuzWBjGUX_nV4rVVDRpaabmj0-M6S7aJkX3w+dK2Jw@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-14T01:55:21Z","receivedAt":"2016-11-14T01:55:29Z","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>>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n>>> index 600b703..f4ad297 100644\n>>> --- a/Documentation/git-for-each-ref.txt\n>>> +++ b/Documentation/git-for-each-ref.txt\n>>> @@ -96,7 +96,9 @@ refname::\n>>>         slash-separated path components from the front of the refname\n>>>         (e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.\n>>>         `<N>` must be a positive integer.  If a displayed ref has fewer\n>>> -       components than `<N>`, the command aborts with an error.\n>>> +       components than `<N>`, the command aborts with an error. For the base\n>>> +       directory of the ref (i.e. foo in refs/foo/bar/boz) append\n>>> +       `:base`. For the entire directory path append `:dir`.\n\nSorry that I missed this so far and I do not know how many recent\nrerolls had them like this, but I am not sure about these :base and\n:dir suffixes.  From their names I think readers would expect that\nthey do rough equivalents to basename() and dirname() applied to the\nrefname, but the example contradicts with that intuition.  The\nresult of applying basename() to 'refs/boo/bar/boz' would be 'boz'\nand not 'foo' as the example says.\n\nSo assuming that :base and :dir are unrelated to basename() and\ndirname():\n\n - I think calling these :base and :dir may be misleading\n\n - More importantly, what do these do?  I do not think of a good\n   description that generalizes \"base of refs/foo/bar/boz is foo\" to\n   explain your :base.\n\n - A :dir that corresponds to the :base that picks 'foo' from\n   'refs/foo/bar/boz' needs an example, too.\n\nOr is the above example simply a typo?  Is refs/foo/bar/boz:base\n'boz', not 'foo'?\n\n\n\n"},{"id":"305909","messageId":"CAOLa=ZQSQe=jfTpdyscZCZgi5p6cVLN23oi2eE-hqFTXgt2LEQ@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xr20UyKMKUZBpP-SjtEhbow2df+iT6nF67mOAZ8BAaxEg@mail.gmail.com","subject":"Re: [PATCH v7 16/17] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-14T19:23:04Z","receivedAt":"2016-11-14T19:23:49Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hello\n\nOn Wed, Nov 9, 2016 at 5:44 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> From: Karthik Nayak <karthik.188@gmail.com>\n>>\n>> Port branch.c to use ref-filter APIs for printing. This clears out\n>> most of the code used in branch.c for printing and replaces them with\n>> calls made to the ref-filter library.\n>\n> Nice. This looks correct based on checking against the current\n> branch.c implementation by hand. There was one minor change I\n> suggested but I'm not really sure it buys is that much.\n>\n\nThanks for this review. More down.\n\n>> +               if (filter->verbose > 1)\n>> +                       strbuf_addf(&local, \"%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)\"\n>> +                                   \"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)\",\n>> +                                   branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));\n>\n> When we have extra verbose, we check whether we have an upstream, and\n> if so, we print the short name of that upstream inside brackets. If we\n> have tracking information, we print that without brackets, and then we\n> end this section. Finally we print the subject.\n>\n> We could almost re-use the code for the subject bits, but I'm not sure\n> it's worth it. Maybe drop the %contents:subject part and add it\n> afterwards since we always want it? It would remove some duplication\n> but overall not sure it's actually worth it.\n>\n\nIf you see that's the last part we add to the 'local' strbuf in the\nverbose case.\nIf we want to remove the duplication we'll end up adding one more\nstrbuf_addf(...).\nSo I guess its better this way.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305910","messageId":"CAOLa=ZRKTE7-LQ27K+aqKanfE0ioerdkgTgAmjOXxC3CZVY7yg@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xqRjMThZF7u_W6G0sHjFP3j5PMr=TszC6UxL2XCYO+CVA@mail.gmail.com","subject":"Re: [PATCH v7 00/17] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-14T19:24:23Z","receivedAt":"2016-11-14T19:24:59Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hello,\n\nOn Wed, Nov 9, 2016 at 5:45 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Nov 8, 2016 at 12:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> This is part of unification of the commands 'git tag -l, git branch -l\n>> and git for-each-ref'. This ports over branch.c to use ref-filter's\n>> printing options.\n>>\n>> Initially posted here: $(gmane/279226). It was decided that this series\n>> would follow up after refactoring ref-filter parsing mechanism, which\n>> is now merged into master (9606218b32344c5c756f7c29349d3845ef60b80c).\n>>\n>> v1 can be found here: $(gmane/288342)\n>> v2 can be found here: $(gmane/288863)\n>> v3 can be found here: $(gmane/290299)\n>> v4 can be found here: $(gmane/291106)\n>> v5b can be found here: $(gmane/292467)\n>> v6 can be found here: http://marc.info/?l=git&m=146330914118766&w=2\n>>\n>\n> I reviewed the full series. I found a few minor things I would have\n> done differently, but overall I think it looks good. Thanks for the\n> hard work and the time invested here. I remember seeing this on the\n> list quite some time ago, so it's nice to see it finally come\n> together.\n>\n\nThanks for the review. I've gone through all the mails and will follow\nup with replies.\nWill post the next version as soon as possible. Thanks.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305911","messageId":"CAOLa=ZSFuq2+6xsrJ=CcXuOVbTnbDirbRtu7Fonfk+9EdRpbxg@mail.gmail.com","threadId":"44444","inReplyTo":"xmqq60nqzuye.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-14T19:36:45Z","receivedAt":"2016-11-14T19:37:35Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Mon, Nov 14, 2016 at 7:25 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>>>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n>>>> index 600b703..f4ad297 100644\n>>>> --- a/Documentation/git-for-each-ref.txt\n>>>> +++ b/Documentation/git-for-each-ref.txt\n>>>> @@ -96,7 +96,9 @@ refname::\n>>>>         slash-separated path components from the front of the refname\n>>>>         (e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.\n>>>>         `<N>` must be a positive integer.  If a displayed ref has fewer\n>>>> -       components than `<N>`, the command aborts with an error.\n>>>> +       components than `<N>`, the command aborts with an error. For the base\n>>>> +       directory of the ref (i.e. foo in refs/foo/bar/boz) append\n>>>> +       `:base`. For the entire directory path append `:dir`.\n>\n> Sorry that I missed this so far and I do not know how many recent\n> rerolls had them like this, but I am not sure about these :base and\n> :dir suffixes.  From their names I think readers would expect that\n> they do rough equivalents to basename() and dirname() applied to the\n> refname, but the example contradicts with that intuition.  The\n> result of applying basename() to 'refs/boo/bar/boz' would be 'boz'\n> and not 'foo' as the example says.\n>\n\nTrue that the options ':dir' and ':base' seem to be conflicting with\nthe use case\nof basename() and dirname(), These were names which I thought best fit\nthe options\nwith no relation to basename() and dirname(). But now that you mention\nit, it would\nmake sense to change these names to something more suitable, any suggestions\nwould be welcome.\n\n> So assuming that :base and :dir are unrelated to basename() and\n> dirname():\n>\n>  - I think calling these :base and :dir may be misleading\n>\n>  - More importantly, what do these do?  I do not think of a good\n>    description that generalizes \"base of refs/foo/bar/boz is foo\" to\n>    explain your :base.\n\n$ ./git for-each-ref --format \"%(refname)%(end) %(refname:dir)\"\nrefs/heads/master                  refs/heads\nrefs/heads/ref-filter                refs/heads\nrefs/remotes/junio/va/i18n     refs/remotes/junio/va\n\n$ ./git for-each-ref  refs/heads --format\n\"%(align:left,30)%(refname)%(end) %(refname:base)\"\nrefs/heads/master                 heads\nrefs/heads/ref-filter                heads\nrefs/remotes/junio/va/i18n     remotes\n\nI guess this should clear it up.\n\n>\n>  - A :dir that corresponds to the :base that picks 'foo' from\n>    'refs/foo/bar/boz' needs an example, too.\n>\n\nI could add in an example.\n\n> Or is the above example simply a typo?  Is refs/foo/bar/boz:base\n> 'boz', not 'foo'?\n>\n>\n\nNot a typo, but open to changes in name.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305915","messageId":"xmqqy40lx2k8.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"CAOLa=ZSFuq2+6xsrJ=CcXuOVbTnbDirbRtu7Fonfk+9EdRpbxg@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-14T19:51:35Z","receivedAt":"2016-11-14T19:51:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n>>  - More importantly, what do these do?  I do not think of a good\n>>    description that generalizes \"base of refs/foo/bar/boz is foo\" to\n>>    explain your :base.\n>\n> $ ./git for-each-ref --format \"%(refname)%(end) %(refname:dir)\"\n> refs/heads/master                  refs/heads\n> refs/heads/ref-filter                refs/heads\n> refs/remotes/junio/va/i18n     refs/remotes/junio/va\n>\n> $ ./git for-each-ref  refs/heads --format\n> \"%(align:left,30)%(refname)%(end) %(refname:base)\"\n> refs/heads/master                 heads\n> refs/heads/ref-filter                heads\n> refs/remotes/junio/va/i18n     remotes\n>\n> I guess this should clear it up.\n\nHmph.\n\nI would guess from these examples that :dir is an equivalent to\ndirname().  But it is unclear how :base is defined.  Is it the path\ncomponent that comes immediately after \"refs/\" that appears at the\nbeginning?\n"},{"id":"305933","messageId":"CA+P7+xpWTwA2S+nHDsX4pemCsw=XWToYOMkevPMdLZy3ywagWQ@mail.gmail.com","threadId":"44444","inReplyTo":"CAOLa=ZQSQe=jfTpdyscZCZgi5p6cVLN23oi2eE-hqFTXgt2LEQ@mail.gmail.com","subject":"Re: [PATCH v7 16/17] branch: use ref-filter printing APIs","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-15T01:36:00Z","receivedAt":"2016-11-15T01:36:25Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Mon, Nov 14, 2016 at 11:23 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> Hello\n>\n> On Wed, Nov 9, 2016 at 5:44 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n>> On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>>> From: Karthik Nayak <karthik.188@gmail.com>\n>>>\n>>> Port branch.c to use ref-filter APIs for printing. This clears out\n>>> most of the code used in branch.c for printing and replaces them with\n>>> calls made to the ref-filter library.\n>>\n>> Nice. This looks correct based on checking against the current\n>> branch.c implementation by hand. There was one minor change I\n>> suggested but I'm not really sure it buys is that much.\n>>\n>\n> Thanks for this review. More down.\n>\n>>> +               if (filter->verbose > 1)\n>>> +                       strbuf_addf(&local, \"%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)\"\n>>> +                                   \"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)\",\n>>> +                                   branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));\n>>\n>> When we have extra verbose, we check whether we have an upstream, and\n>> if so, we print the short name of that upstream inside brackets. If we\n>> have tracking information, we print that without brackets, and then we\n>> end this section. Finally we print the subject.\n>>\n>> We could almost re-use the code for the subject bits, but I'm not sure\n>> it's worth it. Maybe drop the %contents:subject part and add it\n>> afterwards since we always want it? It would remove some duplication\n>> but overall not sure it's actually worth it.\n>>\n>\n> If you see that's the last part we add to the 'local' strbuf in the\n> verbose case.\n> If we want to remove the duplication we'll end up adding one more\n> strbuf_addf(...).\n> So I guess its better this way.\n>\n\nAgreed, I think that it makes more sense to keep this as is. It is\nrelatively complicated and the strings do have some duplicate code,\nbut I think it's still ok.\n\nThanks,\nJake\n\n> --\n> Regards,\n> Karthik Nayak\n"},{"id":"305937","messageId":"CAOLa=ZQepW9GiUrKEWXojpy10B86K-jb84G_dJeL=mqtjZ4AWg@mail.gmail.com","threadId":"44444","inReplyTo":"xmqqy40lx2k8.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-15T06:48:15Z","receivedAt":"2016-11-15T06:48:52Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Tue, Nov 15, 2016 at 1:21 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>>>  - More importantly, what do these do?  I do not think of a good\n>>>    description that generalizes \"base of refs/foo/bar/boz is foo\" to\n>>>    explain your :base.\n>>\n>> $ ./git for-each-ref --format \"%(refname)%(end) %(refname:dir)\"\n>> refs/heads/master                  refs/heads\n>> refs/heads/ref-filter                refs/heads\n>> refs/remotes/junio/va/i18n     refs/remotes/junio/va\n>>\n>> $ ./git for-each-ref  refs/heads --format\n>> \"%(align:left,30)%(refname)%(end) %(refname:base)\"\n>> refs/heads/master                 heads\n>> refs/heads/ref-filter                heads\n>> refs/remotes/junio/va/i18n     remotes\n>>\n>> I guess this should clear it up.\n>\n> Hmph.\n>\n> I would guess from these examples that :dir is an equivalent to\n> dirname().  But it is unclear how :base is defined.  Is it the path\n> component that comes immediately after \"refs/\" that appears at the\n> beginning?\n\n':dir' is equivalent to dirname(). Yup, base is the folder right after 'refs/'.\nFor local branches it is 'heads' for remotes it is 'remotes'. This is useful\nwhen we want to make decisions based on the type of branch we're dealing\nwith (using along with %(if)...%(end)).\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"305939","messageId":"CA+P7+xo6OqcpLZ7v_m1EPm85eK2xCPD_LCw1Ly2RSPeSC0Ei7g@mail.gmail.com","threadId":"44444","inReplyTo":"CAOLa=ZQepW9GiUrKEWXojpy10B86K-jb84G_dJeL=mqtjZ4AWg@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-15T07:55:31Z","receivedAt":"2016-11-15T07:55:57Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Mon, Nov 14, 2016 at 10:48 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> On Tue, Nov 15, 2016 at 1:21 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Karthik Nayak <karthik.188@gmail.com> writes:\n>>\n>>>>  - More importantly, what do these do?  I do not think of a good\n>>>>    description that generalizes \"base of refs/foo/bar/boz is foo\" to\n>>>>    explain your :base.\n>>>\n>>> $ ./git for-each-ref --format \"%(refname)%(end) %(refname:dir)\"\n>>> refs/heads/master                  refs/heads\n>>> refs/heads/ref-filter                refs/heads\n>>> refs/remotes/junio/va/i18n     refs/remotes/junio/va\n>>>\n>>> $ ./git for-each-ref  refs/heads --format\n>>> \"%(align:left,30)%(refname)%(end) %(refname:base)\"\n>>> refs/heads/master                 heads\n>>> refs/heads/ref-filter                heads\n>>> refs/remotes/junio/va/i18n     remotes\n>>>\n>>> I guess this should clear it up.\n>>\n>> Hmph.\n>>\n>> I would guess from these examples that :dir is an equivalent to\n>> dirname().  But it is unclear how :base is defined.  Is it the path\n>> component that comes immediately after \"refs/\" that appears at the\n>> beginning?\n>\n> ':dir' is equivalent to dirname(). Yup, base is the folder right after 'refs/'.\n> For local branches it is 'heads' for remotes it is 'remotes'. This is useful\n> when we want to make decisions based on the type of branch we're dealing\n> with (using along with %(if)...%(end)).\n>\n> --\n> Regards,\n> Karthik Nayak\n\n\ndirname makes sense. What about implementing a reverse variant of\nstrip, which you could perform stripping of right-most components and\ninstead of stripping by a number, strip \"to\" a number, ie: keep the\nleft N most components, and then you could use something like\n\n\"keep=2\" to keep \"refs/heads\"\n\n?\n\nI think that would be more general purpose than basename, and less confusing?\n\nThanks,\nJake\n"},{"id":"305940","messageId":"CA+P7+xrrg8Dn26JX7LPGV+FFUsPe7XcoGm2btBN=-dX=cBLeMA@mail.gmail.com","threadId":"44444","inReplyTo":"CA+P7+xo6OqcpLZ7v_m1EPm85eK2xCPD_LCw1Ly2RSPeSC0Ei7g@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-15T07:56:12Z","receivedAt":"2016-11-15T07:56:37Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Mon, Nov 14, 2016 at 11:55 PM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Mon, Nov 14, 2016 at 10:48 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> On Tue, Nov 15, 2016 at 1:21 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> Karthik Nayak <karthik.188@gmail.com> writes:\n>>>\n>>>>>  - More importantly, what do these do?  I do not think of a good\n>>>>>    description that generalizes \"base of refs/foo/bar/boz is foo\" to\n>>>>>    explain your :base.\n>>>>\n>>>> $ ./git for-each-ref --format \"%(refname)%(end) %(refname:dir)\"\n>>>> refs/heads/master                  refs/heads\n>>>> refs/heads/ref-filter                refs/heads\n>>>> refs/remotes/junio/va/i18n     refs/remotes/junio/va\n>>>>\n>>>> $ ./git for-each-ref  refs/heads --format\n>>>> \"%(align:left,30)%(refname)%(end) %(refname:base)\"\n>>>> refs/heads/master                 heads\n>>>> refs/heads/ref-filter                heads\n>>>> refs/remotes/junio/va/i18n     remotes\n>>>>\n>>>> I guess this should clear it up.\n>>>\n>>> Hmph.\n>>>\n>>> I would guess from these examples that :dir is an equivalent to\n>>> dirname().  But it is unclear how :base is defined.  Is it the path\n>>> component that comes immediately after \"refs/\" that appears at the\n>>> beginning?\n>>\n>> ':dir' is equivalent to dirname(). Yup, base is the folder right after 'refs/'.\n>> For local branches it is 'heads' for remotes it is 'remotes'. This is useful\n>> when we want to make decisions based on the type of branch we're dealing\n>> with (using along with %(if)...%(end)).\n>>\n>> --\n>> Regards,\n>> Karthik Nayak\n>\n>\n> dirname makes sense. What about implementing a reverse variant of\n> strip, which you could perform stripping of right-most components and\n> instead of stripping by a number, strip \"to\" a number, ie: keep the\n> left N most components, and then you could use something like\n>\n> \"keep=2\" to keep \"refs/heads\"\n>\n> ?\n>\n> I think that would be more general purpose than basename, and less confusing?\n>\n> Thanks,\n> Jake\n\nYou could even make it compatible with strip so that:\n\n\"keep=2,strip=1\" would return the equivalent of base?\n\nThanks,\nJake\n"},{"id":"305966","messageId":"xmqq4m38vdw4.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"CA+P7+xo6OqcpLZ7v_m1EPm85eK2xCPD_LCw1Ly2RSPeSC0Ei7g@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-15T17:42:03Z","receivedAt":"2016-11-15T17:42:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.keller@gmail.com> writes:\n\n> dirname makes sense. What about implementing a reverse variant of\n> strip, which you could perform stripping of right-most components and\n> instead of stripping by a number, strip \"to\" a number, ie: keep the\n> left N most components, and then you could use something like\n> ...\n> I think that would be more general purpose than basename, and less confusing?\n\nI think you are going in the right direction.  I had a similar\nthought but built around a different axis.  I.e. if strip=1 strips\none from the left, perhaps we want to have rstrip=1 that strips one\nfrom the right, and also strip=-1 to mean strip everything except\none from the left and so on?.  I think this and your keep (and\nperhaps you'll have rkeep for completeness) have the same expressive\npower.  I do not offhand have a preference one over the other.\n\nSomehow it sounds a bit strange to me to treat 'remotes' as the same\nclass of token as 'heads' and 'tags' (I'd expect 'heads' and\n'remotes/origin' would be at the same level in end-user's mind), but\nthat is probably an unrelated tangent.  The reason this series wants\nto introduce :base must be to emulate an existing feature, so that\nexisting feature is a concrete counter-example that argues against\nmy \"it sounds a bit strange\" reaction.\n"},{"id":"305982","messageId":"xmqqbmxgtqxv.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"20161108201211.25213-1-Karthik.188@gmail.com","subject":"Re: [PATCH v7 00/17] port branch.c to use ref-filter's printing options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-15T20:43:08Z","receivedAt":"2016-11-15T20:43:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> This is part of unification of the commands 'git tag -l, git branch -l\n> and git for-each-ref'. This ports over branch.c to use ref-filter's\n> printing options.\n>\n> Karthik Nayak (17):\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 symref_atom_parser() and refname_atom_parser()\n>   ref-filter: make remote_ref_atom_parser() use\n>     refname_atom_parser_internal()\n>   ref-filter: add `:dir` and `:base` options for ref printing atoms\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\nThis is not a new issue, but --format='%(HEAD)' you stole from\nfor-each-ref is broken when you are on an unborn branch, and the\nsecond patch from the tip makes \"git branch\" (no other args) on\nan unborn branch to segfault, when there are real branches that\nhave commits.\n\nSomething like this needs to go before that step.\n\n ref-filter.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 944671af5a..c71d7360d2 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1318,7 +1318,7 @@ static void populate_value(struct ref_array_item *ref)\n \n \t\t\thead = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n \t\t\t\t\t\t  sha1, NULL);\n-\t\t\tif (!strcmp(ref->refname, head))\n+\t\t\tif (head && !strcmp(ref->refname, head))\n \t\t\t\tv->s = \"*\";\n \t\t\telse\n \t\t\t\tv->s = \" \";\n\n\n"},{"id":"305984","messageId":"xmqq7f84tqa7.fsf_-_@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"xmqqbmxgtqxv.fsf@gitster.mtv.corp.google.com","subject":"Re* [PATCH v7 00/17] port branch.c to use ref-filter's printing options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-15T20:57:20Z","receivedAt":"2016-11-15T20:57:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Something like this needs to go before that step.\n\nThis time with a log message and an additional test.  The second\nparagraph of the proposed log message is written expecting that this\npatch will come before your \"branch: use ref-filter printing APIs\",\nwhich I think is a good place to avoid breakage in the series.\n\n-- >8 --\nSubject: [PATCH] for-each-ref: do not segv with %(HEAD) on an unborn branch\n\nThe code to flip between \"*\" and \" \" prefixes depending on what\nbranch is checked out used in --format='%(HEAD)' did not consider\nthat HEAD may resolve to an unborn branch and dereferenced a NULL.\n\nThis will become a lot easier to trigger as the codepath will now be\nshared with \"git branch [--list]\".\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n ref-filter.c             | 2 +-\n t/t3203-branch-output.sh | 8 ++++++++\n 2 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 944671af5a..c71d7360d2 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1318,7 +1318,7 @@ static void populate_value(struct ref_array_item *ref)\n \n \t\t\thead = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n \t\t\t\t\t\t  sha1, NULL);\n-\t\t\tif (!strcmp(ref->refname, head))\n+\t\t\tif (head && !strcmp(ref->refname, head))\n \t\t\t\tv->s = \"*\";\n \t\t\telse\n \t\t\t\tv->s = \" \";\ndiff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh\nindex d8edaf27e9..1a8dbca8c8 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -37,6 +37,14 @@ test_expect_success 'git branch --list shows local branches' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'same, but on an unborn branch' '\n+\ttest_when_finished \"git checkout master\" &&\n+\tgit checkout --orphan naster &&\n+\tgit branch --list >actual &&\n+\tsed -e \"s/\\* /  /\" expect >expect-unborn &&\n+\ttest_cmp expect-unborn actual\n+'\n+\n cat >expect <<'EOF'\n   branch-one\n   branch-two\n-- \n2.11.0-rc1-154-gcd2a643dcd\n\n"},{"id":"306030","messageId":"FAA5CD51-8772-4AA9-A9BF-F32C9D0A47CA@gmail.com","threadId":"44444","inReplyTo":"xmqq4m38vdw4.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-15T21:19:04Z","receivedAt":"2016-11-16T01:33:51Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On November 15, 2016 9:42:03 AM PST, Junio C Hamano <gitster@pobox.com> wrote:\n>I think you are going in the right direction.  I had a similar\n>thought but built around a different axis.  I.e. if strip=1 strips\n>one from the left, perhaps we want to have rstrip=1 that strips one\n>from the right, and also strip=-1 to mean strip everything except\n>one from the left and so on?.  I think this and your keep (and\n>perhaps you'll have rkeep for completeness) have the same expressive\n>power.  I do not offhand have a preference one over the other.\n\nI prefer strip implemented with negative numbers. That is simple and expressive and if we need we can implement rstrip if necessary.\n\n>\n>Somehow it sounds a bit strange to me to treat 'remotes' as the same\n>class of token as 'heads' and 'tags' (I'd expect 'heads' and\n>'remotes/origin' would be at the same level in end-user's mind), but\n>that is probably an unrelated tangent.  The reason this series wants\n>to introduce :base must be to emulate an existing feature, so that\n>existing feature is a concrete counter-example that argues against\n>my \"it sounds a bit strange\" reaction.\n\nIt may be a bit strange indeed. What is the requirement for this?\n\nI think implementing a strip and rstrip ( if necessary ) with negative numbers would be most ideal.\n\nThanks\nJake\n\n\n"},{"id":"306036","messageId":"CAOLa=ZSaTdACC60g6D6k5frjKkChbkBL8+kLJjNgoutLSe8mOQ@mail.gmail.com","threadId":"44444","inReplyTo":"xmqq4m38vdw4.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-16T07:58:35Z","receivedAt":"2016-11-16T07:59:11Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Tue, Nov 15, 2016 at 11:12 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jacob Keller <jacob.keller@gmail.com> writes:\n>\n>> dirname makes sense. What about implementing a reverse variant of\n>> strip, which you could perform stripping of right-most components and\n>> instead of stripping by a number, strip \"to\" a number, ie: keep the\n>> left N most components, and then you could use something like\n>> ...\n>> I think that would be more general purpose than basename, and less confusing?\n>\n> I think you are going in the right direction.  I had a similar\n> thought but built around a different axis.  I.e. if strip=1 strips\n> one from the left, perhaps we want to have rstrip=1 that strips one\n> from the right, and also strip=-1 to mean strip everything except\n> one from the left and so on?.  I think this and your keep (and\n> perhaps you'll have rkeep for completeness) have the same expressive\n> power.  I do not offhand have a preference one over the other.\n>\n\nIf we do implement strip with negative numbers, it definitely would be\nneat, but to get\nthe desired feature which I've mentioned below, we'd need to call\nstrip twice, i.e\nto get remotes from /refs/foo/abc/xyz we'd need to do\nstrip=1,strip=-1, which could be\ndone but would need a lot of tweaking, since we have the same\nsubatom/option having\nmultiple values.\n\nOn the other hand it would be easier maybe to just introduce rstrip,\nwhere we strip from\nright and ensure that strip and rstrip can be used together.\n\nOn Wed, Nov 16, 2016 at 2:49 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On November 15, 2016 9:42:03 AM PST, Junio C Hamano <gitster@pobox.com> wrote:\n>>Somehow it sounds a bit strange to me to treat 'remotes' as the same\n>>class of token as 'heads' and 'tags' (I'd expect 'heads' and\n>>'remotes/origin' would be at the same level in end-user's mind), but\n>>that is probably an unrelated tangent.  The reason this series wants\n>>to introduce :base must be to emulate an existing feature, so that\n>>existing feature is a concrete counter-example that argues against\n>>my \"it sounds a bit strange\" reaction.\n>\n> It may be a bit strange indeed. What is the requirement for this?\n>\n> I think implementing a strip and rstrip ( if necessary ) with negative numbers would be most ideal.\n\nThe necessity is that we need to do different formatting as per the\nref type in branch -l. If you see the\nlast but one patch, we do\n\nstrbuf_addf(&fmt,\n\"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\",\nlocal.buf, remote.buf);\n\nwhere its using ':base' to check for the ref type and do conditional\nprinting along with the %(if)...%(end) atoms.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306055","messageId":"CAOLa=ZQtmQWpFMPa-SD29N7hASHAPp8SGGJsLu+AW_Kv-1LqwA@mail.gmail.com","threadId":"44444","inReplyTo":"xmqqbmxgtqxv.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 00/17] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-16T15:31:35Z","receivedAt":"2016-11-16T15:32:16Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Nov 16, 2016 at 2:13 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> This is part of unification of the commands 'git tag -l, git branch -l\n>> and git for-each-ref'. This ports over branch.c to use ref-filter's\n>> printing options.\n>>\n>> Karthik Nayak (17):\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 symref_atom_parser() and refname_atom_parser()\n>>   ref-filter: make remote_ref_atom_parser() use\n>>     refname_atom_parser_internal()\n>>   ref-filter: add `:dir` and `:base` options for ref printing atoms\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> This is not a new issue, but --format='%(HEAD)' you stole from\n> for-each-ref is broken when you are on an unborn branch, and the\n> second patch from the tip makes \"git branch\" (no other args) on\n> an unborn branch to segfault, when there are real branches that\n> have commits.\n>\n> Something like this needs to go before that step.\n>\n>  ref-filter.c | 2 +-\n>  1 file changed, 1 insertion(+), 1 deletion(-)\n>\n> diff --git a/ref-filter.c b/ref-filter.c\n> index 944671af5a..c71d7360d2 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -1318,7 +1318,7 @@ static void populate_value(struct ref_array_item *ref)\n>\n>                         head = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n>                                                   sha1, NULL);\n> -                       if (!strcmp(ref->refname, head))\n> +                       if (head && !strcmp(ref->refname, head))\n>                                 v->s = \"*\";\n>                         else\n>                                 v->s = \" \";\n>\n>\n\nThanks, will add it in.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306108","messageId":"xmqqmvgynee4.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"CAOLa=ZSaTdACC60g6D6k5frjKkChbkBL8+kLJjNgoutLSe8mOQ@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-17T18:35:15Z","receivedAt":"2016-11-17T18:35:23Z","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> On Tue, Nov 15, 2016 at 11:12 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Jacob Keller <jacob.keller@gmail.com> writes:\n>> ...\n>> I think you are going in the right direction.  I had a similar\n>> thought but built around a different axis.  I.e. if strip=1 strips\n>> one from the left, perhaps we want to have rstrip=1 that strips one\n>> from the right, and also strip=-1 to mean strip everything except\n>> one from the left and so on?\n> ...\n\n> If we do implement strip with negative numbers, it definitely\n> would be neat, but to get the desired feature which I've mentioned\n> below, we'd need to call strip twice, i.e\n> to get remotes from /refs/foo/abc/xyz we'd need to do\n> strip=1,strip=-1, which could be\n> done but ...\n\n... would be unnecessary if this is the only use case:\n\n> strbuf_addf(&fmt,\n> \"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\",\n> local.buf, remote.buf);\n\nYou can \"strip to leave only 2 components\" and compare the result\nwith refs/remotes instead, no?\n\n"},{"id":"306119","messageId":"xmqqinrlopge.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"20161108201211.25213-17-Karthik.188@gmail.com","subject":"Re: [PATCH v7 16/17] branch: use ref-filter printing APIs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-17T19:50:57Z","receivedAt":"2016-11-17T19:51:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> +static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)\n> +{\n\nI understand that the return value of this function is used as if\nthe value given via --format=... option to for-each-ref.\n\n> +\tstruct strbuf fmt = STRBUF_INIT;\n> +\tstruct strbuf local = STRBUF_INIT;\n> +\tstruct strbuf remote = STRBUF_INIT;\n> +\n> +\tstrbuf_addf(&fmt, \"%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)\", branch_get_color(BRANCH_COLOR_CURRENT));\n\nThis switches between \"* \" and \"  \" prefixed for each line of output\nin \"git branch --list\" output, where an asterisk is used to mark the\nbranch that is currently checked out.  OK.\n\n> +\tif (filter->verbose) {\n> +\t\tstrbuf_addf(&local, \"%%(align:%d,left)%%(refname:strip=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:strip=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,\n> +\t\t\t    remote_prefix, branch_get_color(BRANCH_COLOR_RESET));\n> +\t} else {\n> +\t\tstrbuf_addf(&local, \"%%(refname:strip=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:strip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n> +\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), remote_prefix, branch_get_color(BRANCH_COLOR_RESET));\n> +\t}\n\nThis block prepares \"local\" and \"remote\", two formats that are used\nfor local and remote branches.\n\n> +\tstrbuf_addf(&fmt, \"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\", local.buf, remote.buf);\n\nAnd this uses the %(if)...%(then)...%(else)...%(end) construct to\nswitch between these formats.\n\nSounds good.  \n\nOne worry that I have is if the strings embedded in this function to\nthe final format are safe.  As far as I can tell, the pieces of\nstrings that are literally inserted into the resulting format string\nby this function are maxwidth, remote_prefix, and return values from\nbranch_get_color() calls.\n\nThe maxwidth is inserted via \"%d\" and made into decimal constant,\nand there is no risk for it being in the resulting format.  Are\nthe return values of branch_get_color() calls safe?  I do not think\nthey can have '%' in them, but if they do, they need to be quoted.\nThe same worry exists for remote_prefix.  Currently it can either be\nan empty string or \"remotes/\", and is safe to be embedded in a\nformat string.\n"},{"id":"306126","messageId":"xmqqa8cxoj7k.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"xmqqinrlopge.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 16/17] branch: use ref-filter printing APIs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-17T22:05:51Z","receivedAt":"2016-11-17T22:06:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> One worry that I have is if the strings embedded in this function to\n> the final format are safe.  As far as I can tell, the pieces of\n> strings that are literally inserted into the resulting format string\n> by this function are maxwidth, remote_prefix, and return values from\n> branch_get_color() calls.\n>\n> The maxwidth is inserted via \"%d\" and made into decimal constant,\n> and there is no risk for it being in the resulting format.  Are\n> the return values of branch_get_color() calls safe?  I do not think\n> they can have '%' in them, but if they do, they need to be quoted.\n> The same worry exists for remote_prefix.  Currently it can either be\n> an empty string or \"remotes/\", and is safe to be embedded in a\n> format string.\n\nIn case it was not clear, in short, I do not think there is anything\nbroken in the code, but it is a longer-term improvement to introduce\na helper that takes a string and returns a version of the string\nthat is safely quoted to be used in the for-each-ref format string\nuse it like so:\n\n    strbuf_addf(&remote,\n\t\t\"%s\"\n\t\t\"%%(align:%d,left)%s%%(refname:strip=2)%%(end)\"\n\t\t...\n                \"%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)\",\n\t        quote_literal_for_format(branch_get_color(BRANCH_COLOR_REMOTE)),\n\t\t...);\n\nand the implementation of the helper may look like:\n\n    const char *quote_literal_for_format(const char *s)\n    {\n        static strbuf buf = STRBUF_INIT;\n\n        strbuf_reset(&buf);\n        while (*s) {\n            const char *ep = strchrnul(s, '%');\n            if (s < ep)\n                strbuf_add(&buf, s, ep - s);\n            if (*ep == '%') {\n                strbuf_addstr(&buf, \"%%\");\n                s = ep + 1;\n            } else {\n                s = ep;\n            }\n        }\n        return buf.buf;\n    }\n\n"},{"id":"306138","messageId":"CAOLa=ZTVTZ+1dXpcp=kdoGbT1Feq=vOfFpNpBiZepajMucraPQ@mail.gmail.com","threadId":"44444","inReplyTo":"xmqqmvgynee4.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-18T07:33:34Z","receivedAt":"2016-11-18T07:34:11Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hey,\n\nOn Fri, Nov 18, 2016 at 12:05 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> On Tue, Nov 15, 2016 at 11:12 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> Jacob Keller <jacob.keller@gmail.com> writes:\n>>> ...\n>>> I think you are going in the right direction.  I had a similar\n>>> thought but built around a different axis.  I.e. if strip=1 strips\n>>> one from the left, perhaps we want to have rstrip=1 that strips one\n>>> from the right, and also strip=-1 to mean strip everything except\n>>> one from the left and so on?\n>> ...\n>\n>> If we do implement strip with negative numbers, it definitely\n>> would be neat, but to get the desired feature which I've mentioned\n>> below, we'd need to call strip twice, i.e\n>> to get remotes from /refs/foo/abc/xyz we'd need to do\n>> strip=1,strip=-1, which could be\n>> done but ...\n>\n> ... would be unnecessary if this is the only use case:\n>\n>> strbuf_addf(&fmt,\n>> \"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\",\n>> local.buf, remote.buf);\n>\n> You can \"strip to leave only 2 components\" and compare the result\n> with refs/remotes instead, no?\n>\n\nOf course, my only objective was that someone would find it useful to\nhave these two additional\natoms. So if you think it's unnecessary we could drop it entirely :D\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306139","messageId":"CA+P7+xo847HRkm1Kur0mdB15OgwVMOckuK5fXNMkPxDGkK1XGA@mail.gmail.com","threadId":"44444","inReplyTo":"CAOLa=ZTVTZ+1dXpcp=kdoGbT1Feq=vOfFpNpBiZepajMucraPQ@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-11-18T08:19:42Z","receivedAt":"2016-11-18T08:20:09Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Thu, Nov 17, 2016 at 11:33 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> Hey,\n>\n> On Fri, Nov 18, 2016 at 12:05 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Karthik Nayak <karthik.188@gmail.com> writes:\n>>\n>>> On Tue, Nov 15, 2016 at 11:12 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>>>> Jacob Keller <jacob.keller@gmail.com> writes:\n>>>> ...\n>>>> I think you are going in the right direction.  I had a similar\n>>>> thought but built around a different axis.  I.e. if strip=1 strips\n>>>> one from the left, perhaps we want to have rstrip=1 that strips one\n>>>> from the right, and also strip=-1 to mean strip everything except\n>>>> one from the left and so on?\n>>> ...\n>>\n>>> If we do implement strip with negative numbers, it definitely\n>>> would be neat, but to get the desired feature which I've mentioned\n>>> below, we'd need to call strip twice, i.e\n>>> to get remotes from /refs/foo/abc/xyz we'd need to do\n>>> strip=1,strip=-1, which could be\n>>> done but ...\n>>\n>> ... would be unnecessary if this is the only use case:\n>>\n>>> strbuf_addf(&fmt,\n>>> \"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\",\n>>> local.buf, remote.buf);\n>>\n>> You can \"strip to leave only 2 components\" and compare the result\n>> with refs/remotes instead, no?\n>>\n>\n> Of course, my only objective was that someone would find it useful to\n> have these two additional\n> atoms. So if you think it's unnecessary we could drop it entirely :D\n>\n> --\n> Regards,\n> Karthik Nayak\n\nI think having strip and rstrip make sense, (along with support for\nnegative numbers) I don't think we need to make them work together\nunless someone is interested, since we can use strip=-2 to get the\nbehavior we need today.\n\nThanks,\nJake\n"},{"id":"306149","messageId":"xmqqpolsmz28.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"CA+P7+xo847HRkm1Kur0mdB15OgwVMOckuK5fXNMkPxDGkK1XGA@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-18T18:18:39Z","receivedAt":"2016-11-18T18:18:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.keller@gmail.com> writes:\n\n>>>> to get remotes from /refs/foo/abc/xyz we'd need to do\n>>>> strip=1,strip=-1, which could be\n>>>> done but ...\n>>>\n>>> ... would be unnecessary if this is the only use case:\n>>>\n>>>> strbuf_addf(&fmt,\n>>>> \"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\",\n>>>> local.buf, remote.buf);\n>>>\n>>> You can \"strip to leave only 2 components\" and compare the result\n>>> with refs/remotes instead, no?\n>>>\n>>\n>> Of course, my only objective was that someone would find it useful to\n>> have these two additional\n>> atoms. So if you think it's unnecessary we could drop it entirely :D\n>>\n>> --\n>> Regards,\n>> Karthik Nayak\n>\n> I think having strip and rstrip make sense, (along with support for\n> negative numbers) I don't think we need to make them work together\n> unless someone is interested, since we can use strip=-2 to get the\n> behavior we need today.\n\nI am OK with multiple strips Karthik suggests, e.g.\n\n    %(refname:strip=1,rstrip=-1)\n\nif it is cleanly implemented.\n\nI have a bit of trouble with these names, though.  If we call one\nstrip and the other rstrip, to only those who know about rstrip it\nwould be clear that strip is about stripping from the left.  Perhaps\nwe should call it lstrip for symmetry and ease-of-remembering?\n\n    refs/heads/master:lstrip=-1 => master (strip all but one level\n    from the left)\n\n    refs/heads/master:rstrip=-2 => refs/heads (strip all but two\n    levels from the right)\n\n    refs/heads/master:lstrip=1,rstrip=-1 => heads (strip one level\n    from the left and then strip all but one level from the right)\n\nI dunno.\n"},{"id":"306153","messageId":"37c2cbf2-7160-49d7-f8f1-3b65d9ecf9ec@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-4-Karthik.188@gmail.com","subject":"Re: [PATCH v7 03/17] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-11-18T19:58:30Z","receivedAt":"2016-11-18T19:58:39Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"W dniu 08.11.2016 o 21:11, Karthik Nayak pisze:\n> From: Karthik Nayak <karthik.188@gmail.com>\n> \n> Implement %(if:equals=<string>) wherein the if condition is only\n> satisfied if the value obtained between the %(if:...) and %(then) atom\n> is the same as the given '<string>'.\n> \n> Similarly, implement (if:notequals=<string>) wherein the if condition\n> is only satisfied if the value obtained between the %(if:...) and\n> %(then) atom is differnt from the given '<string>'.\n                  ^^^^^^^^\n\ns/differnt/different/   <-- typo\n\n> \n> This is done by introducing 'if_atom_parser()' which parses the given\n> %(if) atom and then stores the data in used_atom which is later passed\n> on to the used_atom of the %(then) atom, so that it can do the required\n> comparisons.\n\nNb. the syntax reminds me a bit of RPM SPEC language.\n\n> \n> Add tests and Documentation for the same.\n> \n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt |  3 +++\n>  ref-filter.c                       | 43 +++++++++++++++++++++++++++++++++-----\n>  t/t6302-for-each-ref-filter.sh     | 18 ++++++++++++++++\n>  3 files changed, 59 insertions(+), 5 deletions(-)\n> \n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index fed8126..b7b8560 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -155,6 +155,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\nSo %(if) is actually %(if:notempty) ?  Just kidding.\n\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\n> diff --git a/ref-filter.c b/ref-filter.c\n> index 8392303..44481c3 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -22,6 +22,8 @@ struct align {\n>  };\n>  \n>  struct if_then_else {\n> +\tconst char *if_equals,\n> +\t\t*not_equals;\n\nI guess using anonymous structs from C11 here...\n\n>  \tunsigned int then_atom_seen : 1,\n>  \t\telse_atom_seen : 1,\n>  \t\tcondition_satisfied : 1;\n> @@ -49,6 +51,10 @@ static struct used_atom {\n>  \t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n>  \t\t\tunsigned int nlines;\n>  \t\t} contents;\n> +\t\tstruct {\n> +\t\t\tconst char *if_equals,\n> +\t\t\t\t*not_equals;\n> +\t\t} if_then_else;\n\n...to avoid code duplication there is rather out of question?\n\n>  \t\tenum { O_FULL, O_SHORT } objectname;\n>  \t} u;\n>  } *used_atom;\n> @@ -169,6 +175,19 @@ 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\treturn;\n> +\telse if (skip_prefix(arg, \"equals=\", &atom->u.if_then_else.if_equals))\n> +\t\t ;\n> +\telse if (skip_prefix(arg, \"notequals=\", &atom->u.if_then_else.not_equals))\n> +\t\t;\n\nThose ';' should be perfectly aligned, isn't it?\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\nNice!\n\n-- \nJakub Narębski\n\n"},{"id":"306166","messageId":"ce2862d5-874b-f244-f9b3-f74e18f7ad42@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-10-Karthik.188@gmail.com","subject":"Re: [PATCH v7 09/17] ref-filter: make \"%(symref)\" atom work with the ':short' modifier","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-11-18T21:34:01Z","receivedAt":"2016-11-18T21:34:10Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"W dniu 08.11.2016 o 21:12, Karthik Nayak pisze:\n[...]\n \n> Add tests for %(symref) and %(symref:short) while we're here.\n\nThat's nice.\n\n> \n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Signed-off-by: Karthik Nayak <Karthik.188@gmail.com>\n> ---\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\nThis should be inside the relevant test, not outside.  In other\npatches in this series you are putting setup together with the\nrest of test, by using \"cat >expected <<-\\EOF\".\n\n> +\n> +test_expect_success 'Verify usage of %(symref) atom' '\n> +\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym > actual &&\n\nThis should be spelled \" >actual\", rather than \" > actual\"; there\nshould be no space between redirection and file name.\n\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\nSame here.\n\n> +\n>  test_done\n> \n\n-- \nJakub Narębski\n\n"},{"id":"306167","messageId":"5df0a607-4d83-8211-457a-96d7bde46eff@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-11-Karthik.188@gmail.com","subject":"Re: [PATCH v7 10/17] ref-filter: introduce refname_atom_parser_internal()","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-11-18T21:36:45Z","receivedAt":"2016-11-18T21:36:53Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"W dniu 08.11.2016 o 21:12, Karthik Nayak pisze:\n> From: Karthik Nayak <karthik.188@gmail.com>\n> \n> Since there are multiple atoms which print refs ('%(refname)',\n> '%(symref)', '%(push)', '%upstream'), it makes sense to have a common\n\nMinor typo; it should be: \"%(upstream)\"\n\n> ground for parsing them. This would allow us to share implementations of\n> the atom modifiers between these atoms.\n> \n> Introduce refname_atom_parser_internal() to act as a common parsing\n> function for ref printing atoms. This would eventually be used to\n> introduce refname_atom_parser() and symref_atom_parser() and also be\n> internally used in remote_ref_atom_parser().\n> \n> Helped-by: Jeff King <peff@peff.net>\n> Signed-off-by: Karthik Nayak <Karthik.188@gmail.com>\n> ---\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} \telse\n          ^^^^^^\n\nIt looks like you have spurious tab here.\n\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> \n\n"},{"id":"306170","messageId":"20d067ef-9e2c-0d1f-f81a-06c154e95e4f@gmail.com","threadId":"44444","inReplyTo":"xmqq4m38vdw4.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-11-18T21:49:44Z","receivedAt":"2016-11-18T21:49:51Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"W dniu 15.11.2016 o 18:42, Junio C Hamano pisze:\n> Jacob Keller <jacob.keller@gmail.com> writes:\n> \n>> dirname makes sense. What about implementing a reverse variant of\n>> strip, which you could perform stripping of right-most components and\n>> instead of stripping by a number, strip \"to\" a number, ie: keep the\n>> left N most components, and then you could use something like\n>> ...\n>> I think that would be more general purpose than basename, and less confusing?\n> \n> I think you are going in the right direction.  I had a similar\n> thought but built around a different axis.  I.e. if strip=1 strips\n> one from the left, perhaps we want to have rstrip=1 that strips one\n> from the right, and also strip=-1 to mean strip everything except\n> one from the left and so on?.  I think this and your keep (and\n> perhaps you'll have rkeep for completeness) have the same expressive\n> power.  I do not offhand have a preference one over the other.\n> \n> Somehow it sounds a bit strange to me to treat 'remotes' as the same\n> class of token as 'heads' and 'tags' (I'd expect 'heads' and\n> 'remotes/origin' would be at the same level in end-user's mind), but\n> that is probably an unrelated tangent.  The reason this series wants\n> to introduce :base must be to emulate an existing feature, so that\n> existing feature is a concrete counter-example that argues against\n> my \"it sounds a bit strange\" reaction.\n\nIf it is to implement the feature where we select if to display only\nlocal branches (refs/heads/**), only remote-tracking branches\n(refs/remotes/**/**), or only tags (refs/tags/**), then perhaps\n':category' or ':type' would make sense?\n\nAs in '%(refname:category)', e.g.\n\n  %(if:equals=heads)%(refname:category)%(then)...%(end)\n\n-- \nJakub Narębski\n\n"},{"id":"306174","messageId":"af0b7bdc-2b29-0d04-85f1-aa1d5a2ba549@gmail.com","threadId":"44444","inReplyTo":"20161108201211.25213-15-Karthik.188@gmail.com","subject":"Re: [PATCH v7 14/17] ref-filter: allow porcelain to translate messages in the output","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-11-18T22:46:51Z","receivedAt":"2016-11-18T22:46:59Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"W dniu 08.11.2016 o 21:12, Karthik Nayak pisze:\n> From: Karthik Nayak <karthik.188@gmail.com>\n> \n> Introduce setup_ref_filter_porcelain_msg() so that the messages used in\n> the atom %(upstream:track) can be translated if needed. This is needed\n> as we port branch.c to use ref-filter's printing API's.\n> \n> Written-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  ref-filter.c | 28 ++++++++++++++++++++++++----\n>  ref-filter.h |  2 ++\n>  2 files changed, 26 insertions(+), 4 deletions(-)\n> \n> diff --git a/ref-filter.c b/ref-filter.c\n> index b47b900..944671a 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -15,6 +15,26 @@\n>  #include \"version.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\"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\nDo I understand it correctly that this mechanism is here to avoid\nrepeated calls into gettext, as those messages would get repeated\nover and over; otherwise one would use foo = N_(\"...\") and _(foo),\nisn't it?\n\nI wonder if there is some way to avoid duplication here, but I don't\nsee anything easy and safe (e.g. against running setup_*() twice).\n\nBest,\n-- \nJakub Narębski\n\n"},{"id":"306181","messageId":"xmqqd1hsl5zm.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"CAOLa=ZQtmQWpFMPa-SD29N7hASHAPp8SGGJsLu+AW_Kv-1LqwA@mail.gmail.com","subject":"Re: [PATCH v7 00/17] port branch.c to use ref-filter's printing options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-18T23:31:57Z","receivedAt":"2016-11-18T23:32:06Z","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> Thanks, will add it in.\n\nOK, here is a reroll of what I sent earlier in\n\n    http://public-inbox.org/git/<xmqq7f84tqa7.fsf_-_@gitster.mtv.corp.google.com>\n\nbut rebased so that it can happen as a preparatory bugfix before\nyour series.  \n\nThe bug dates back to the very original implementation of %(HEAD) in\n7a48b83219 (\"for-each-ref: introduce %(HEAD) asterisk marker\",\n2013-11-18) and was moved to the current location in the v2.6 days\nat c95b758587 (\"ref-filter: move code from 'for-each-ref'\",\n2015-06-14).\n\n-- >8 --\nSubject: [PATCH] for-each-ref: do not segv with %(HEAD) on an unborn branch\n\nThe code to flip between \"*\" and \" \" prefixes depending on what\nbranch is checked out used in --format='%(HEAD)' did not consider\nthat HEAD may resolve to an unborn branch and dereferenced a NULL.\n\nThis will become a lot easier to trigger as the codepath will be\nused to reimplement \"git branch [--list]\" in the future.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n ref-filter.c            |  2 +-\n t/t6300-for-each-ref.sh | 10 ++++++++++\n 2 files changed, 11 insertions(+), 1 deletion(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex bc551a752c..d7e91a78da 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1017,7 +1017,7 @@ static void populate_value(struct ref_array_item *ref)\n \n \t\t\thead = resolve_ref_unsafe(\"HEAD\", RESOLVE_REF_READING,\n \t\t\t\t\t\t  sha1, NULL);\n-\t\t\tif (!strcmp(ref->refname, head))\n+\t\t\tif (head && !strcmp(ref->refname, head))\n \t\t\t\tv->s = \"*\";\n \t\t\telse\n \t\t\t\tv->s = \" \";\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 19a2823025..039509a9cb 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -553,4 +553,14 @@ test_expect_success 'Verify sort with multiple keys' '\n \t\trefs/tags/bogo refs/tags/master > actual &&\n \ttest_cmp expected actual\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+\tsed -e \"s/^\\* /  /\" actual >expect &&\n+\tgit checkout --orphan HEAD &&\n+\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.11.0-rc2-152-gc9ad1dc38a\n\n"},{"id":"306191","messageId":"CAOLa=ZSa7+N04wevEjHSnToub-12biJC5eeMpAFCcCwSsxHG_g@mail.gmail.com","threadId":"44444","inReplyTo":"xmqqd1hsl5zm.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 00/17] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-20T07:08:15Z","receivedAt":"2016-11-20T07:08:53Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Sat, Nov 19, 2016 at 5:01 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> Thanks, will add it in.\n>\n> OK, here is a reroll of what I sent earlier in\n>\n>     http://public-inbox.org/git/<xmqq7f84tqa7.fsf_-_@gitster.mtv.corp.google.com>\n>\n> but rebased so that it can happen as a preparatory bugfix before\n> your series.\n>\n> The bug dates back to the very original implementation of %(HEAD) in\n> 7a48b83219 (\"for-each-ref: introduce %(HEAD) asterisk marker\",\n> 2013-11-18) and was moved to the current location in the v2.6 days\n> at c95b758587 (\"ref-filter: move code from 'for-each-ref'\",\n> 2015-06-14).\n>\n\nI'll rebase on this patch. Thanks for your efforts.\n\nI assume you'll be merging it in before my series, so I wont be making\nit a part of my series.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306192","messageId":"CAOLa=ZQ1wPhd=aXck5e+Zd0020CbEwaohVX4eOGjy50BTqsTpA@mail.gmail.com","threadId":"44444","inReplyTo":"37c2cbf2-7160-49d7-f8f1-3b65d9ecf9ec@gmail.com","subject":"Re: [PATCH v7 03/17] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-20T07:23:17Z","receivedAt":"2016-11-20T07:23:55Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Sat, Nov 19, 2016 at 1:28 AM, Jakub Narębski <jnareb@gmail.com> wrote:\n> W dniu 08.11.2016 o 21:11, Karthik Nayak pisze:\n>> From: Karthik Nayak <karthik.188@gmail.com>\n>>\n>> Implement %(if:equals=<string>) wherein the if condition is only\n>> satisfied if the value obtained between the %(if:...) and %(then) atom\n>> is the same as the given '<string>'.\n>>\n>> Similarly, implement (if:notequals=<string>) wherein the if condition\n>> is only satisfied if the value obtained between the %(if:...) and\n>> %(then) atom is differnt from the given '<string>'.\n>                   ^^^^^^^^\n>\n> s/differnt/different/   <-- typo\n>\n\nWill change that.\n\n>>\n>> Add tests and Documentation for the same.\n>>\n>> Mentored-by: Christian Couder <christian.couder@gmail.com>\n>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>> ---\n>>  Documentation/git-for-each-ref.txt |  3 +++\n>>  ref-filter.c                       | 43 +++++++++++++++++++++++++++++++++-----\n>>  t/t6302-for-each-ref-filter.sh     | 18 ++++++++++++++++\n>>  3 files changed, 59 insertions(+), 5 deletions(-)\n>>\n>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n>> index fed8126..b7b8560 100644\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -155,6 +155,9 @@ if::\n>>       evaluating the string before %(then), this is useful when we\n>>       use the %(HEAD) atom which prints either \"*\" or \" \" and we\n>>       want to apply the 'if' condition only on the 'HEAD' ref.\n>\n> So %(if) is actually %(if:notempty) ?  Just kidding.\n>\n\nIt's not a bug, it's a feature ;)\n\n>> +     Append \":equals=<string>\" or \":notequals=<string>\" to compare\n>> +     the value between the %(if:...) and %(then) atoms with the\n>> +     given 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\n>> diff --git a/ref-filter.c b/ref-filter.c\n>> index 8392303..44481c3 100644\n>> --- a/ref-filter.c\n>> +++ b/ref-filter.c\n>> @@ -22,6 +22,8 @@ struct align {\n>>  };\n>>\n>>  struct if_then_else {\n>> +     const char *if_equals,\n>> +             *not_equals;\n>\n> I guess using anonymous structs from C11 here...\n>\n>>       unsigned int then_atom_seen : 1,\n>>               else_atom_seen : 1,\n>>               condition_satisfied : 1;\n>> @@ -49,6 +51,10 @@ static struct used_atom {\n>>                       enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n>>                       unsigned int nlines;\n>>               } contents;\n>> +             struct {\n>> +                     const char *if_equals,\n>> +                             *not_equals;\n>> +             } if_then_else;\n>\n> ...to avoid code duplication there is rather out of question?\n>\n\nI believe it holds better context without the use of anonymous structs/unions.\nBut that's my perspective, I wouldn't mind changing it.\n\n>>               enum { O_FULL, O_SHORT } objectname;\n>>       } u;\n>>  } *used_atom;\n>> @@ -169,6 +175,19 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)\n>>       string_list_clear(&params, 0);\n>>  }\n>>\n>> +static void if_atom_parser(struct used_atom *atom, const char *arg)\n>> +{\n>> +     if (!arg)\n>> +             return;\n>> +     else if (skip_prefix(arg, \"equals=\", &atom->u.if_then_else.if_equals))\n>> +              ;\n>> +     else if (skip_prefix(arg, \"notequals=\", &atom->u.if_then_else.not_equals))\n>> +             ;\n>\n> Those ';' should be perfectly aligned, isn't it?\n>\n\nThis should be dropped with the new changes made on this patch.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306193","messageId":"CAOLa=ZQJ+NQuGvxtaA5euspQtNZOZjU_cHRgyYv3z1AcV5d8-w@mail.gmail.com","threadId":"44444","inReplyTo":"ce2862d5-874b-f244-f9b3-f74e18f7ad42@gmail.com","subject":"Re: [PATCH v7 09/17] ref-filter: make \"%(symref)\" atom work with the ':short' modifier","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-20T07:31:25Z","receivedAt":"2016-11-20T07:32:02Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Sat, Nov 19, 2016 at 3:04 AM, Jakub Narębski <jnareb@gmail.com> wrote:\n> W dniu 08.11.2016 o 21:12, Karthik Nayak pisze:\n>>\n>> Helped-by: Junio C Hamano <gitster@pobox.com>\n>> Signed-off-by: Karthik Nayak <Karthik.188@gmail.com>\n>> ---\n> [...]\n>\n>> +test_expect_success 'Add symbolic ref for the following tests' '\n>> +     git symbolic-ref refs/heads/sym refs/heads/master\n>> +'\n>> +\n>> +cat >expected <<EOF\n>> +refs/heads/master\n>> +EOF\n>\n> This should be inside the relevant test, not outside.  In other\n> patches in this series you are putting setup together with the\n> rest of test, by using \"cat >expected <<-\\EOF\".\n>\n\nAh! That's because I was just trying to keep it consistent. These tests\nare added to t6300, where the `expected` block is usually outside the tests\nthemselves.\n\nThe other tests in the series are added to t6302, where we keep the `expected`\nblock within the tests themselves.\n\n>> +\n>> +test_expect_success 'Verify usage of %(symref) atom' '\n>> +     git for-each-ref --format=\"%(symref)\" refs/heads/sym > actual &&\n>\n> This should be spelled \" >actual\", rather than \" > actual\"; there\n> should be no space between redirection and file name.\n>\n>> +     test_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>> +     git for-each-ref --format=\"%(symref:short)\" refs/heads/sym > actual &&\n>> +     test_cmp expected actual\n>> +'\n>\n> Same here.\n>\n\nWill remove the space between '>' and 'actual', Thanks.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306194","messageId":"CAOLa=ZTVTTqRO+Gh2aFJfuSznmnF9zvWw4603kMSTv7_UEOJig@mail.gmail.com","threadId":"44444","inReplyTo":"5df0a607-4d83-8211-457a-96d7bde46eff@gmail.com","subject":"Re: [PATCH v7 10/17] ref-filter: introduce refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-20T07:34:15Z","receivedAt":"2016-11-20T07:34:51Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Sat, Nov 19, 2016 at 3:06 AM, Jakub Narębski <jnareb@gmail.com> wrote:\n> W dniu 08.11.2016 o 21:12, Karthik Nayak pisze:\n>> From: Karthik Nayak <karthik.188@gmail.com>\n>>\n>> Since there are multiple atoms which print refs ('%(refname)',\n>> '%(symref)', '%(push)', '%upstream'), it makes sense to have a common\n>\n> Minor typo; it should be: \"%(upstream)\"\n>\n\nWill fix that.\n\n>> ground for parsing them. This would allow us to share implementations of\n>> the atom modifiers between these atoms.\n>>\n>> Introduce refname_atom_parser_internal() to act as a common parsing\n>> function for ref printing atoms. This would eventually be used to\n>> introduce refname_atom_parser() and symref_atom_parser() and also be\n>> internally used in remote_ref_atom_parser().\n>>\n>> Helped-by: Jeff King <peff@peff.net>\n>> Signed-off-by: Karthik Nayak <Karthik.188@gmail.com>\n>> ---\n> [...]\n>\n>> +static void refname_atom_parser_internal(struct refname_atom *atom,\n>> +                                      const char *arg, const char *name)\n>> +{\n>> +     if (!arg)\n>> +             atom->option = R_NORMAL;\n>> +     else if (!strcmp(arg, \"short\"))\n>> +             atom->option = R_SHORT;\n>> +     else if (skip_prefix(arg, \"strip=\", &arg)) {\n>> +             atom->option = R_STRIP;\n>> +             if (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0)\n>> +                     die(_(\"positive value expected refname:strip=%s\"), arg);\n>> +     }       else\n>           ^^^^^^\n>\n> It looks like you have spurious tab here.\n>\n\nThat would have gone unnoticed, thanks for pointing it out.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306197","messageId":"CAOLa=ZRf+vPOPK=ovP7JmJ52qdgwuqkpGH4UfP=+caQeyu9Ucw@mail.gmail.com","threadId":"44444","inReplyTo":"20d067ef-9e2c-0d1f-f81a-06c154e95e4f@gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-20T15:16:03Z","receivedAt":"2016-11-20T15:16:40Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Fri, Nov 18, 2016 at 11:48 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jacob Keller <jacob.keller@gmail.com> writes:\n>\n>>>>> to get remotes from /refs/foo/abc/xyz we'd need to do\n>>>>> strip=1,strip=-1, which could be\n>>>>> done but ...\n>>>>\n>>>> ... would be unnecessary if this is the only use case:\n>>>>\n>>>>> strbuf_addf(&fmt,\n>>>>> \"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\",\n>>>>> local.buf, remote.buf);\n>>>>\n>>>> You can \"strip to leave only 2 components\" and compare the result\n>>>> with refs/remotes instead, no?\n>>>>\n>>>\n>>> Of course, my only objective was that someone would find it useful to\n>>> have these two additional\n>>> atoms. So if you think it's unnecessary we could drop it entirely :D\n>>>\n>>> --\n>>> Regards,\n>>> Karthik Nayak\n>>\n>> I think having strip and rstrip make sense, (along with support for\n>> negative numbers) I don't think we need to make them work together\n>> unless someone is interested, since we can use strip=-2 to get the\n>> behavior we need today.\n>\n> I am OK with multiple strips Karthik suggests, e.g.\n>\n>     %(refname:strip=1,rstrip=-1)\n>\n> if it is cleanly implemented.\n>\n> I have a bit of trouble with these names, though.  If we call one\n> strip and the other rstrip, to only those who know about rstrip it\n> would be clear that strip is about stripping from the left.  Perhaps\n> we should call it lstrip for symmetry and ease-of-remembering?\n>\n>     refs/heads/master:lstrip=-1 => master (strip all but one level\n>     from the left)\n>\n>     refs/heads/master:rstrip=-2 => refs/heads (strip all but two\n>     levels from the right)\n>\n>     refs/heads/master:lstrip=1,rstrip=-1 => heads (strip one level\n>     from the left and then strip all but one level from the right)\n>\n> I dunno.\n\nWe could have lstrip and rstrip as you suggested and perhaps make it work\ntogether too. But I see this going off the scope of this series. Maybe\nI'll follow up\nwith another series introducing these features. Since we can currently\nmake do with\n'strip=2' I'll drop this patch from v8 of this series and pursue this\nidea after this.\n\nOn Sat, Nov 19, 2016 at 3:19 AM, Jakub Narębski <jnareb@gmail.com> wrote:\n> W dniu 15.11.2016 o 18:42, Junio C Hamano pisze:\n>> Jacob Keller <jacob.keller@gmail.com> writes:\n>>\n>>> dirname makes sense. What about implementing a reverse variant of\n>>> strip, which you could perform stripping of right-most components and\n>>> instead of stripping by a number, strip \"to\" a number, ie: keep the\n>>> left N most components, and then you could use something like\n>>> ...\n>>> I think that would be more general purpose than basename, and less confusing?\n>>\n>> I think you are going in the right direction.  I had a similar\n>> thought but built around a different axis.  I.e. if strip=1 strips\n>> one from the left, perhaps we want to have rstrip=1 that strips one\n>> from the right, and also strip=-1 to mean strip everything except\n>> one from the left and so on?.  I think this and your keep (and\n>> perhaps you'll have rkeep for completeness) have the same expressive\n>> power.  I do not offhand have a preference one over the other.\n>>\n>> Somehow it sounds a bit strange to me to treat 'remotes' as the same\n>> class of token as 'heads' and 'tags' (I'd expect 'heads' and\n>> 'remotes/origin' would be at the same level in end-user's mind), but\n>> that is probably an unrelated tangent.  The reason this series wants\n>> to introduce :base must be to emulate an existing feature, so that\n>> existing feature is a concrete counter-example that argues against\n>> my \"it sounds a bit strange\" reaction.\n>\n> If it is to implement the feature where we select if to display only\n> local branches (refs/heads/**), only remote-tracking branches\n> (refs/remotes/**/**), or only tags (refs/tags/**), then perhaps\n> ':category' or ':type' would make sense?\n>\n> As in '%(refname:category)', e.g.\n>\n>   %(if:equals=heads)%(refname:category)%(then)...%(end)\n>\n\nThis is also a good idea but would bring about the same confusion that Junio\nwas referring to, i.e.\n\n\"Somehow it sounds a bit strange to me to treat 'remotes' as the same\nclass of token as 'heads' and 'tags' (I'd expect 'heads' and\n'remotes/origin' would be at the same level in end-user's mind), but\nthat is probably an unrelated tangent.  The reason this series wants\nto introduce :base must be to emulate an existing feature, so that\nexisting feature is a concrete counter-example that argues against\nmy \"it sounds a bit strange\" reaction.\"\n\nSo right now the rstrip/lstrip idea seems to be a good way to go about\nthis, but I\nthink that'd be after this series.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306198","messageId":"CAOLa=ZRU--FyFWNvv7RGH2mho36hvUu2ye=JOapXZ6QoEy=nRw@mail.gmail.com","threadId":"44444","inReplyTo":"af0b7bdc-2b29-0d04-85f1-aa1d5a2ba549@gmail.com","subject":"Re: [PATCH v7 14/17] ref-filter: allow porcelain to translate messages in the output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-20T15:33:28Z","receivedAt":"2016-11-20T15:34:04Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"cc'in Matthieu since he wrote the patch.\n\nOn Sat, Nov 19, 2016 at 4:16 AM, Jakub Narębski <jnareb@gmail.com> wrote:\n> W dniu 08.11.2016 o 21:12, Karthik Nayak pisze:\n>> From: Karthik Nayak <karthik.188@gmail.com>\n>>\n>> Introduce setup_ref_filter_porcelain_msg() so that the messages used in\n>> the atom %(upstream:track) can be translated if needed. This is needed\n>> as we port branch.c to use ref-filter's printing API's.\n>>\n>> Written-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>> Mentored-by: Christian Couder <christian.couder@gmail.com>\n>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>> ---\n>>  ref-filter.c | 28 ++++++++++++++++++++++++----\n>>  ref-filter.h |  2 ++\n>>  2 files changed, 26 insertions(+), 4 deletions(-)\n>>\n>> diff --git a/ref-filter.c b/ref-filter.c\n>> index b47b900..944671a 100644\n>> --- a/ref-filter.c\n>> +++ b/ref-filter.c\n>> @@ -15,6 +15,26 @@\n>>  #include \"version.h\"\n>>  #include \"wt-status.h\"\n>>\n>> +static struct ref_msg {\n>> +     const char *gone;\n>> +     const char *ahead;\n>> +     const char *behind;\n>> +     const char *ahead_behind;\n>> +} msgs = {\n>> +     \"gone\",\n>> +     \"ahead %d\",\n>> +     \"behind %d\",\n>> +     \"ahead %d, behind %d\"\n>> +};\n>> +\n>> +void setup_ref_filter_porcelain_msg(void)\n>> +{\n>> +     msgs.gone = _(\"gone\");\n>> +     msgs.ahead = _(\"ahead %d\");\n>> +     msgs.behind = _(\"behind %d\");\n>> +     msgs.ahead_behind = _(\"ahead %d, behind %d\");\n>> +}\n>\n> Do I understand it correctly that this mechanism is here to avoid\n> repeated calls into gettext, as those messages would get repeated\n> over and over; otherwise one would use foo = N_(\"...\") and _(foo),\n> isn't it?\n>\n> I wonder if there is some way to avoid duplication here, but I don't\n> see anything easy and safe (e.g. against running setup_*() twice).\n>\n\nThat is the intention.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306199","messageId":"CAOLa=ZRL=UrfjZ0AypY5d_=-bi6JVfLsx5_3sVvf_rOapJ+m5g@mail.gmail.com","threadId":"44444","inReplyTo":"CAOLa=ZRf+vPOPK=ovP7JmJ52qdgwuqkpGH4UfP=+caQeyu9Ucw@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-20T16:52:10Z","receivedAt":"2016-11-20T16:52:47Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Sun, Nov 20, 2016 at 8:46 PM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> On Fri, Nov 18, 2016 at 11:48 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Jacob Keller <jacob.keller@gmail.com> writes:\n>>\n>>>>>> to get remotes from /refs/foo/abc/xyz we'd need to do\n>>>>>> strip=1,strip=-1, which could be\n>>>>>> done but ...\n>>>>>\n>>>>> ... would be unnecessary if this is the only use case:\n>>>>>\n>>>>>> strbuf_addf(&fmt,\n>>>>>> \"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\",\n>>>>>> local.buf, remote.buf);\n>>>>>\n>>>>> You can \"strip to leave only 2 components\" and compare the result\n>>>>> with refs/remotes instead, no?\n>>>>>\n>>>>\n>>>> Of course, my only objective was that someone would find it useful to\n>>>> have these two additional\n>>>> atoms. So if you think it's unnecessary we could drop it entirely :D\n>>>>\n>>>> --\n>>>> Regards,\n>>>> Karthik Nayak\n>>>\n>>> I think having strip and rstrip make sense, (along with support for\n>>> negative numbers) I don't think we need to make them work together\n>>> unless someone is interested, since we can use strip=-2 to get the\n>>> behavior we need today.\n>>\n>> I am OK with multiple strips Karthik suggests, e.g.\n>>\n>>     %(refname:strip=1,rstrip=-1)\n>>\n>> if it is cleanly implemented.\n>>\n>> I have a bit of trouble with these names, though.  If we call one\n>> strip and the other rstrip, to only those who know about rstrip it\n>> would be clear that strip is about stripping from the left.  Perhaps\n>> we should call it lstrip for symmetry and ease-of-remembering?\n>>\n>>     refs/heads/master:lstrip=-1 => master (strip all but one level\n>>     from the left)\n>>\n>>     refs/heads/master:rstrip=-2 => refs/heads (strip all but two\n>>     levels from the right)\n>>\n>>     refs/heads/master:lstrip=1,rstrip=-1 => heads (strip one level\n>>     from the left and then strip all but one level from the right)\n>>\n>> I dunno.\n>\n> We could have lstrip and rstrip as you suggested and perhaps make it work\n> together too. But I see this going off the scope of this series. Maybe\n> I'll follow up\n> with another series introducing these features. Since we can currently\n> make do with\n> 'strip=2' I'll drop this patch from v8 of this series and pursue this\n> idea after this.\n>\n\nI meant 'strip=-2'. I mean I'll add in the negative striping in this\nseries and follow\nup with something that'd introduce lstrip and rstrip.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306200","messageId":"xmqq4m32kqet.fsf@gitster.mtv.corp.google.com","threadId":"44444","inReplyTo":"CAOLa=ZRf+vPOPK=ovP7JmJ52qdgwuqkpGH4UfP=+caQeyu9Ucw@mail.gmail.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-11-20T17:32:58Z","receivedAt":"2016-11-20T17:33:12Z","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> We could have lstrip and rstrip as you suggested and perhaps make it work\n> together too. But I see this going off the scope of this series. Maybe\n> I'll follow up\n> with another series introducing these features. Since we can currently\n> make do with\n> 'strip=2' I'll drop this patch from v8 of this series and pursue this\n> idea after this.\n\nMy primary point was that if we know we want to add \"rstrip\" later\nand still decide not to add it right now, it is OK, but we will\nregret it if we named the one we are going to add right now \"strip\".\nThat will mean that future users, when \"rstrip\" is introduced, will\nend up having to choose between \"strip\" and \"rstrip\" (as opposed to\n\"lstrip\" and \"rstrip\"), wondering why left-variant is more important\nand named without left/right prefix.\n\n"},{"id":"306201","messageId":"51aea3cf-eaba-f909-faa0-00d3998c0d6c@gmail.com","threadId":"44444","inReplyTo":"xmqq4m32kqet.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-11-20T18:43:07Z","receivedAt":"2016-11-20T18:43:20Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"W dniu 20.11.2016 o 18:32, Junio C Hamano pisze:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n> \n>> We could have lstrip and rstrip as you suggested and perhaps make\n>> it work together too. But I see this going off the scope of this\n>> series. Maybe I'll follow up with another series introducing these\n>> features. Since we can currently make do with 'strip=2' I'll drop\n>> this patch from v8 of this series and pursue this idea after this.\n> \n> My primary point was that if we know we want to add \"rstrip\" later\n> and still decide not to add it right now, it is OK, but we will\n> regret it if we named the one we are going to add right now \"strip\".\n> That will mean that future users, when \"rstrip\" is introduced, will\n> end up having to choose between \"strip\" and \"rstrip\" (as opposed to\n> \"lstrip\" and \"rstrip\"), wondering why left-variant is more important\n> and named without left/right prefix.\n\nAnother solution would be to implement 'splice=<offset>[,<length>]',\nwhere if length is omitted it means to the end; perhaps with special\ncasing (as in Perl) of negative <offset> and/or negative <length>.\n\nOr implement POSIX shell expansion:\n\n  %(parameter%word)  - Remove Smallest Suffix Glob Pattern.\n  %(parameter%%word) - Remove Largest Suffix Glob Pattern.\n  %(parameter#word)  - Remove Smallest Prefix Pattern.\n  %(parameter##word) - Remove Largest Prefix Pattern.\n\nThough this one looks like serious overkill...\n\n-- \nJakub Narębski\n\n"},{"id":"306213","messageId":"vpq1sy58bsl.fsf@anie.imag.fr","threadId":"44444","inReplyTo":"CAOLa=ZRU--FyFWNvv7RGH2mho36hvUu2ye=JOapXZ6QoEy=nRw@mail.gmail.com","subject":"Re: [PATCH v7 14/17] ref-filter: allow porcelain to translate messages in the output","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2016-11-21T08:41:46Z","receivedAt":"2016-11-21T08:42:08Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> cc'in Matthieu since he wrote the patch.\n>\n> On Sat, Nov 19, 2016 at 4:16 AM, Jakub Narębski <jnareb@gmail.com> wrote:\n>> W dniu 08.11.2016 o 21:12, Karthik Nayak pisze:\n>>> From: Karthik Nayak <karthik.188@gmail.com>\n>>>\n>>> Introduce setup_ref_filter_porcelain_msg() so that the messages used in\n>>> the atom %(upstream:track) can be translated if needed. This is needed\n>>> as we port branch.c to use ref-filter's printing API's.\n>>>\n>>> Written-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>>> Mentored-by: Christian Couder <christian.couder@gmail.com>\n>>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>>> ---\n>>>  ref-filter.c | 28 ++++++++++++++++++++++++----\n>>>  ref-filter.h |  2 ++\n>>>  2 files changed, 26 insertions(+), 4 deletions(-)\n>>>\n>>> diff --git a/ref-filter.c b/ref-filter.c\n>>> index b47b900..944671a 100644\n>>> --- a/ref-filter.c\n>>> +++ b/ref-filter.c\n>>> @@ -15,6 +15,26 @@\n>>>  #include \"version.h\"\n>>>  #include \"wt-status.h\"\n>>>\n>>> +static struct ref_msg {\n>>> +     const char *gone;\n>>> +     const char *ahead;\n>>> +     const char *behind;\n>>> +     const char *ahead_behind;\n>>> +} msgs = {\n>>> +     \"gone\",\n>>> +     \"ahead %d\",\n>>> +     \"behind %d\",\n>>> +     \"ahead %d, behind %d\"\n>>> +};\n>>> +\n>>> +void setup_ref_filter_porcelain_msg(void)\n>>> +{\n>>> +     msgs.gone = _(\"gone\");\n>>> +     msgs.ahead = _(\"ahead %d\");\n>>> +     msgs.behind = _(\"behind %d\");\n>>> +     msgs.ahead_behind = _(\"ahead %d, behind %d\");\n>>> +}\n>>\n>> Do I understand it correctly that this mechanism is here to avoid\n>> repeated calls into gettext, as those messages would get repeated\n>> over and over; otherwise one would use foo = N_(\"...\") and _(foo),\n>> isn't it?\n\nThat's not the primary goal. The primary goal is to keep untranslated,\nand immutable messages in plumbing commands. We may decide one day that\n_(\"gone\") is not the best message for the end user and replace it with,\nsay, _(\"vanished\"), but the \"gone\" has to remain the same forever and\nregardless of the user's config for scripts using it.\n\nWe could have written\n\n  in_porcelain ? _(\"gone\") : \"gone\"\n\nhere and there in the code, but having a single place where we set all\nthe messages seems simpler. Call setup_ref_filter_porcelain_msg() and\nget the porcelain messages, don't call it and keep the plumbing\nmessages.\n\nNote that it's not the first place in the code where we do this, see\nsetup_unpack_trees_porcelain in unpack-trees.c for another instance.\n\nKarthik: the commit message could be improved, for example:\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\nWhy not a comment right below \"} msgs = {\" saying e.g.:\n\n\t/* Untranslated plumbing messages: */\n\n>> I wonder if there is some way to avoid duplication here, but I don't\n>> see anything easy and safe (e.g. against running setup_*() twice).\n\nI think we do need duplication for the reason above.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"306326","messageId":"CAOLa=ZT3eFZ+rf8t-TN5KSxjg2EDszV+DJM=CA=b6fGfgBnN0g@mail.gmail.com","threadId":"44444","inReplyTo":"xmqqa8cxoj7k.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 16/17] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-22T18:31:55Z","receivedAt":"2016-11-22T18:32:58Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Fri, Nov 18, 2016 at 3:35 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> One worry that I have is if the strings embedded in this function to\n>> the final format are safe.  As far as I can tell, the pieces of\n>> strings that are literally inserted into the resulting format string\n>> by this function are maxwidth, remote_prefix, and return values from\n>> branch_get_color() calls.\n>>\n>> The maxwidth is inserted via \"%d\" and made into decimal constant,\n>> and there is no risk for it being in the resulting format.  Are\n>> the return values of branch_get_color() calls safe?  I do not think\n>> they can have '%' in them, but if they do, they need to be quoted.\n>> The same worry exists for remote_prefix.  Currently it can either be\n>> an empty string or \"remotes/\", and is safe to be embedded in a\n>> format string.\n>\n> In case it was not clear, in short, I do not think there is anything\n> broken in the code, but it is a longer-term improvement to introduce\n> a helper that takes a string and returns a version of the string\n> that is safely quoted to be used in the for-each-ref format string\n> use it like so:\n>\n>     strbuf_addf(&remote,\n>                 \"%s\"\n>                 \"%%(align:%d,left)%s%%(refname:strip=2)%%(end)\"\n>                 ...\n>                 \"%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)\",\n>                 quote_literal_for_format(branch_get_color(BRANCH_COLOR_REMOTE)),\n>                 ...);\n>\n> and the implementation of the helper may look like:\n>\n>     const char *quote_literal_for_format(const char *s)\n>     {\n>         static strbuf buf = STRBUF_INIT;\n>\n>         strbuf_reset(&buf);\n>         while (*s) {\n>             const char *ep = strchrnul(s, '%');\n>             if (s < ep)\n>                 strbuf_add(&buf, s, ep - s);\n>             if (*ep == '%') {\n>                 strbuf_addstr(&buf, \"%%\");\n>                 s = ep + 1;\n>             } else {\n>                 s = ep;\n>             }\n>         }\n>         return buf.buf;\n>     }\n>\n\nPerfect. I get what you're saying, I'll add this in :)\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306327","messageId":"CAOLa=ZTQagQdHAdj0TcOs6OYjw2o+sYFbEV8kRG0SoUiud61=g@mail.gmail.com","threadId":"44444","inReplyTo":"vpq1sy58bsl.fsf@anie.imag.fr","subject":"Re: [PATCH v7 14/17] ref-filter: allow porcelain to translate messages in the output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-22T18:33:15Z","receivedAt":"2016-11-22T18:33:58Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Mon, Nov 21, 2016 at 2:11 PM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> cc'in Matthieu since he wrote the patch.\n>>\n>> On Sat, Nov 19, 2016 at 4:16 AM, Jakub Narębski <jnareb@gmail.com> wrote:\n>>> W dniu 08.11.2016 o 21:12, Karthik Nayak pisze:\n>>>> From: Karthik Nayak <karthik.188@gmail.com>\n>>>>\n>>>> Introduce setup_ref_filter_porcelain_msg() so that the messages used in\n>>>> the atom %(upstream:track) can be translated if needed. This is needed\n>>>> as we port branch.c to use ref-filter's printing API's.\n>>>>\n>>>> Written-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>>>> Mentored-by: Christian Couder <christian.couder@gmail.com>\n>>>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>>>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>>>> ---\n>>>>  ref-filter.c | 28 ++++++++++++++++++++++++----\n>>>>  ref-filter.h |  2 ++\n>>>>  2 files changed, 26 insertions(+), 4 deletions(-)\n>>>>\n>>>> diff --git a/ref-filter.c b/ref-filter.c\n>>>> index b47b900..944671a 100644\n>>>> --- a/ref-filter.c\n>>>> +++ b/ref-filter.c\n>>>> @@ -15,6 +15,26 @@\n>>>>  #include \"version.h\"\n>>>>  #include \"wt-status.h\"\n>>>>\n>>>> +static struct ref_msg {\n>>>> +     const char *gone;\n>>>> +     const char *ahead;\n>>>> +     const char *behind;\n>>>> +     const char *ahead_behind;\n>>>> +} msgs = {\n>>>> +     \"gone\",\n>>>> +     \"ahead %d\",\n>>>> +     \"behind %d\",\n>>>> +     \"ahead %d, behind %d\"\n>>>> +};\n>>>> +\n>>>> +void setup_ref_filter_porcelain_msg(void)\n>>>> +{\n>>>> +     msgs.gone = _(\"gone\");\n>>>> +     msgs.ahead = _(\"ahead %d\");\n>>>> +     msgs.behind = _(\"behind %d\");\n>>>> +     msgs.ahead_behind = _(\"ahead %d, behind %d\");\n>>>> +}\n>>>\n>>> Do I understand it correctly that this mechanism is here to avoid\n>>> repeated calls into gettext, as those messages would get repeated\n>>> over and over; otherwise one would use foo = N_(\"...\") and _(foo),\n>>> isn't it?\n>\n> That's not the primary goal. The primary goal is to keep untranslated,\n> and immutable messages in plumbing commands. We may decide one day that\n> _(\"gone\") is not the best message for the end user and replace it with,\n> say, _(\"vanished\"), but the \"gone\" has to remain the same forever and\n> regardless of the user's config for scripts using it.\n>\n> We could have written\n>\n>   in_porcelain ? _(\"gone\") : \"gone\"\n>\n> here and there in the code, but having a single place where we set all\n> the messages seems simpler. Call setup_ref_filter_porcelain_msg() and\n> get the porcelain messages, don't call it and keep the plumbing\n> messages.\n>\n> Note that it's not the first place in the code where we do this, see\n> setup_unpack_trees_porcelain in unpack-trees.c for another instance.\n>\n> Karthik: the commit message could be improved, for example:\n>\n> Introduce setup_ref_filter_porcelain_msg() so that the messages used in\n> the atom %(upstream:track) can be translated if needed. By default, keep\n> the messages untranslated, which is the right behavior for plumbing\n> commands. This is needed as we port branch.c to use ref-filter's\n> printing API's.\n>\n> Why not a comment right below \"} msgs = {\" saying e.g.:\n>\n>         /* Untranslated plumbing messages: */\n>\n\nWill update the commit message and add the comment. Thanks :)\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"306328","messageId":"CAOLa=ZSx=FbBTB1BGMFS4ppp=u-C=TAHBSzwn9-Ex-7ZZj20bA@mail.gmail.com","threadId":"44444","inReplyTo":"xmqq4m32kqet.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v7 13/17] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-11-22T18:34:32Z","receivedAt":"2016-11-22T18:35:07Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Sun, Nov 20, 2016 at 11:02 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> We could have lstrip and rstrip as you suggested and perhaps make it work\n>> together too. But I see this going off the scope of this series. Maybe\n>> I'll follow up\n>> with another series introducing these features. Since we can currently\n>> make do with\n>> 'strip=2' I'll drop this patch from v8 of this series and pursue this\n>> idea after this.\n>\n> My primary point was that if we know we want to add \"rstrip\" later\n> and still decide not to add it right now, it is OK, but we will\n> regret it if we named the one we are going to add right now \"strip\".\n> That will mean that future users, when \"rstrip\" is introduced, will\n> end up having to choose between \"strip\" and \"rstrip\" (as opposed to\n> \"lstrip\" and \"rstrip\"), wondering why left-variant is more important\n> and named without left/right prefix.\n>\n\nThat's a good point actually, I'll try and implement both and re-roll.\n\n-- \nRegards,\nKarthik Nayak\n"}]}