{"thread":{"id":"44642","subject":"[PATCH v8 02/19] ref-filter: include reference to 'used_atom' within 'atom_value'","startedAt":"2016-12-07T15:36:59Z","lastAt":"2016-12-12T17:19:03Z","messageCount":32,"participants":["Karthik Nayak","Jacob Keller","Junio C Hamano","Jeff King"],"isPatch":true,"patchVersion":8,"patchTotal":19},"messages":[{"id":"307118","messageId":"20161207153627.1468-3-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 02/19] ref-filter: include reference to 'used_atom' within 'atom_value'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:10Z","receivedAt":"2016-12-07T15:36:59Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\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 2fed7fe..5166326 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@@ -1070,6 +1068,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@@ -1134,7 +1133,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":"307119","messageId":"20161207153627.1468-1-Karthik.188@gmail.com","threadId":"44642","inReplyTo":null,"subject":"[PATCH v8 00/19] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:08Z","receivedAt":"2016-12-07T15:37:03Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"This is part of unification of the commands 'git tag -l, git branch -l\nand git for-each-ref'. This ports over branch.c to use ref-filter's\nprinting options.\n\nInitially posted here: $(gmane/279226). It was decided that this series\nwould follow up after refactoring ref-filter parsing mechanism, which\nis now merged into master (9606218b32344c5c756f7c29349d3845ef60b80c).\n\nv1 can be found here: $(gmane/288342)\nv2 can be found here: $(gmane/288863)\nv3 can be found here: $(gmane/290299)\nv4 can be found here: $(gmane/291106)\nv5b can be found here: $(gmane/292467)\nv6 can be found here: http://marc.info/?l=git&m=146330914118766&w=2\nv7 can be found here: http://marc.info/?l=git&m=147863593317362&w=2\n\nChanges in this version:\n\n1. use an enum for holding the comparision type in\n%(if:[equals/notequals=...]) options.\n2. rename the 'strip' option to 'lstrip' and introduce an 'rstrip'\noption. Also modify them to take negative values. This drops the\n':dri' and ':base' options.\n3. Drop unecessary code.\n4. Cleanup code and fix spacing.\n5. Add more comments wherever required.\n6. Add quote_literal_for_format(const char *s) for safer string\ninsertions in branch.c:build_format().\n\nThanks to Jacob, Jackub, Junio and Matthieu for their inputs on the\nprevious version.\n\nInterdiff below.\n\nKarthik Nayak (19):\n  ref-filter: implement %(if), %(then), and %(else) atoms\n  ref-filter: include reference to 'used_atom' within 'atom_value'\n  ref-filter: implement %(if:equals=<string>) and\n    %(if:notequals=<string>)\n  ref-filter: modify \"%(objectname:short)\" to take length\n  ref-filter: move get_head_description() from branch.c\n  ref-filter: introduce format_ref_array_item()\n  ref-filter: make %(upstream:track) prints \"[gone]\" for invalid\n    upstreams\n  ref-filter: add support for %(upstream:track,nobracket)\n  ref-filter: make \"%(symref)\" atom work with the ':short' modifier\n  ref-filter: introduce refname_atom_parser_internal()\n  ref-filter: introduce refname_atom_parser()\n  ref-filter: make remote_ref_atom_parser() use\n    refname_atom_parser_internal()\n  ref-filter: rename the 'strip' option to 'lstrip'\n  ref-filter: modify the 'lstrip=<N>' option to work with negative '<N>'\n  ref-filter: add an 'rstrip=<N>' option to atoms which deal with\n    refnames\n  ref-filter: allow porcelain to translate messages in the output\n  branch, tag: use porcelain output\n  branch: use ref-filter printing APIs\n  branch: implement '--format' option\n\n Documentation/git-branch.txt       |   7 +-\n Documentation/git-for-each-ref.txt |  86 +++++--\n builtin/branch.c                   | 290 +++++++---------------\n builtin/tag.c                      |   6 +-\n ref-filter.c                       | 488 +++++++++++++++++++++++++++++++------\n ref-filter.h                       |   7 +\n t/t3203-branch-output.sh           |  16 +-\n t/t6040-tracking-info.sh           |   2 +-\n t/t6300-for-each-ref.sh            |  88 ++++++-\n t/t6302-for-each-ref-filter.sh     |  94 +++++++\n 10 files changed, 784 insertions(+), 300 deletions(-)\n\nInterdiff:\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex f4ad297..c72baeb 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -92,13 +92,14 @@ refname::\n \tThe name of the ref (the part after $GIT_DIR/).\n \tFor a non-ambiguous short name of the ref append `:short`.\n \tThe option core.warnAmbiguousRefs is used to select the strict\n-\tabbreviation mode. If `strip=<N>` is appended, strips `<N>`\n-\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. 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+\tabbreviation mode. If `lstrip=<N>` or `rstrip=<N>` option can\n+\tbe appended to strip `<N>` slash-separated path components\n+\tfrom or end of the refname respectively (e.g.,\n+\t`%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and\n+\t`%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).  if\n+\t`<N>` is a negative number, then only `<N>` path components\n+\tare left behind.  If a displayed ref has fewer components than\n+\t`<N>`, the command aborts with an error.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\n@@ -113,11 +114,10 @@ objectname::\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''\n-\tfrom the displayed ref. Respects `:short`, `:strip`, `:base`\n-\tand `:dir` in the same way as `refname` above.  Additionally\n+\tfrom the displayed ref. Respects `:short`, `:lstrip` and\n+\t`:rstrip` in the same way as `refname` above.  Additionally\n \trespects `:track` to show \"[ahead N, behind M]\" and\n \t`:trackshort` to show the terse version: \">\" (ahead), \"<\"\n \t(behind), \"<>\" (ahead and behind), or \"=\" (in sync). `:track`\n@@ -125,14 +125,16 @@ upstream::\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+\tassociated with it.  All the options apart from `nobracket`\n+\tare mutually exclusive, but if used together the last option\n+\tis selected.\n \n push::\n \tThe name of a local ref which represents the `@{push}`\n-\tlocation for the displayed ref. Respects `:short`, `:strip`,\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+\tlocation for the displayed ref. Respects `:short`, `:lstrip`,\n+\t`:rstrip`, `: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), ' '\n@@ -158,7 +160,7 @@ align::\n \tquoting.\n \n if::\n-\tUsed as %(if)...%(then)...(%end) or\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@@ -173,8 +175,8 @@ if::\n symref::\n \tThe ref which the given symbolic ref refers to. If not a\n \tsymbolic ref, nothing is printed. Respects the `:short`,\n-\t`:strip`, `:base` and `:dir` options in the same way as\n-\t`refname` above.\n+\t`:lstrip` and `:rstrip` options in the same way as `refname`\n+\tabove.\n \n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n@@ -214,12 +216,6 @@ values the `--date` option to linkgit:git-rev-list[1] takes).\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\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex acadb99..6393c3c 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -306,16 +306,36 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n \treturn max;\n }\n \n+const char *quote_literal_for_format(const char *s)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tstrbuf_reset(&buf);\n+\twhile (*s) {\n+\t\tconst char *ep = strchrnul(s, '%');\n+\t\tif (s < ep)\n+\t\t\tstrbuf_add(&buf, s, ep - s);\n+\t\tif (*ep == '%') {\n+\t\t\tstrbuf_addstr(&buf, \"%%\");\n+\t\t\ts = ep + 1;\n+\t\t} else {\n+\t\t\ts = ep;\n+\t\t}\n+\t}\n+\treturn buf.buf;\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+\tstrbuf_addf(&fmt, \"%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)\",\n+\t\t    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, \"%%(align:%d,left)%%(refname:lstrip=2)%%(end)\", maxwidth);\n \t\tstrbuf_addf(&local, \"%s\", branch_get_color(BRANCH_COLOR_RESET));\n \t\tstrbuf_addf(&local, \" %%(objectname:short=7) \");\n \n@@ -326,18 +346,19 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r\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\tstrbuf_addf(&remote, \"%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)\"\n \t\t\t    \"%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)\",\n-\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), maxwidth,\n-\t\t\t    remote_prefix, branch_get_color(BRANCH_COLOR_RESET));\n+\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),\n+\t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n \t} else {\n-\t\tstrbuf_addf(&local, \"%%(refname:strip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n+\t\tstrbuf_addf(&local, \"%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n+\t\tstrbuf_addf(&remote, \"%s%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), quote_literal_for_format(remote_prefix),\n \t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n-\t\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+\tstrbuf_addf(&fmt, \"%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)\", local.buf, remote.buf);\n \n \tstrbuf_release(&local);\n \tstrbuf_release(&remote);\n@@ -682,7 +703,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t * create_branch takes care of setting up the tracking\n \t\t * info and making sure new_upstream is correct\n \t\t */\n-\t\tcreate_branch(head, branch->name, new_upstream, 0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE);\n+\t\tcreate_branch(branch->name, new_upstream, 0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE);\n \t} else if (unset_upstream) {\n \t\tstruct branch *branch = branch_get(argv[0]);\n \t\tstruct strbuf buf = STRBUF_INIT;\n@@ -728,7 +749,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_release(&buf);\n \n \t\tbranch_existed = ref_exists(branch->refname);\n-\t\tcreate_branch(head, argv[0], (argc == 2) ? argv[1] : head,\n+\t\tcreate_branch(argv[0], (argc == 2) ? argv[1] : head,\n \t\t\t      force, reflog, 0, quiet, track);\n \n \t\t/*\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 944671a..a68ed7b 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -21,6 +21,7 @@ static struct ref_msg {\n \tconst char *behind;\n \tconst char *ahead_behind;\n } msgs = {\n+\t /* Untranslated plumbing messages: */\n \t\"gone\",\n \t\"ahead %d\",\n \t\"behind %d\",\n@@ -36,6 +37,7 @@ void setup_ref_filter_porcelain_msg(void)\n }\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n+typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;\n \n struct align {\n \talign_type position;\n@@ -43,16 +45,16 @@ struct align {\n };\n \n struct if_then_else {\n-\tconst char *if_equals,\n-\t\t*not_equals;\n+\tcmp_status cmp_status;\n+\tconst char *str;\n \tunsigned int then_atom_seen : 1,\n \t\telse_atom_seen : 1,\n \t\tcondition_satisfied : 1;\n };\n \n struct refname_atom {\n-\tenum { R_BASE, R_DIR, R_NORMAL, R_SHORT, R_STRIP } option;\n-\tunsigned int strip;\n+\tenum { R_NORMAL, R_SHORT, R_LSTRIP, R_RSTRIP } option;\n+\tint lstrip, rstrip;\n };\n \n /*\n@@ -81,8 +83,8 @@ static struct used_atom {\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\tcmp_status cmp_status;\n+\t\t\tconst char *str;\n \t\t} if_then_else;\n \t\tstruct {\n \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n@@ -109,15 +111,15 @@ static void refname_atom_parser_internal(struct refname_atom *atom,\n \t\tatom->option = R_NORMAL;\n \telse if (!strcmp(arg, \"short\"))\n \t\tatom->option = R_SHORT;\n-\telse if (skip_prefix(arg, \"strip=\", &arg)) {\n-\t\tatom->option = R_STRIP;\n-\t\tif (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0)\n-\t\t\tdie(_(\"positive value expected refname:strip=%s\"), arg);\n-\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+\telse if (skip_prefix(arg, \"lstrip=\", &arg)) {\n+\t\tatom->option = R_LSTRIP;\n+\t\tif (strtol_i(arg, 10, &atom->lstrip))\n+\t\t\tdie(_(\"Integer value expected refname:lstrip=%s\"), arg);\n+\t} else if (skip_prefix(arg, \"rstrip=\", &arg)) {\n+\t\tatom->option = R_RSTRIP;\n+\t\tif (strtol_i(arg, 10, &atom->rstrip))\n+\t\t\tdie(_(\"Integer value expected refname:rstrip=%s\"), arg);\n+\t} else\n \t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n }\n \n@@ -204,11 +206,6 @@ 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@@ -266,16 +263,19 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)\n \n static void if_atom_parser(struct used_atom *atom, const char *arg)\n {\n-\tif (!arg)\n+\tif (!arg) {\n+\t\tatom->u.if_then_else.cmp_status = COMPARE_NONE;\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} else if (skip_prefix(arg, \"equals=\", &atom->u.if_then_else.str)) {\n+\t\tatom->u.if_then_else.cmp_status = COMPARE_EQUAL;\n+\t} else if (skip_prefix(arg, \"notequals=\", &atom->u.if_then_else.str)) {\n+\t\tatom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;\n+\t} else {\n \t\tdie(_(\"unrecognized %%(if) argument: %s\"), arg);\n+\t}\n }\n \n+\n static struct {\n \tconst char *name;\n \tcmp_type cmp_type;\n@@ -310,7 +310,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\", FIELD_STR, symref_atom_parser },\n+\t{ \"symref\", FIELD_STR, refname_atom_parser },\n \t{ \"flag\" },\n \t{ \"HEAD\" },\n \t{ \"color\", FIELD_STR, color_atom_parser },\n@@ -501,12 +501,13 @@ static void if_then_else_handler(struct ref_formatting_stack **stack)\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} else if (!if_then_else->condition_satisfied) {\n \t\t/*\n \t\t * No %(else) atom: just drop the %(then) branch if the\n \t\t * condition is not satisfied.\n \t\t */\n \t\tstrbuf_reset(&cur->output);\n+\t}\n \n \t*stack = cur;\n \tfree(if_then_else);\n@@ -517,8 +518,8 @@ 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+\tif_then_else->str = atomv->atom->u.if_then_else.str;\n+\tif_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;\n \n \tpush_stack_element(&state->stack);\n \tnew = state->stack;\n@@ -555,11 +556,11 @@ static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_st\n \t * perform the required comparison. If not, only non-empty\n \t * strings satisfy the 'if' condition.\n \t */\n-\tif (if_then_else->if_equals) {\n-\t\tif (!strcmp(if_then_else->if_equals, cur->output.buf))\n+\tif (if_then_else->cmp_status == COMPARE_EQUAL) {\n+\t\tif (!strcmp(if_then_else->str, cur->output.buf))\n \t\t\tif_then_else->condition_satisfied = 1;\n-\t} else \tif (if_then_else->not_equals) {\n-\t\tif (strcmp(if_then_else->not_equals, cur->output.buf))\n+\t} else \tif (if_then_else->cmp_status == COMPARE_UNEQUAL) {\n+\t\tif (strcmp(if_then_else->str, cur->output.buf))\n \t\t\tif_then_else->condition_satisfied = 1;\n \t} else if (cur->output.len && !is_empty(cur->output.buf))\n \t\tif_then_else->condition_satisfied = 1;\n@@ -1095,21 +1096,68 @@ static inline char *copy_advance(char *dst, const char *src)\n \treturn dst;\n }\n \n-static const char *strip_ref_components(const char *refname, unsigned int len)\n+static const char *lstrip_ref_components(const char *refname, int len)\n {\n \tlong remaining = len;\n \tconst char *start = refname;\n \n+\tif (len < 0) {\n+\t\tint i;\n+\t\tconst char *p = refname;\n+\n+\t\t/* Find total no of '/' separated path-components */\n+\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++);\n+\t\t/*\n+\t\t * The number of components we need to strip is now\n+\t\t * the total minus the components to be left (Plus one\n+\t\t * because we count the number of '/', but the number\n+\t\t * of components is one more than the no of '/').\n+\t\t */\n+\t\tremaining = i + len + 1;\n+\t}\n+\n \twhile (remaining) {\n \t\tswitch (*start++) {\n \t\tcase '\\0':\n-\t\t\tdie(_(\"ref '%s' does not have %ud components to :strip\"),\n+\t\t\tdie(_(\"ref '%s' does not have %d components to :lstrip\"),\n \t\t\t    refname, len);\n \t\tcase '/':\n \t\t\tremaining--;\n \t\t\tbreak;\n \t\t}\n \t}\n+\n+\treturn start;\n+}\n+\n+static const char *rstrip_ref_components(const char *refname, int len)\n+{\n+\tlong remaining = len;\n+\tchar *start = xstrdup(refname);\n+\n+\tif (len < 0) {\n+\t\tint i;\n+\t\tconst char *p = refname;\n+\n+\t\t/* Find total no of '/' separated path-components */\n+\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++);\n+\t\t/*\n+\t\t * The number of components we need to strip is now\n+\t\t * the total minus the components to be left (Plus one\n+\t\t * because we count the number of '/', but the number\n+\t\t * of components is one more than the no of '/').\n+\t\t */\n+\t\tremaining = i + len + 1;\n+\t}\n+\n+\twhile (remaining--) {\n+\t\tchar *p = strrchr(start, '/');\n+\t\tif (p == NULL)\n+\t\t\tdie(_(\"ref '%s' does not have %d components to :rstrip\"),\n+\t\t\t  refname, len);\n+\t\telse\n+\t\t\tp[0] = '\\0';\n+\t}\n \treturn start;\n }\n \n@@ -1117,27 +1165,11 @@ static const char *show_ref(struct refname_atom *atom, const char *refname)\n {\n \tif (atom->option == R_SHORT)\n \t\treturn shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n-\telse if (atom->option == R_STRIP)\n-\t\treturn strip_ref_components(refname, atom->strip);\n-\telse if (atom->option == R_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+\telse if (atom->option == R_LSTRIP)\n+\t\treturn lstrip_ref_components(refname, atom->lstrip);\n+\telse if (atom->option == R_RSTRIP)\n+\t\treturn rstrip_ref_components(refname, atom->rstrip);\n+\telse\n \t\treturn refname;\n }\n \n@@ -1318,7 +1350,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 8ff6568..8d75cef 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -51,20 +51,26 @@ test_atom() {\n \n test_atom head refname refs/heads/master\n test_atom head refname:short master\n-test_atom head refname:strip=1 heads/master\n-test_atom head refname:strip=2 master\n-test_atom head refname:dir refs/heads\n-test_atom head refname:base heads\n+test_atom head refname:lstrip=1 heads/master\n+test_atom head refname:lstrip=2 master\n+test_atom head refname:lstrip=-1 master\n+test_atom head refname:lstrip=-2 heads/master\n+test_atom head refname:rstrip=1 refs/heads\n+test_atom head refname:rstrip=2 refs\n+test_atom head refname:rstrip=-1 refs\n+test_atom head refname:rstrip=-2 refs/heads\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n-test_atom head upstream:strip=2 origin/master\n-test_atom head upstream:dir refs/remotes/origin\n-test_atom head upstream:base remotes\n+test_atom head upstream:lstrip=2 origin/master\n+test_atom head upstream:lstrip=-2 origin/master\n+test_atom head upstream:rstrip=2 refs/remotes\n+test_atom head upstream:rstrip=-2 refs/remotes\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n-test_atom head push: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 push:lstrip=1 remotes/myfork/master\n+test_atom head push:lstrip=-1 master\n+test_atom head push:rstrip=1 refs/remotes/myfork\n+test_atom head push:rstrip=-1 refs\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n@@ -147,14 +153,14 @@ test_expect_success 'Check invalid atoms names are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n '\n \n-test_expect_success 'arguments to :strip must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=foo)\"\n+test_expect_success 'stripping refnames too far gives an error' '\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=3)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=-4)\"\n '\n \n test_expect_success 'stripping refnames too far gives an error' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=3)\"\n+\ttest_must_fail git for-each-ref --format=\"%(refname:rstrip=3)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(refname:rstrip=-4)\"\n '\n \n test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n@@ -575,6 +581,16 @@ test_expect_success 'Verify sort with multiple keys' '\n \ttest_cmp expected actual\n '\n \n+\n+test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n+\ttest_when_finished \"git checkout master\" &&\n+\tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n+\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_expect_success 'Add symbolic ref for the following tests' '\n \tgit symbolic-ref refs/heads/sym refs/heads/master\n '\n@@ -584,7 +600,7 @@ 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+\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -593,34 +609,29 @@ 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+\tgit for-each-ref --format=\"%(symref:short)\" refs/heads/sym >actual &&\n \ttest_cmp expected actual\n '\n \n cat >expected <<EOF\n master\n+heads/master\n EOF\n \n-test_expect_success 'Verify usage of %(symref:strip) atom' '\n-\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n+\tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n+\tgit for-each-ref --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n \ttest_cmp expected actual\n '\n \n cat >expected <<EOF\n+refs\n refs/heads\n EOF\n \n-test_expect_success 'Verify usage of %(symref: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+test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n+\tgit for-each-ref --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n+\tgit for-each-ref --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n \ttest_cmp expected actual\n '\n\n\n-- \n2.10.2\n\n"},{"id":"307120","messageId":"20161207153627.1468-16-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 15/19] ref-filter: add an 'rstrip=<N>' option to atoms which deal with refnames","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:23Z","receivedAt":"2016-12-07T15:37:21Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Complimenting the existing 'lstrip=<N>' option, add an 'rstrip=<N>'\noption which strips `<N>` slash-separated path components from the end\nof the refname (e.g., `%(refname:rstrip=2)` turns `refs/tags/foo` into\n`refs`).\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 40 ++++++++++++++++++++-----------------\n ref-filter.c                       | 41 ++++++++++++++++++++++++++++++++++++--\n t/t6300-for-each-ref.sh            | 24 ++++++++++++++++++++++\n 3 files changed, 85 insertions(+), 20 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex ad9b243..c72baeb 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -92,10 +92,12 @@ refname::\n \tThe name of the ref (the part after $GIT_DIR/).\n \tFor a non-ambiguous short name of the ref append `:short`.\n \tThe option core.warnAmbiguousRefs is used to select the strict\n-\tabbreviation mode. If `lstrip=<N>` is appended, strips `<N>`\n-\tslash-separated path components from the front of the refname\n-\t(e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n-\tif `<N>` is a negative number, then only `<N>` path components\n+\tabbreviation mode. If `lstrip=<N>` or `rstrip=<N>` option can\n+\tbe appended to strip `<N>` slash-separated path components\n+\tfrom or end of the refname respectively (e.g.,\n+\t`%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and\n+\t`%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).  if\n+\t`<N>` is a negative number, then only `<N>` path components\n \tare left behind.  If a displayed ref has fewer components than\n \t`<N>`, the command aborts with an error.\n \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 `:lstrip` in the\n-\tsame way as `refname` above.  Additionally respects `:track`\n-\tto show \"[ahead N, behind M]\" and `:trackshort` to show the\n-\tterse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n-\tbehind), or \"=\" (in sync). `:track` also prints \"[gone]\"\n-\twhenever unknown upstream ref is encountered. Append\n-\t`:track,nobracket` to show tracking information without\n-\tbrackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n-\tdoes not have tracking information associated with it.  All\n-\tthe options apart from `nobracket` are mutually exclusive, but\n-\tif used together the last option is selected.\n+\tfrom the displayed ref. Respects `:short`, `:lstrip` and\n+\t`:rstrip` in the same way as `refname` above.  Additionally\n+\trespects `:track` to show \"[ahead N, behind M]\" and\n+\t`:trackshort` to show the terse version: \">\" (ahead), \"<\"\n+\t(behind), \"<>\" (ahead and behind), or \"=\" (in sync). `:track`\n+\talso prints \"[gone]\" whenever unknown upstream ref is\n+\tencountered. Append `:track,nobracket` to show tracking\n+\tinformation without brackets (i.e \"ahead N, behind M\").  Has\n+\tno effect if the ref does not have tracking information\n+\tassociated with it.  All the options apart from `nobracket`\n+\tare mutually exclusive, but if used together the last option\n+\tis selected.\n \n push::\n \tThe name of a local ref which represents the `@{push}`\n \tlocation for the displayed ref. Respects `:short`, `:lstrip`,\n-\t`:track`, and `:trackshort` options as `upstream`\n+\t`:rstrip`, `:track`, and `:trackshort` options as `upstream`\n \tdoes. Produces an empty string if no `@{push}` ref is\n \tconfigured.\n \n@@ -171,8 +174,9 @@ if::\n \n symref::\n \tThe ref which the given symbolic ref refers to. If not a\n-\tsymbolic ref, nothing is printed. Respects the `:short` and\n-\t`:lstrip` options in the same way as `refname` above.\n+\tsymbolic ref, nothing is printed. Respects the `:short`,\n+\t`:lstrip` and `:rstrip` options in the same way as `refname`\n+\tabove.\n \n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\ndiff --git a/ref-filter.c b/ref-filter.c\nindex deb2b29..9fce5bb 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -32,8 +32,8 @@ struct if_then_else {\n };\n \n struct refname_atom {\n-\tenum { R_NORMAL, R_SHORT, R_LSTRIP } option;\n-\tint lstrip;\n+\tenum { R_NORMAL, R_SHORT, R_LSTRIP, R_RSTRIP } option;\n+\tint lstrip, rstrip;\n };\n \n /*\n@@ -94,6 +94,10 @@ static void refname_atom_parser_internal(struct refname_atom *atom,\n \t\tatom->option = R_LSTRIP;\n \t\tif (strtol_i(arg, 10, &atom->lstrip))\n \t\t\tdie(_(\"Integer value expected refname:lstrip=%s\"), arg);\n+\t} else if (skip_prefix(arg, \"rstrip=\", &arg)) {\n+\t\tatom->option = R_RSTRIP;\n+\t\tif (strtol_i(arg, 10, &atom->rstrip))\n+\t\t\tdie(_(\"Integer value expected refname:rstrip=%s\"), arg);\n \t} else\n \t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n }\n@@ -1105,12 +1109,45 @@ static const char *lstrip_ref_components(const char *refname, int len)\n \treturn start;\n }\n \n+static const char *rstrip_ref_components(const char *refname, int len)\n+{\n+\tlong remaining = len;\n+\tchar *start = xstrdup(refname);\n+\n+\tif (len < 0) {\n+\t\tint i;\n+\t\tconst char *p = refname;\n+\n+\t\t/* Find total no of '/' separated path-components */\n+\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++);\n+\t\t/*\n+\t\t * The number of components we need to strip is now\n+\t\t * the total minus the components to be left (Plus one\n+\t\t * because we count the number of '/', but the number\n+\t\t * of components is one more than the no of '/').\n+\t\t */\n+\t\tremaining = i + len + 1;\n+\t}\n+\n+\twhile (remaining--) {\n+\t\tchar *p = strrchr(start, '/');\n+\t\tif (p == NULL)\n+\t\t\tdie(_(\"ref '%s' does not have %d components to :rstrip\"),\n+\t\t\t  refname, len);\n+\t\telse\n+\t\t\tp[0] = '\\0';\n+\t}\n+\treturn start;\n+}\n+\n static const char *show_ref(struct refname_atom *atom, const char *refname)\n {\n \tif (atom->option == R_SHORT)\n \t\treturn shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n \telse if (atom->option == R_LSTRIP)\n \t\treturn lstrip_ref_components(refname, atom->lstrip);\n+\telse if (atom->option == R_RSTRIP)\n+\t\treturn rstrip_ref_components(refname, atom->rstrip);\n \telse\n \t\treturn refname;\n }\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 26adca8..8d75cef 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -55,14 +55,22 @@ test_atom head refname:lstrip=1 heads/master\n test_atom head refname:lstrip=2 master\n test_atom head refname:lstrip=-1 master\n test_atom head refname:lstrip=-2 heads/master\n+test_atom head refname:rstrip=1 refs/heads\n+test_atom head refname:rstrip=2 refs\n+test_atom head refname:rstrip=-1 refs\n+test_atom head refname:rstrip=-2 refs/heads\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n test_atom head upstream:lstrip=2 origin/master\n test_atom head upstream:lstrip=-2 origin/master\n+test_atom head upstream:rstrip=2 refs/remotes\n+test_atom head upstream:rstrip=-2 refs/remotes\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n test_atom head push:lstrip=1 remotes/myfork/master\n test_atom head push:lstrip=-1 master\n+test_atom head push:rstrip=1 refs/remotes/myfork\n+test_atom head push:rstrip=-1 refs\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n@@ -150,6 +158,11 @@ test_expect_success 'stripping refnames too far gives an error' '\n \ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=-4)\"\n '\n \n+test_expect_success 'stripping refnames too far gives an error' '\n+\ttest_must_fail git for-each-ref --format=\"%(refname:rstrip=3)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(refname:rstrip=-4)\"\n+'\n+\n test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n \tgit for-each-ref --format=\"%(authordate)\" refs/heads &&\n \tgit for-each-ref --format=\"%(authordate:default) %(authordate)\" refs/heads &&\n@@ -611,4 +624,15 @@ test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n \ttest_cmp expected actual\n '\n \n+cat >expected <<EOF\n+refs\n+refs/heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:rstrip) atom' '\n+\tgit for-each-ref --format=\"%(symref:rstrip=2)\" refs/heads/sym > actual &&\n+\tgit for-each-ref --format=\"%(symref:rstrip=-2)\" refs/heads/sym >> actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.10.2\n\n"},{"id":"307121","messageId":"20161207153627.1468-9-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 08/19] ref-filter: add support for %(upstream:track,nobracket)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:16Z","receivedAt":"2016-12-07T15:37: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\nAdd support for %(upstream:track,nobracket) which will print the\ntracking information without the brackets (i.e. \"ahead N, behind M\").\nThis is needed when we port branch.c to use ref-filter's printing APIs.\n\nAdd test and documentation for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 10 ++++--\n ref-filter.c                       | 67 +++++++++++++++++++++++++-------------\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 53 insertions(+), 26 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 536846f..e1d250e 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -117,9 +117,13 @@ upstream::\n \t`refname` above.  Additionally respects `:track` to show\n \t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n \tversion: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n-\tor \"=\" (in sync).  Has no effect if the ref does not have\n-\ttracking information associated with it. `:track` also prints\n-\t\"[gone]\" whenever unknown upstream ref is encountered.\n+\tor \"=\" (in sync). `:track` also prints \"[gone]\" whenever\n+\tunknown upstream ref is encountered. Append `:track,nobracket`\n+\tto show tracking information without brackets (i.e \"ahead N,\n+\tbehind M\").  Has no effect if the ref does not have tracking\n+\tinformation associated with it.  All the options apart from\n+\t`nobracket` are mutually exclusive, but if used together the\n+\tlast option is selected.\n \n push::\n \tThe name of a local ref which represents the `@{push}` location\ndiff --git a/ref-filter.c b/ref-filter.c\nindex e0229d3..49bb120 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -47,8 +47,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@@ -76,16 +78,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@@ -1049,25 +1068,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 5019f40..ed36a0a 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":"307122","messageId":"20161207153627.1468-10-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 09/19] ref-filter: make \"%(symref)\" atom work with the ':short' modifier","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:17Z","receivedAt":"2016-12-07T15:37:26Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nThe \"%(symref)\" atom doesn't work when used with the ':short' modifier\nbecause we strictly match only 'symref' for setting the 'need_symref'\nindicator. Fix this by comparing with the valid_atom rather than the\nused_atom.\n\nAdd tests for %(symref) and %(symref:short) while we're here.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n ref-filter.c            |  2 +-\n t/t6300-for-each-ref.sh | 24 ++++++++++++++++++++++++\n 2 files changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 49bb120..405a0e5 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -341,7 +341,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 ed36a0a..1935583 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -38,6 +38,7 @@ test_atom() {\n \tcase \"$1\" in\n \t\thead) ref=refs/heads/master ;;\n \t\t tag) ref=refs/tags/testtag ;;\n+\t\t sym) ref=refs/heads/sym ;;\n \t\t   *) ref=$1 ;;\n \tesac\n \tprintf '%s\\n' \"$3\" >expected\n@@ -566,6 +567,7 @@ test_expect_success 'Verify sort with multiple keys' '\n \ttest_cmp expected actual\n '\n \n+\n test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n \ttest_when_finished \"git checkout master\" &&\n \tgit for-each-ref --format=\"%(HEAD) %(refname:short)\" refs/heads/ >actual &&\n@@ -575,4 +577,26 @@ test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '\n \ttest_cmp expect 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":"307123","messageId":"20161207153627.1468-11-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 10/19] ref-filter: introduce refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:18Z","receivedAt":"2016-12-07T15:37: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\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 405a0e5..1a18c7d 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -31,6 +31,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@@ -63,6 +68,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@@ -76,6 +82,21 @@ static void color_atom_parser(struct used_atom *atom, const char *color_value)\n \t\tdie(_(\"unrecognized color: %%(color:%s)\"), color_value);\n }\n \n+static void refname_atom_parser_internal(struct refname_atom *atom,\n+\t\t\t\t\t const char *arg, const char *name)\n+{\n+\tif (!arg)\n+\t\tatom->option = R_NORMAL;\n+\telse if (!strcmp(arg, \"short\"))\n+\t\tatom->option = R_SHORT;\n+\telse if (skip_prefix(arg, \"strip=\", &arg)) {\n+\t\tatom->option = R_STRIP;\n+\t\tif (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0)\n+\t\t\tdie(_(\"positive value expected refname:strip=%s\"), arg);\n+\t} else\n+\t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n+}\n+\n static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n {\n \tstruct string_list params = STRING_LIST_INIT_DUP;\n-- \n2.10.2\n\n"},{"id":"307124","messageId":"20161207153627.1468-17-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 16/19] ref-filter: allow porcelain to translate messages in the output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:24Z","receivedAt":"2016-12-07T15:37:28Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nIntroduce setup_ref_filter_porcelain_msg() so that the messages used in\nthe atom %(upstream:track) can be translated if needed. By default, keep\nthe messages untranslated, which is the right behavior for plumbing\ncommands. This is needed as we port branch.c to use ref-filter's\nprinting API's.\n\nWritten-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n ref-filter.c | 29 +++++++++++++++++++++++++----\n ref-filter.h |  2 ++\n 2 files changed, 27 insertions(+), 4 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 9fce5bb..a68ed7b 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -15,6 +15,27 @@\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 /* Untranslated plumbing messages: */\n+\t\"gone\",\n+\t\"ahead %d\",\n+\t\"behind %d\",\n+\t\"ahead %d, behind %d\"\n+};\n+\n+void setup_ref_filter_porcelain_msg(void)\n+{\n+\tmsgs.gone = _(\"gone\");\n+\tmsgs.ahead = _(\"ahead %d\");\n+\tmsgs.behind = _(\"behind %d\");\n+\tmsgs.ahead_behind = _(\"ahead %d, behind %d\");\n+}\n+\n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;\n \n@@ -1161,15 +1182,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":"307125","messageId":"20161207153627.1468-20-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 19/19] branch: implement '--format' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:27Z","receivedAt":"2016-12-07T15:37:29Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\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 046d245..6393c3c 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -28,6 +28,7 @@ static const char * const builtin_branch_usage[] = {\n \tN_(\"git branch [<options>] [-r] (-d | -D) <branch-name>...\"),\n \tN_(\"git branch [<options>] (-m | -M) [<old-branch>] <new-branch>\"),\n \tN_(\"git branch [<options>] [-r | -a] [--points-at]\"),\n+\tN_(\"git branch [<options>] [-r | -a] [--format]\"),\n \tNULL\n };\n \n@@ -364,14 +365,14 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r\n \treturn strbuf_detach(&fmt, NULL);\n }\n \n-static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)\n+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)\n {\n \tint i;\n \tstruct ref_array array;\n \tint maxwidth = 0;\n \tconst char *remote_prefix = \"\";\n \tstruct strbuf out = STRBUF_INIT;\n-\tchar *format;\n+\tchar *to_free = NULL;\n \n \t/*\n \t * If we are listing more than just remote branches,\n@@ -388,7 +389,8 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin\n \tif (filter->verbose)\n \t\tmaxwidth = calc_maxwidth(&array, strlen(remote_prefix));\n \n-\tformat = build_format(filter, maxwidth, remote_prefix);\n+\tif (!format)\n+\t\tformat = to_free = build_format(filter, maxwidth, remote_prefix);\n \tverify_ref_format(format);\n \n \t/*\n@@ -416,7 +418,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@@ -536,6 +538,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@@ -576,6 +579,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@@ -636,7 +640,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":"307126","messageId":"20161207153627.1468-19-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 18/19] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:26Z","receivedAt":"2016-12-07T15:37:30Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nPort branch.c to use ref-filter APIs for printing. This clears out\nmost of the code used in branch.c for printing and replaces them with\ncalls made to the ref-filter library.\n\nIntroduce build_format() which gets the format required for printing\nof refs. Make amendments to print_ref_list() to reflect these changes.\n\nThe strings included in build_format() may not be safely quoted for\ninclusion (i.e. it might contain '%' which needs to be escaped with an\nadditional '%'). Introduce quote_literal_for_format() as a helper\nfunction which takes a string and returns a version of the string that\nis safely quoted to be used in the for-each-ref format which is built\nin build_format().\n\nChange calc_maxwidth() to also account for the length of HEAD ref, by\ncalling ref-filter:get_head_discription().\n\nAlso change the test in t6040 to reflect the changes.\n\nBefore this patch, all cross-prefix symrefs weren't shortened. Since\nwe're using ref-filter APIs, we shorten all symrefs by default. We also\nallow the user to change the format if needed with the introduction of\nthe '--format' option in the next patch.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/branch.c         | 249 ++++++++++++++++-------------------------------\n t/t3203-branch-output.sh |   2 +-\n t/t6040-tracking-info.sh |   2 +-\n 3 files changed, 88 insertions(+), 165 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 4d06553..046d245 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -36,12 +36,12 @@ static unsigned char head_sha1[20];\n \n static int branch_use_color = -1;\n static char branch_colors[][COLOR_MAXLEN] = {\n-\tGIT_COLOR_RESET,\n-\tGIT_COLOR_NORMAL,\t/* PLAIN */\n-\tGIT_COLOR_RED,\t\t/* REMOTE */\n-\tGIT_COLOR_NORMAL,\t/* LOCAL */\n-\tGIT_COLOR_GREEN,\t/* CURRENT */\n-\tGIT_COLOR_BLUE,\t\t/* UPSTREAM */\n+\t\"%(color:reset)\",\n+\t\"%(color:reset)\",\t/* PLAIN */\n+\t\"%(color:red)\",\t\t/* REMOTE */\n+\t\"%(color:reset)\",\t/* LOCAL */\n+\t\"%(color:green)\",\t/* CURRENT */\n+\t\"%(color:blue)\",\t/* UPSTREAM */\n };\n enum color_branch {\n \tBRANCH_COLOR_RESET = 0,\n@@ -280,180 +280,88 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,\n \treturn(ret);\n }\n \n-static void fill_tracking_info(struct strbuf *stat, const char *branch_name,\n-\t\tint show_upstream_ref)\n+static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n {\n-\tint ours, theirs;\n-\tchar *ref = NULL;\n-\tstruct branch *branch = branch_get(branch_name);\n-\tconst char *upstream;\n-\tstruct strbuf fancy = STRBUF_INIT;\n-\tint upstream_is_gone = 0;\n-\tint added_decoration = 1;\n-\n-\tif (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {\n-\t\tif (!upstream)\n-\t\t\treturn;\n-\t\tupstream_is_gone = 1;\n-\t}\n-\n-\tif (show_upstream_ref) {\n-\t\tref = shorten_unambiguous_ref(upstream, 0);\n-\t\tif (want_color(branch_use_color))\n-\t\t\tstrbuf_addf(&fancy, \"%s%s%s\",\n-\t\t\t\t\tbranch_get_color(BRANCH_COLOR_UPSTREAM),\n-\t\t\t\t\tref, branch_get_color(BRANCH_COLOR_RESET));\n-\t\telse\n-\t\t\tstrbuf_addstr(&fancy, ref);\n-\t}\n+\tint i, max = 0;\n+\tfor (i = 0; i < refs->nr; i++) {\n+\t\tstruct ref_array_item *it = refs->items[i];\n+\t\tconst char *desc = it->refname;\n+\t\tint w;\n \n-\tif (upstream_is_gone) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: gone]\"), fancy.buf);\n-\t\telse\n-\t\t\tadded_decoration = 0;\n-\t} else if (!ours && !theirs) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s]\"), fancy.buf);\n-\t\telse\n-\t\t\tadded_decoration = 0;\n-\t} else if (!ours) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: behind %d]\"), fancy.buf, theirs);\n-\t\telse\n-\t\t\tstrbuf_addf(stat, _(\"[behind %d]\"), theirs);\n+\t\tskip_prefix(it->refname, \"refs/heads/\", &desc);\n+\t\tskip_prefix(it->refname, \"refs/remotes/\", &desc);\n+\t\tif (it->kind == FILTER_REFS_DETACHED_HEAD) {\n+\t\t\tchar *head_desc = get_head_description();\n+\t\t\tw = utf8_strwidth(head_desc);\n+\t\t\tfree(head_desc);\n+\t\t} else\n+\t\t\tw = utf8_strwidth(desc);\n \n-\t} else if (!theirs) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: ahead %d]\"), fancy.buf, ours);\n-\t\telse\n-\t\t\tstrbuf_addf(stat, _(\"[ahead %d]\"), ours);\n-\t} else {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: ahead %d, behind %d]\"),\n-\t\t\t\t    fancy.buf, ours, theirs);\n-\t\telse\n-\t\t\tstrbuf_addf(stat, _(\"[ahead %d, behind %d]\"),\n-\t\t\t\t    ours, theirs);\n+\t\tif (it->kind == FILTER_REFS_REMOTES)\n+\t\t\tw += remote_bonus;\n+\t\tif (w > max)\n+\t\t\tmax = w;\n \t}\n-\tstrbuf_release(&fancy);\n-\tif (added_decoration)\n-\t\tstrbuf_addch(stat, ' ');\n-\tfree(ref);\n+\treturn max;\n }\n \n-static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,\n-\t\t\t     struct ref_filter *filter, const char *refname)\n+const char *quote_literal_for_format(const char *s)\n {\n-\tstruct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;\n-\tconst char *sub = _(\" **** invalid ref ****\");\n-\tstruct commit *commit = item->commit;\n+\tstruct strbuf buf = STRBUF_INIT;\n \n-\tif (!parse_commit(commit)) {\n-\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);\n-\t\tsub = subject.buf;\n+\tstrbuf_reset(&buf);\n+\twhile (*s) {\n+\t\tconst char *ep = strchrnul(s, '%');\n+\t\tif (s < ep)\n+\t\t\tstrbuf_add(&buf, s, ep - s);\n+\t\tif (*ep == '%') {\n+\t\t\tstrbuf_addstr(&buf, \"%%\");\n+\t\t\ts = ep + 1;\n+\t\t} else {\n+\t\t\ts = ep;\n+\t\t}\n \t}\n-\n-\tif (item->kind == FILTER_REFS_BRANCHES)\n-\t\tfill_tracking_info(&stat, refname, filter->verbose > 1);\n-\n-\tstrbuf_addf(out, \" %s %s%s\",\n-\t\tfind_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),\n-\t\tstat.buf, sub);\n-\tstrbuf_release(&stat);\n-\tstrbuf_release(&subject);\n+\treturn buf.buf;\n }\n \n-static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,\n-\t\t\t\t      struct ref_filter *filter, const char *remote_prefix)\n+static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)\n {\n-\tchar c;\n-\tint current = 0;\n-\tint color;\n-\tstruct strbuf out = STRBUF_INIT, name = STRBUF_INIT;\n-\tconst char *prefix_to_show = \"\";\n-\tconst char *prefix_to_skip = NULL;\n-\tconst char *desc = item->refname;\n-\tchar *to_free = NULL;\n-\n-\tswitch (item->kind) {\n-\tcase FILTER_REFS_BRANCHES:\n-\t\tprefix_to_skip = \"refs/heads/\";\n-\t\tskip_prefix(desc, prefix_to_skip, &desc);\n-\t\tif (!filter->detached && !strcmp(desc, head))\n-\t\t\tcurrent = 1;\n-\t\telse\n-\t\t\tcolor = BRANCH_COLOR_LOCAL;\n-\t\tbreak;\n-\tcase FILTER_REFS_REMOTES:\n-\t\tprefix_to_skip = \"refs/remotes/\";\n-\t\tskip_prefix(desc, prefix_to_skip, &desc);\n-\t\tcolor = BRANCH_COLOR_REMOTE;\n-\t\tprefix_to_show = remote_prefix;\n-\t\tbreak;\n-\tcase FILTER_REFS_DETACHED_HEAD:\n-\t\tdesc = to_free = get_head_description();\n-\t\tcurrent = 1;\n-\t\tbreak;\n-\tdefault:\n-\t\tcolor = BRANCH_COLOR_PLAIN;\n-\t\tbreak;\n-\t}\n+\tstruct strbuf fmt = STRBUF_INIT;\n+\tstruct strbuf local = STRBUF_INIT;\n+\tstruct strbuf remote = STRBUF_INIT;\n \n-\tc = ' ';\n-\tif (current) {\n-\t\tc = '*';\n-\t\tcolor = BRANCH_COLOR_CURRENT;\n-\t}\n+\tstrbuf_addf(&fmt, \"%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)\",\n+\t\t    branch_get_color(BRANCH_COLOR_CURRENT));\n \n-\tstrbuf_addf(&name, \"%s%s\", prefix_to_show, desc);\n \tif (filter->verbose) {\n-\t\tint utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);\n-\t\tstrbuf_addf(&out, \"%c %s%-*s%s\", c, branch_get_color(color),\n-\t\t\t    maxwidth + utf8_compensation, name.buf,\n+\t\tstrbuf_addf(&local, \"%%(align:%d,left)%%(refname:lstrip=2)%%(end)\", maxwidth);\n+\t\tstrbuf_addf(&local, \"%s\", branch_get_color(BRANCH_COLOR_RESET));\n+\t\tstrbuf_addf(&local, \" %%(objectname:short=7) \");\n+\n+\t\tif (filter->verbose > 1)\n+\t\t\tstrbuf_addf(&local, \"%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)\"\n+\t\t\t\t    \"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)\",\n+\t\t\t\t    branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));\n+\t\telse\n+\t\t\tstrbuf_addf(&local, \"%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)\");\n+\n+\t\tstrbuf_addf(&remote, \"%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)\"\n+\t\t\t    \"%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),\n \t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n-\t} else\n-\t\tstrbuf_addf(&out, \"%c %s%s%s\", c, branch_get_color(color),\n-\t\t\t    name.buf, branch_get_color(BRANCH_COLOR_RESET));\n-\n-\tif (item->symref) {\n-\t\tconst char *symref = item->symref;\n-\t\tif (prefix_to_skip)\n-\t\t\tskip_prefix(symref, prefix_to_skip, &symref);\n-\t\tstrbuf_addf(&out, \" -> %s\", symref);\n-\t}\n-\telse if (filter->verbose)\n-\t\t/* \" f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h\" */\n-\t\tadd_verbose_info(&out, item, filter, desc);\n-\tif (column_active(colopts)) {\n-\t\tassert(!filter->verbose && \"--column and --verbose are incompatible\");\n-\t\tstring_list_append(&output, out.buf);\n \t} else {\n-\t\tprintf(\"%s\\n\", out.buf);\n+\t\tstrbuf_addf(&local, \"%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n+\t\tstrbuf_addf(&remote, \"%s%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), quote_literal_for_format(remote_prefix),\n+\t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n \t}\n-\tstrbuf_release(&name);\n-\tstrbuf_release(&out);\n-\tfree(to_free);\n-}\n-\n-static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n-{\n-\tint i, max = 0;\n-\tfor (i = 0; i < refs->nr; i++) {\n-\t\tstruct ref_array_item *it = refs->items[i];\n-\t\tconst char *desc = it->refname;\n-\t\tint w;\n \n-\t\tskip_prefix(it->refname, \"refs/heads/\", &desc);\n-\t\tskip_prefix(it->refname, \"refs/remotes/\", &desc);\n-\t\tw = utf8_strwidth(desc);\n+\tstrbuf_addf(&fmt, \"%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)\", local.buf, remote.buf);\n \n-\t\tif (it->kind == FILTER_REFS_REMOTES)\n-\t\t\tw += remote_bonus;\n-\t\tif (w > max)\n-\t\t\tmax = w;\n-\t}\n-\treturn max;\n+\tstrbuf_release(&local);\n+\tstrbuf_release(&remote);\n+\treturn strbuf_detach(&fmt, NULL);\n }\n \n static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)\n@@ -462,6 +370,8 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin\n \tstruct ref_array array;\n \tint maxwidth = 0;\n \tconst char *remote_prefix = \"\";\n+\tstruct strbuf out = STRBUF_INIT;\n+\tchar *format;\n \n \t/*\n \t * If we are listing more than just remote branches,\n@@ -473,12 +383,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 +402,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":"307127","messageId":"20161207153627.1468-15-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 14/19] ref-filter: modify the 'lstrip=<N>' option to work with negative '<N>'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:22Z","receivedAt":"2016-12-07T15:37:38Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Currently the 'lstrip=<N>' option only takes a positive value '<N>'\nand strips '<N>' slash-separated path components from the left. Modify\nthe 'lstrip' option to also take a negative number '<N>' which would\nonly _leave_ behind 'N' slash-separated path components from the left.\n\nAdd documentation and tests for the same.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  5 +++--\n ref-filter.c                       | 26 +++++++++++++++++++++-----\n t/t6300-for-each-ref.sh            | 15 ++++++++-------\n 3 files changed, 32 insertions(+), 14 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex f85ccff..ad9b243 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -95,8 +95,9 @@ refname::\n \tabbreviation mode. If `lstrip=<N>` is appended, strips `<N>`\n \tslash-separated path components from the front of the refname\n \t(e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n-\t`<N>` must be a positive integer.  If a displayed ref has fewer\n-\tcomponents than `<N>`, the command aborts with an error.\n+\tif `<N>` is a negative number, then only `<N>` path components\n+\tare left behind.  If a displayed ref has fewer components than\n+\t`<N>`, the command aborts with an error.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\ndiff --git a/ref-filter.c b/ref-filter.c\nindex c74b08d..deb2b29 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -33,7 +33,7 @@ struct if_then_else {\n \n struct refname_atom {\n \tenum { R_NORMAL, R_SHORT, R_LSTRIP } option;\n-\tunsigned int lstrip;\n+\tint lstrip;\n };\n \n /*\n@@ -92,8 +92,8 @@ static void refname_atom_parser_internal(struct refname_atom *atom,\n \t\tatom->option = R_SHORT;\n \telse if (skip_prefix(arg, \"lstrip=\", &arg)) {\n \t\tatom->option = R_LSTRIP;\n-\t\tif (strtoul_ui(arg, 10, &atom->lstrip) || atom->lstrip <= 0)\n-\t\t\tdie(_(\"positive value expected refname:lstrip=%s\"), arg);\n+\t\tif (strtol_i(arg, 10, &atom->lstrip))\n+\t\t\tdie(_(\"Integer value expected refname:lstrip=%s\"), arg);\n \t} else\n \t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n }\n@@ -1071,21 +1071,37 @@ static inline char *copy_advance(char *dst, const char *src)\n \treturn dst;\n }\n \n-static const char *lstrip_ref_components(const char *refname, unsigned int len)\n+static const char *lstrip_ref_components(const char *refname, int len)\n {\n \tlong remaining = len;\n \tconst char *start = refname;\n \n+\tif (len < 0) {\n+\t\tint i;\n+\t\tconst char *p = refname;\n+\n+\t\t/* Find total no of '/' separated path-components */\n+\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++);\n+\t\t/*\n+\t\t * The number of components we need to strip is now\n+\t\t * the total minus the components to be left (Plus one\n+\t\t * because we count the number of '/', but the number\n+\t\t * of components is one more than the no of '/').\n+\t\t */\n+\t\tremaining = i + len + 1;\n+\t}\n+\n \twhile (remaining) {\n \t\tswitch (*start++) {\n \t\tcase '\\0':\n-\t\t\tdie(_(\"ref '%s' does not have %ud components to :lstrip\"),\n+\t\t\tdie(_(\"ref '%s' does not have %d components to :lstrip\"),\n \t\t\t    refname, len);\n \t\tcase '/':\n \t\t\tremaining--;\n \t\t\tbreak;\n \t\t}\n \t}\n+\n \treturn start;\n }\n \ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 2b1af9c..26adca8 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -53,12 +53,16 @@ test_atom head refname refs/heads/master\n test_atom head refname:short master\n test_atom head refname:lstrip=1 heads/master\n test_atom head refname:lstrip=2 master\n+test_atom head refname:lstrip=-1 master\n+test_atom head refname:lstrip=-2 heads/master\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n test_atom head upstream:lstrip=2 origin/master\n+test_atom head upstream:lstrip=-2 origin/master\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n test_atom head push:lstrip=1 remotes/myfork/master\n+test_atom head push:lstrip=-1 master\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n@@ -141,14 +145,9 @@ test_expect_success 'Check invalid atoms names are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n '\n \n-test_expect_success 'arguments to :lstrip must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=foo)\"\n-'\n-\n test_expect_success 'stripping refnames too far gives an error' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=3)\"\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=3)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=-4)\"\n '\n \n test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n@@ -603,10 +602,12 @@ test_expect_success 'Verify usage of %(symref:short) atom' '\n \n cat >expected <<EOF\n master\n+heads/master\n EOF\n \n test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n \tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n+\tgit for-each-ref --format=\"%(symref:lstrip=-2)\" refs/heads/sym >> actual &&\n \ttest_cmp expected actual\n '\n \n-- \n2.10.2\n\n"},{"id":"307128","messageId":"20161207153627.1468-14-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 13/19] ref-filter: rename the 'strip' option to 'lstrip'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:21Z","receivedAt":"2016-12-07T15:37:41Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"In preparation for the upcoming patch, where we introduce the 'rstrip'\noption. Rename the 'strip' option to 'lstrip' to remove ambiguity.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 10 +++++-----\n builtin/tag.c                      |  4 ++--\n ref-filter.c                       | 20 ++++++++++----------\n t/t6300-for-each-ref.sh            | 22 +++++++++++-----------\n 4 files changed, 28 insertions(+), 28 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 6a1e747..f85ccff 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -92,9 +92,9 @@ refname::\n \tThe name of the ref (the part after $GIT_DIR/).\n \tFor a non-ambiguous short name of the ref append `:short`.\n \tThe option core.warnAmbiguousRefs is used to select the strict\n-\tabbreviation mode. If `strip=<N>` is appended, strips `<N>`\n+\tabbreviation mode. If `lstrip=<N>` is appended, strips `<N>`\n \tslash-separated path components from the front of the refname\n-\t(e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.\n+\t(e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.\n \t`<N>` must be a positive integer.  If a displayed ref has fewer\n \tcomponents than `<N>`, the command aborts with an error.\n \n@@ -113,7 +113,7 @@ objectname::\n \n upstream::\n \tThe name of a local ref which can be considered ``upstream''\n-\tfrom the displayed ref. Respects `:short` and `:strip` in the\n+\tfrom the displayed ref. Respects `:short` and `:lstrip` in the\n \tsame way as `refname` above.  Additionally respects `:track`\n \tto show \"[ahead N, behind M]\" and `:trackshort` to show the\n \tterse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n@@ -127,7 +127,7 @@ upstream::\n \n push::\n \tThe name of a local ref which represents the `@{push}`\n-\tlocation for the displayed ref. Respects `:short`, `:strip`,\n+\tlocation for the displayed ref. Respects `:short`, `:lstrip`,\n \t`:track`, and `:trackshort` options as `upstream`\n \tdoes. Produces an empty string if no `@{push}` ref is\n \tconfigured.\n@@ -171,7 +171,7 @@ if::\n symref::\n \tThe ref which the given symbolic ref refers to. If not a\n \tsymbolic ref, nothing is printed. Respects the `:short` and\n-\t`:strip` options in the same way as `refname` above.\n+\t`:lstrip` options in the same way as `refname` above.\n \n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 50e4ae5..24bbe24 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -45,11 +45,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con\n \tif (!format) {\n \t\tif (filter->lines) {\n \t\t\tto_free = xstrfmt(\"%s %%(contents:lines=%d)\",\n-\t\t\t\t\t  \"%(align:15)%(refname:strip=2)%(end)\",\n+\t\t\t\t\t  \"%(align:15)%(refname:lstrip=2)%(end)\",\n \t\t\t\t\t  filter->lines);\n \t\t\tformat = to_free;\n \t\t} else\n-\t\t\tformat = \"%(refname:strip=2)\";\n+\t\t\tformat = \"%(refname:lstrip=2)\";\n \t}\n \n \tverify_ref_format(format);\ndiff --git a/ref-filter.c b/ref-filter.c\nindex be535a6..c74b08d 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -32,8 +32,8 @@ struct if_then_else {\n };\n \n struct refname_atom {\n-\tenum { R_NORMAL, R_SHORT, R_STRIP } option;\n-\tunsigned int strip;\n+\tenum { R_NORMAL, R_SHORT, R_LSTRIP } option;\n+\tunsigned int lstrip;\n };\n \n /*\n@@ -90,10 +90,10 @@ static void refname_atom_parser_internal(struct refname_atom *atom,\n \t\tatom->option = R_NORMAL;\n \telse if (!strcmp(arg, \"short\"))\n \t\tatom->option = R_SHORT;\n-\telse if (skip_prefix(arg, \"strip=\", &arg)) {\n-\t\tatom->option = R_STRIP;\n-\t\tif (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0)\n-\t\t\tdie(_(\"positive value expected refname:strip=%s\"), arg);\n+\telse if (skip_prefix(arg, \"lstrip=\", &arg)) {\n+\t\tatom->option = R_LSTRIP;\n+\t\tif (strtoul_ui(arg, 10, &atom->lstrip) || atom->lstrip <= 0)\n+\t\t\tdie(_(\"positive value expected refname:lstrip=%s\"), arg);\n \t} else\n \t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n }\n@@ -1071,7 +1071,7 @@ static inline char *copy_advance(char *dst, const char *src)\n \treturn dst;\n }\n \n-static const char *strip_ref_components(const char *refname, unsigned int len)\n+static const char *lstrip_ref_components(const char *refname, unsigned int len)\n {\n \tlong remaining = len;\n \tconst char *start = refname;\n@@ -1079,7 +1079,7 @@ static const char *strip_ref_components(const char *refname, unsigned int len)\n \twhile (remaining) {\n \t\tswitch (*start++) {\n \t\tcase '\\0':\n-\t\t\tdie(_(\"ref '%s' does not have %ud components to :strip\"),\n+\t\t\tdie(_(\"ref '%s' does not have %ud components to :lstrip\"),\n \t\t\t    refname, len);\n \t\tcase '/':\n \t\t\tremaining--;\n@@ -1093,8 +1093,8 @@ static const char *show_ref(struct refname_atom *atom, const char *refname)\n {\n \tif (atom->option == R_SHORT)\n \t\treturn shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n-\telse if (atom->option == R_STRIP)\n-\t\treturn strip_ref_components(refname, atom->strip);\n+\telse if (atom->option == R_LSTRIP)\n+\t\treturn lstrip_ref_components(refname, atom->lstrip);\n \telse\n \t\treturn refname;\n }\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 2facfaf..2b1af9c 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -51,14 +51,14 @@ test_atom() {\n \n test_atom head refname refs/heads/master\n test_atom head refname:short master\n-test_atom head refname:strip=1 heads/master\n-test_atom head refname:strip=2 master\n+test_atom head refname:lstrip=1 heads/master\n+test_atom head refname:lstrip=2 master\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n-test_atom head upstream:strip=2 origin/master\n+test_atom head upstream:lstrip=2 origin/master\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n-test_atom head push:strip=1 remotes/myfork/master\n+test_atom head push:lstrip=1 remotes/myfork/master\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n@@ -141,14 +141,14 @@ test_expect_success 'Check invalid atoms names are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n '\n \n-test_expect_success 'arguments to :strip must be positive integers' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=0)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=-1)\" &&\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=foo)\"\n+test_expect_success 'arguments to :lstrip must be positive integers' '\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=0)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=-1)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=foo)\"\n '\n \n test_expect_success 'stripping refnames too far gives an error' '\n-\ttest_must_fail git for-each-ref --format=\"%(refname:strip=3)\"\n+\ttest_must_fail git for-each-ref --format=\"%(refname:lstrip=3)\"\n '\n \n test_expect_success 'Check format specifiers are ignored in naming date atoms' '\n@@ -605,8 +605,8 @@ cat >expected <<EOF\n master\n EOF\n \n-test_expect_success 'Verify usage of %(symref:strip) atom' '\n-\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+test_expect_success 'Verify usage of %(symref:lstrip) atom' '\n+\tgit for-each-ref --format=\"%(symref:lstrip=2)\" refs/heads/sym > actual &&\n \ttest_cmp expected actual\n '\n \n-- \n2.10.2\n\n"},{"id":"307129","messageId":"20161207153627.1468-18-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 17/19] branch, tag: use porcelain output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:25Z","receivedAt":"2016-12-07T15:37: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\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 0b80c13..4d06553 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 24bbe24..774e955 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":"307130","messageId":"20161207153627.1468-12-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 11/19] ref-filter: introduce refname_atom_parser()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:19Z","receivedAt":"2016-12-07T15:37: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\nUsing refname_atom_parser_internal(), introduce refname_atom_parser()\nwhich will parse the %(symref) and %(refname) atoms. Store the parsed\ninformation into the 'used_atom' structure based on the modifiers used\nalong with the atoms.\n\nNow the '%(symref)' atom supports the ':strip' atom modifier. Update the\nDocumentation and tests to reflect this.\n\nHelped-by: Jeff King <peff@peff.net>\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  5 +++\n ref-filter.c                       | 73 +++++++++++++++++++++-----------------\n t/t6300-for-each-ref.sh            |  9 +++++\n 3 files changed, 54 insertions(+), 33 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex e1d250e..8224f37 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -167,6 +167,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 1a18c7d..9f5522d 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -177,6 +177,11 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg)\n \t\tdie(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n }\n \n+static void refname_atom_parser(struct used_atom *atom, const char *arg)\n+{\n+\treturn refname_atom_parser_internal(&atom->u.refname, arg, atom->name);\n+}\n+\n static align_type parse_align_position(const char *s)\n {\n \tif (!strcmp(s, \"right\"))\n@@ -247,7 +252,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@@ -276,7 +281,7 @@ static struct {\n \t{ \"contents\", FIELD_STR, contents_atom_parser },\n \t{ \"upstream\", FIELD_STR, remote_ref_atom_parser },\n \t{ \"push\", FIELD_STR, remote_ref_atom_parser },\n-\t{ \"symref\" },\n+\t{ \"symref\", FIELD_STR, refname_atom_parser },\n \t{ \"flag\" },\n \t{ \"HEAD\" },\n \t{ \"color\", FIELD_STR, color_atom_parser },\n@@ -1062,21 +1067,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@@ -1085,6 +1085,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@@ -1157,6 +1167,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@@ -1185,7 +1210,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@@ -1196,12 +1220,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@@ -1277,21 +1299,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 1935583..b32ab79 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -599,4 +599,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":"307131","messageId":"20161207153627.1468-13-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 12/19] ref-filter: make remote_ref_atom_parser() use refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:20Z","receivedAt":"2016-12-07T15:37:46Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nUse the recently introduced refname_atom_parser_internal() within\nremote_ref_atom_parser(), this provides a common base for all the ref\nprinting atoms, allowing %(upstream) and %(push) to also use the\n':strip' option.\n\nThe atoms '%(push)' and '%(upstream)' will retain the ':track' and\n':trackshort' atom modifiers to themselves as they have no meaning in\ncontext to the '%(refname)' and '%(symref)' atoms.\n\nUpdate the documentation and tests to reflect the same.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 31 ++++++++++++++++---------------\n ref-filter.c                       | 26 +++++++++++++++-----------\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 33 insertions(+), 26 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 8224f37..6a1e747 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -113,23 +113,24 @@ objectname::\n \n upstream::\n \tThe name of a local ref which can be considered ``upstream''\n-\tfrom the displayed ref. Respects `:short` in the same way as\n-\t`refname` above.  Additionally respects `:track` to show\n-\t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n-\tversion: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n-\tor \"=\" (in sync). `:track` also prints \"[gone]\" whenever\n-\tunknown upstream ref is encountered. Append `:track,nobracket`\n-\tto show tracking information without brackets (i.e \"ahead N,\n-\tbehind M\").  Has no effect if the ref does not have tracking\n-\tinformation associated with it.  All the options apart from\n-\t`nobracket` are mutually exclusive, but if used together the\n-\tlast option is selected.\n+\tfrom the displayed ref. Respects `:short` and `:strip` in the\n+\tsame way as `refname` above.  Additionally respects `:track`\n+\tto show \"[ahead N, behind M]\" and `:trackshort` to show the\n+\tterse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n+\tbehind), or \"=\" (in sync). `:track` also prints \"[gone]\"\n+\twhenever unknown upstream ref is encountered. Append\n+\t`:track,nobracket` to show tracking information without\n+\tbrackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n+\tdoes not have tracking information associated with it.  All\n+\tthe options apart from `nobracket` are mutually exclusive, but\n+\tif used together the last option is selected.\n \n push::\n-\tThe name of a local ref which represents the `@{push}` location\n-\tfor the displayed ref. Respects `:short`, `:track`, and\n-\t`:trackshort` options as `upstream` does. Produces an empty\n-\tstring if no `@{push}` ref is configured.\n+\tThe name of a local ref which represents the `@{push}`\n+\tlocation for the displayed ref. Respects `:short`, `:strip`,\n+\t`:track`, and `:trackshort` options as `upstream`\n+\tdoes. Produces an empty string if no `@{push}` ref is\n+\tconfigured.\n \n HEAD::\n \t'*' if HEAD matches current ref (the checked out branch), ' '\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 9f5522d..be535a6 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -53,7 +53,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@@ -103,7 +104,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@@ -113,16 +116,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@@ -1099,8 +1103,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@@ -1132,8 +1136,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 b32ab79..2facfaf 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":"307132","messageId":"20161207153627.1468-8-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 07/19] ref-filter: make %(upstream:track) prints \"[gone]\" for invalid upstreams","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:15Z","receivedAt":"2016-12-07T15:37:57Z","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 b730735..536846f 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -118,7 +118,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 0f81f9f..e0229d3 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1053,8 +1053,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 644f169..5019f40 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":"307133","messageId":"20161207153627.1468-5-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 04/19] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:12Z","receivedAt":"2016-12-07T15:38: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\nAdd support for %(objectname:short=<length>) which would print the\nabbreviated unique objectname of given length. When no length is\nspecified, the length is 'DEFAULT_ABBREV'. The minimum length is\n'MINIMUM_ABBREV'. The length may be exceeded to ensure that the\nprovided object name is unique.\n\nAdd tests and documentation for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nHelped-by: Jacob Keller <jacob.keller@gmail.com>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 +++\n ref-filter.c                       | 25 +++++++++++++++++++------\n t/t6300-for-each-ref.sh            | 10 ++++++++++\n 3 files changed, 32 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 392df6b..b730735 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -107,6 +107,9 @@ objectsize::\n objectname::\n \tThe object name (aka SHA-1).\n \tFor a non-ambiguous abbreviation of the object name append `:short`.\n+\tFor an abbreviation of the object name with desired length append\n+\t`:short=<length>`, where the minimum length is MINIMUM_ABBREV. The\n+\tlength may be exceeded to ensure unique object names.\n \n upstream::\n \tThe name of a local ref which can be considered ``upstream''\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7a21256..d1534d0 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -56,7 +56,10 @@ static struct used_atom {\n \t\t\tcmp_status cmp_status;\n \t\t\tconst char *str;\n \t\t} if_then_else;\n-\t\tenum { O_FULL, O_SHORT } objectname;\n+\t\tstruct {\n+\t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n+\t\t\tunsigned int length;\n+\t\t} objectname;\n \t} u;\n } *used_atom;\n static int used_atom_cnt, need_tagged, need_symref;\n@@ -119,10 +122,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@@ -595,12 +605,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 039509a..644f169 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":"307134","messageId":"20161207153627.1468-6-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 05/19] ref-filter: move get_head_description() from branch.c","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:13Z","receivedAt":"2016-12-07T15:38:01Z","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 60cc5c8..0b80c13 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 d1534d0..bb69573 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 typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;\n@@ -1081,6 +1082,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@@ -1120,9 +1152,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":"307135","messageId":"20161207153627.1468-7-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 06/19] ref-filter: introduce format_ref_array_item()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:14Z","receivedAt":"2016-12-07T15:38:02Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\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 bb69573..0f81f9f 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1799,10 +1799,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@@ -1832,9 +1832,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":"307136","messageId":"20161207153627.1468-4-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 03/19] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:11Z","receivedAt":"2016-12-07T15:38:05Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nImplement %(if:equals=<string>) wherein the if condition is only\nsatisfied if the value obtained between the %(if:...) and %(then) atom\nis the same as the given '<string>'.\n\nSimilarly, implement (if:notequals=<string>) wherein the if condition\nis only satisfied if the value obtained between the %(if:...) and\n%(then) atom is different from the given '<string>'.\n\nThis is done by introducing 'if_atom_parser()' which parses the given\n%(if) atom and then stores the data in used_atom which is later passed\non to the used_atom of the %(then) atom, so that it can do the required\ncomparisons.\n\nAdd tests and Documentation for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 +++\n ref-filter.c                       | 46 +++++++++++++++++++++++++++++++++-----\n t/t6302-for-each-ref-filter.sh     | 18 +++++++++++++++\n 3 files changed, 62 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 3018969..392df6b 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 5166326..7a21256 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -15,6 +15,7 @@\n #include \"version.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n+typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;\n \n struct align {\n \talign_type position;\n@@ -22,6 +23,8 @@ struct align {\n };\n \n struct if_then_else {\n+\tcmp_status cmp_status;\n+\tconst char *str;\n \tunsigned int then_atom_seen : 1,\n \t\telse_atom_seen : 1,\n \t\tcondition_satisfied : 1;\n@@ -49,6 +52,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\tcmp_status cmp_status;\n+\t\t\tconst char *str;\n+\t\t} if_then_else;\n \t\tenum { O_FULL, O_SHORT } objectname;\n \t} u;\n } *used_atom;\n@@ -169,6 +176,21 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)\n \tstring_list_clear(&params, 0);\n }\n \n+static void if_atom_parser(struct used_atom *atom, const char *arg)\n+{\n+\tif (!arg) {\n+\t\tatom->u.if_then_else.cmp_status = COMPARE_NONE;\n+\t\treturn;\n+\t} else if (skip_prefix(arg, \"equals=\", &atom->u.if_then_else.str)) {\n+\t\tatom->u.if_then_else.cmp_status = COMPARE_EQUAL;\n+\t} else if (skip_prefix(arg, \"notequals=\", &atom->u.if_then_else.str)) {\n+\t\tatom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;\n+\t} else {\n+\t\tdie(_(\"unrecognized %%(if) argument: %s\"), arg);\n+\t}\n+}\n+\n+\n static struct {\n \tconst char *name;\n \tcmp_type cmp_type;\n@@ -209,7 +231,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@@ -411,6 +433,9 @@ static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_stat\n \tstruct ref_formatting_stack *new;\n \tstruct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);\n \n+\tif_then_else->str = atomv->atom->u.if_then_else.str;\n+\tif_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;\n+\n \tpush_stack_element(&state->stack);\n \tnew = state->stack;\n \tnew->at_end = if_then_else_handler;\n@@ -442,10 +467,17 @@ static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_st\n \t\tdie(_(\"format: %%(then) atom used after %%(else)\"));\n \tif_then_else->then_atom_seen = 1;\n \t/*\n-\t * If there exists non-empty string between the 'if' and\n-\t * 'then' atom then the 'if' condition is satisfied.\n+\t * If the 'equals' or 'notequals' attribute is used then\n+\t * perform the required comparison. If not, only non-empty\n+\t * strings satisfy the 'if' condition.\n \t */\n-\tif (cur->output.len && !is_empty(cur->output.buf))\n+\tif (if_then_else->cmp_status == COMPARE_EQUAL) {\n+\t\tif (!strcmp(if_then_else->str, cur->output.buf))\n+\t\t\tif_then_else->condition_satisfied = 1;\n+\t} else \tif (if_then_else->cmp_status == COMPARE_UNEQUAL) {\n+\t\tif (strcmp(if_then_else->str, cur->output.buf))\n+\t\t\tif_then_else->condition_satisfied = 1;\n+\t} else if (cur->output.len && !is_empty(cur->output.buf))\n \t\tif_then_else->condition_satisfied = 1;\n \tstrbuf_reset(&cur->output);\n }\n@@ -1138,7 +1170,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":"307137","messageId":"20161207153627.1468-2-Karthik.188@gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"[PATCH v8 01/19] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-07T15:36:09Z","receivedAt":"2016-12-07T15:38:09Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nImplement %(if), %(then) and %(else) atoms. Used as\n%(if)...%(then)...%(end) or %(if)...%(then)...%(else)...%(end). If the\nformat string between %(if) and %(then) expands to an empty string, or\nto only whitespaces, then the whole %(if)...%(end) expands to the string\nfollowing %(then). Otherwise, it expands to the string following\n%(else), if any. Nesting of this construct is possible.\n\nThis is in preparation for porting over `git branch -l` to use\nref-filter APIs for printing.\n\nAdd Documentation and tests regarding the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  34 ++++++++++\n ref-filter.c                       | 134 +++++++++++++++++++++++++++++++++++--\n t/t6302-for-each-ref-filter.sh     |  76 +++++++++++++++++++++\n 3 files changed, 237 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex f57e69b..3018969 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,14 @@ As a special case for the date-type fields, you may specify a format for\n the date by adding `:` followed by date format name (see the\n values the `--date` option to linkgit:git-rev-list[1] takes).\n \n+Some atoms like %(align) and %(if) always require a matching %(end).\n+We call them \"opening atoms\" and sometimes denote them as %($open).\n+\n+When a scripting language specific quoting is in effect, everything\n+between a top-level opening atom and its matching %(end) is evaluated\n+according to the semantics of the opening atom and its result is\n+quoted.\n+\n \n EXAMPLES\n --------\n@@ -268,6 +286,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 f5f7a70..2fed7fe 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,104 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s\n \tnew->at_end_data = &atomv->u.align;\n }\n \n+static void if_then_else_handler(struct ref_formatting_stack **stack)\n+{\n+\tstruct ref_formatting_stack *cur = *stack;\n+\tstruct ref_formatting_stack *prev = cur->prev;\n+\tstruct if_then_else *if_then_else = (struct if_then_else *)cur->at_end_data;\n+\n+\tif (!if_then_else->then_atom_seen)\n+\t\tdie(_(\"format: %%(if) atom used without a %%(then) atom\"));\n+\n+\tif (if_then_else->else_atom_seen) {\n+\t\t/*\n+\t\t * There is an %(else) atom: we need to drop one state from the\n+\t\t * stack, either the %(else) branch if the condition is satisfied, or\n+\t\t * the %(then) branch if it isn't.\n+\t\t */\n+\t\tif (if_then_else->condition_satisfied) {\n+\t\t\tstrbuf_reset(&cur->output);\n+\t\t\tpop_stack_element(&cur);\n+\t\t} else {\n+\t\t\tstrbuf_swap(&cur->output, &prev->output);\n+\t\t\tstrbuf_reset(&cur->output);\n+\t\t\tpop_stack_element(&cur);\n+\t\t}\n+\t} else if (!if_then_else->condition_satisfied) {\n+\t\t/*\n+\t\t * No %(else) atom: just drop the %(then) branch if the\n+\t\t * condition is not satisfied.\n+\t\t */\n+\t\tstrbuf_reset(&cur->output);\n+\t}\n+\n+\t*stack = cur;\n+\tfree(if_then_else);\n+}\n+\n+static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n+{\n+\tstruct ref_formatting_stack *new;\n+\tstruct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);\n+\n+\tpush_stack_element(&state->stack);\n+\tnew = state->stack;\n+\tnew->at_end = if_then_else_handler;\n+\tnew->at_end_data = if_then_else;\n+}\n+\n+static int is_empty(const char *s)\n+{\n+\twhile (*s != '\\0') {\n+\t\tif (!isspace(*s))\n+\t\t\treturn 0;\n+\t\ts++;\n+\t}\n+\treturn 1;\n+}\n+\n+static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n+{\n+\tstruct ref_formatting_stack *cur = state->stack;\n+\tstruct if_then_else *if_then_else = NULL;\n+\n+\tif (cur->at_end == if_then_else_handler)\n+\t\tif_then_else = (struct if_then_else *)cur->at_end_data;\n+\tif (!if_then_else)\n+\t\tdie(_(\"format: %%(then) atom used without an %%(if) atom\"));\n+\tif (if_then_else->then_atom_seen)\n+\t\tdie(_(\"format: %%(then) atom used more than once\"));\n+\tif (if_then_else->else_atom_seen)\n+\t\tdie(_(\"format: %%(then) atom used after %%(else)\"));\n+\tif_then_else->then_atom_seen = 1;\n+\t/*\n+\t * If there exists non-empty string between the 'if' and\n+\t * 'then' atom then the 'if' condition is satisfied.\n+\t */\n+\tif (cur->output.len && !is_empty(cur->output.buf))\n+\t\tif_then_else->condition_satisfied = 1;\n+\tstrbuf_reset(&cur->output);\n+}\n+\n+static void else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n+{\n+\tstruct ref_formatting_stack *prev = state->stack;\n+\tstruct if_then_else *if_then_else = NULL;\n+\n+\tif (prev->at_end == if_then_else_handler)\n+\t\tif_then_else = (struct if_then_else *)prev->at_end_data;\n+\tif (!if_then_else)\n+\t\tdie(_(\"format: %%(else) atom used without an %%(if) atom\"));\n+\tif (!if_then_else->then_atom_seen)\n+\t\tdie(_(\"format: %%(else) atom used without a %%(then) atom\"));\n+\tif (if_then_else->else_atom_seen)\n+\t\tdie(_(\"format: %%(else) atom used more than once\"));\n+\tif_then_else->else_atom_seen = 1;\n+\tpush_stack_element(&state->stack);\n+\tstate->stack->at_end_data = prev->at_end_data;\n+\tstate->stack->at_end = prev->at_end;\n+}\n+\n static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n {\n \tstruct ref_formatting_stack *current = state->stack;\n@@ -370,14 +478,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 +1140,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":"307200","messageId":"CA+P7+xquordVY19dypqNcAuQqoRbFmHhzb0w+HXCaJmm_Ex7zQ@mail.gmail.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"Re: [PATCH v8 00/19] port branch.c to use ref-filter's printing options","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-12-08T00:01:48Z","receivedAt":"2016-12-08T00:02:15Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Wed, Dec 7, 2016 at 7:36 AM, 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> v7 can be found here: http://marc.info/?l=git&m=147863593317362&w=2\n>\n> Changes in this version:\n>\n> 1. use an enum for holding the comparision type in\n> %(if:[equals/notequals=...]) options.\n> 2. rename the 'strip' option to 'lstrip' and introduce an 'rstrip'\n> option. Also modify them to take negative values. This drops the\n> ':dri' and ':base' options.\n> 3. Drop unecessary code.\n> 4. Cleanup code and fix spacing.\n> 5. Add more comments wherever required.\n> 6. Add quote_literal_for_format(const char *s) for safer string\n> insertions in branch.c:build_format().\n>\n> Thanks to Jacob, Jackub, Junio and Matthieu for their inputs on the\n> previous version.\n>\n> Interdiff below.\n>\n> Karthik Nayak (19):\n>   ref-filter: implement %(if), %(then), and %(else) atoms\n>   ref-filter: include reference to 'used_atom' within 'atom_value'\n>   ref-filter: implement %(if:equals=<string>) and\n>     %(if:notequals=<string>)\n>   ref-filter: modify \"%(objectname:short)\" to take length\n>   ref-filter: move get_head_description() from branch.c\n>   ref-filter: introduce format_ref_array_item()\n>   ref-filter: make %(upstream:track) prints \"[gone]\" for invalid\n>     upstreams\n>   ref-filter: add support for %(upstream:track,nobracket)\n>   ref-filter: make \"%(symref)\" atom work with the ':short' modifier\n>   ref-filter: introduce refname_atom_parser_internal()\n>   ref-filter: introduce refname_atom_parser()\n>   ref-filter: make remote_ref_atom_parser() use\n>     refname_atom_parser_internal()\n>   ref-filter: rename the 'strip' option to 'lstrip'\n>   ref-filter: modify the 'lstrip=<N>' option to work with negative '<N>'\n>   ref-filter: add an 'rstrip=<N>' option to atoms which deal with\n>     refnames\n>   ref-filter: allow porcelain to translate messages in the output\n>   branch, tag: use porcelain output\n>   branch: use ref-filter printing APIs\n>   branch: implement '--format' option\n>\n>  Documentation/git-branch.txt       |   7 +-\n>  Documentation/git-for-each-ref.txt |  86 +++++--\n>  builtin/branch.c                   | 290 +++++++---------------\n>  builtin/tag.c                      |   6 +-\n>  ref-filter.c                       | 488 +++++++++++++++++++++++++++++++------\n>  ref-filter.h                       |   7 +\n>  t/t3203-branch-output.sh           |  16 +-\n>  t/t6040-tracking-info.sh           |   2 +-\n>  t/t6300-for-each-ref.sh            |  88 ++++++-\n>  t/t6302-for-each-ref-filter.sh     |  94 +++++++\n>  10 files changed, 784 insertions(+), 300 deletions(-)\n>\n> Interdiff:\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index f4ad297..c72baeb 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -92,13 +92,14 @@ refname::\n>         The name of the ref (the part after $GIT_DIR/).\n>         For a non-ambiguous short name of the ref append `:short`.\n>         The option core.warnAmbiguousRefs is used to select the strict\n> -       abbreviation mode. If `strip=<N>` is appended, strips `<N>`\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. 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> +       abbreviation mode. If `lstrip=<N>` or `rstrip=<N>` option can\n\nGrammar here, drop the If before `lstrip since you're referring to\nmultiples and you say \"x can be appended to y\" rather than \"if x is\nadded, do y\"\n\n> +       be appended to strip `<N>` slash-separated path components\n> +       from or end of the refname respectively (e.g.,\n> +       `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and\n> +       `%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).  if\n> +       `<N>` is a negative number, then only `<N>` path components\n> +       are left behind.  If a displayed ref has fewer components than\n> +       `<N>`, the command aborts with an error.\n>\n\nWould it make more sense to not die and instead just return the empty\nstring? On the one hand, if we die() it's obvious that you tried to\nstrip too many components. But on the other hand, it's also somewhat\nannoying to have the whole command fail because we happen upon a\nsingle ref that has fewer components?\n\nSo, for positive numbers, we simply strip what we can, which may\nresult in the empty string, and for negative numbers, we keep up to\nwhat we said, while potentially keeping the entire string. I feel\nthat's a better alternative than a die() in the middle of a ref\nfilter..\n\nWhat are other people's thoughts on this?\n\nThanks,\nJake\n"},{"id":"307280","messageId":"xmqqa8c6tfhc.fsf@gitster.mtv.corp.google.com","threadId":"44642","inReplyTo":"CA+P7+xquordVY19dypqNcAuQqoRbFmHhzb0w+HXCaJmm_Ex7zQ@mail.gmail.com","subject":"Re: [PATCH v8 00/19] port branch.c to use ref-filter's printing options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-08T18:58:39Z","receivedAt":"2016-12-08T18:58:47Z","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>> +       are left behind.  If a displayed ref has fewer components than\n>> +       `<N>`, the command aborts with an error.\n>>\n>\n> Would it make more sense to not die and instead just return the empty\n> string? On the one hand, if we die() it's obvious that you tried to\n> strip too many components. But on the other hand, it's also somewhat\n> annoying to have the whole command fail because we happen upon a\n> single ref that has fewer components?\n>\n> So, for positive numbers, we simply strip what we can, which may\n> result in the empty string, and for negative numbers, we keep up to\n> what we said, while potentially keeping the entire string. I feel\n> that's a better alternative than a die() in the middle of a ref\n> filter..\n>\n> What are other people's thoughts on this?\n\nThere probably are three ways to handle a formatting request that\ncannot be satisfied for some refs but not others [*1*].  I agree\nwith you that dying the whole thing is probably the least useful\none.\n\nWe already format \"%(taggername)\" into an empty string when the ref\npoints at an object that is not an annotated tag, and substituting\nan unsatisifiable request \"%(refname:lstrip=N)\" with an empty string\nfor a ref whose name does not have enough number of components is in\nline with that existing practice.\n\nThe other possibility is to omit refs that cannot be formatted\naccording to the format specifier.  We do not currently have\nprovision for doing so, but it may not be bad if we can say:\n\n    $ git for-each-ref \\\n\t--require=\"%(taggername)\" \\\n\t--require=\"%(refname:lstrip=4)\" \\\n\t--format=\"%(refname:short)\" \\\n\trefs/tags/\n\nto list _only_ annotated tags that has at least 4 components from\nrefs/tags/ hierarchy.\n\nThe \"--require\" thing obviously is an orthogonal feature, that\nnobody has asked for, and does not have to exist.  For the purpose\nof this review thread, I think it is OK to just conclude:\n\n    \"--format\" should replace \"%(any unsatisifiable atom)\" with an\n    empty string.\n\nThanks.\n\n[Footnote]\n\n*1* A malformed formatting request (e.g. %(if) that is not closed)\n    cannot be satisified but that is true for all refs and is\n    outside of the scope of this discussion.  The command should die\n    and I think it already does.\n"},{"id":"307316","messageId":"xmqqpol2rn0z.fsf@gitster.mtv.corp.google.com","threadId":"44642","inReplyTo":"20161207153627.1468-1-Karthik.188@gmail.com","subject":"Re: [PATCH v8 00/19] port branch.c to use ref-filter's printing options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-08T23:58:36Z","receivedAt":"2016-12-08T23:58:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks.  \n\nWill replace, with the attached stylistic fixes squashed in for\nminor issues that were spotted by my mechanical pre-acceptance\nfilter.\n\n ref-filter.c | 10 ++++++----\n 1 file changed, 6 insertions(+), 4 deletions(-)\n\ndiff --git b/ref-filter.c a/ref-filter.c\nindex a68ed7b147..a9d2c6a89d 100644\n--- b/ref-filter.c\n+++ a/ref-filter.c\n@@ -76,7 +76,7 @@ static struct used_atom {\n \t\tstruct {\n \t\t\tenum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;\n \t\t\tstruct refname_atom refname;\n-\t\t\tunsigned int nobracket: 1;\n+\t\t\tunsigned int nobracket : 1;\n \t\t} remote_ref;\n \t\tstruct {\n \t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n@@ -559,7 +559,7 @@ static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_st\n \tif (if_then_else->cmp_status == COMPARE_EQUAL) {\n \t\tif (!strcmp(if_then_else->str, cur->output.buf))\n \t\t\tif_then_else->condition_satisfied = 1;\n-\t} else \tif (if_then_else->cmp_status == COMPARE_UNEQUAL) {\n+\t} else if (if_then_else->cmp_status == COMPARE_UNEQUAL) {\n \t\tif (strcmp(if_then_else->str, cur->output.buf))\n \t\t\tif_then_else->condition_satisfied = 1;\n \t} else if (cur->output.len && !is_empty(cur->output.buf))\n@@ -1106,7 +1106,8 @@ static const char *lstrip_ref_components(const char *refname, int len)\n \t\tconst char *p = refname;\n \n \t\t/* Find total no of '/' separated path-components */\n-\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++);\n+\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++)\n+\t\t\t;\n \t\t/*\n \t\t * The number of components we need to strip is now\n \t\t * the total minus the components to be left (Plus one\n@@ -1140,7 +1141,8 @@ static const char *rstrip_ref_components(const char *refname, int len)\n \t\tconst char *p = refname;\n \n \t\t/* Find total no of '/' separated path-components */\n-\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++);\n+\t\tfor (i = 0; p[i]; p[i] == '/' ? i++ : *p++)\n+\t\t\t;\n \t\t/*\n \t\t * The number of components we need to strip is now\n \t\t * the total minus the components to be left (Plus one\n"},{"id":"307322","messageId":"CA+P7+xrGuEjc-QFkPSg7FCNFfq-rRTa7PsuiiY-1Edj381LidA@mail.gmail.com","threadId":"44642","inReplyTo":"xmqqa8c6tfhc.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v8 00/19] port branch.c to use ref-filter's printing options","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-12-09T01:45:29Z","receivedAt":"2016-12-09T01:45:57Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Thu, Dec 8, 2016 at 10:58 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> *1* A malformed formatting request (e.g. %(if) that is not closed)\n>     cannot be satisified but that is true for all refs and is\n>     outside of the scope of this discussion.  The command should die\n>     and I think it already does.\n\nAgreed. I was only making the case that if it could \"possibly\" be\nsatisfied, then we shouldn't die.\n\nThanks,\nJake\n"},{"id":"307354","messageId":"20161209140345.76ybodldmg2lxgbn@sigill.intra.peff.net","threadId":"44642","inReplyTo":"20161207153627.1468-19-Karthik.188@gmail.com","subject":"Re: [PATCH v8 18/19] branch: use ref-filter printing APIs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-12-09T14:03:46Z","receivedAt":"2016-12-09T14:03:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Dec 07, 2016 at 09:06:26PM +0530, Karthik Nayak wrote:\n\n> +const char *quote_literal_for_format(const char *s)\n>  {\n> +\tstruct strbuf buf = STRBUF_INIT;\n>  \n> +\tstrbuf_reset(&buf);\n> +\twhile (*s) {\n> +\t\tconst char *ep = strchrnul(s, '%');\n> +\t\tif (s < ep)\n> +\t\t\tstrbuf_add(&buf, s, ep - s);\n> +\t\tif (*ep == '%') {\n> +\t\t\tstrbuf_addstr(&buf, \"%%\");\n> +\t\t\ts = ep + 1;\n> +\t\t} else {\n> +\t\t\ts = ep;\n> +\t\t}\n>  \t}\n> +\treturn buf.buf;\n>  }\n\nYou should use strbuf_detach() to return the buffer from a strbuf.\nOtherwise it is undefined whether the pointer is allocated or not (and\nwhether it needs to be freed).\n\nIn this case, if \"s\" is empty, buf.buf would point to a string literal,\nbut otherwise to allocated memory. strbuf_detach() normalizes that.\n\nBut...\n\n> +\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),\n\nThis caller never stores the return value, and it ends up leaking. So I\nwonder if you wanted \"static struct strbuf\" in the first place (and that\nwould explain the strbuf_reset() in your function).\n\n-Peff\n"},{"id":"307498","messageId":"CAOLa=ZRh5H=DuHJtE4p4R5M3f=2+G62OPztTDqhmpYr3c19zQw@mail.gmail.com","threadId":"44642","inReplyTo":"CA+P7+xquordVY19dypqNcAuQqoRbFmHhzb0w+HXCaJmm_Ex7zQ@mail.gmail.com","subject":"Re: [PATCH v8 00/19] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-12T10:42:26Z","receivedAt":"2016-12-12T10:43:03Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Thu, Dec 8, 2016 at 5:31 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n>> index f4ad297..c72baeb 100644\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -92,13 +92,14 @@ refname::\n>>         The name of the ref (the part after $GIT_DIR/).\n>>         For a non-ambiguous short name of the ref append `:short`.\n>>         The option core.warnAmbiguousRefs is used to select the strict\n>> -       abbreviation mode. If `strip=<N>` is appended, strips `<N>`\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. 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>> +       abbreviation mode. If `lstrip=<N>` or `rstrip=<N>` option can\n>\n> Grammar here, drop the If before `lstrip since you're referring to\n> multiples and you say \"x can be appended to y\" rather than \"if x is\n> added, do y\"\n>\n\nWill do. Thanks.\n\n>> +       be appended to strip `<N>` slash-separated path components\n>> +       from or end of the refname respectively (e.g.,\n>> +       `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and\n>> +       `%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).  if\n>> +       `<N>` is a negative number, then only `<N>` path components\n>> +       are left behind.  If a displayed ref has fewer components than\n>> +       `<N>`, the command aborts with an error.\n>>\n>\n> Would it make more sense to not die and instead just return the empty\n> string? On the one hand, if we die() it's obvious that you tried to\n> strip too many components. But on the other hand, it's also somewhat\n> annoying to have the whole command fail because we happen upon a\n> single ref that has fewer components?\n>\n> So, for positive numbers, we simply strip what we can, which may\n> result in the empty string, and for negative numbers, we keep up to\n> what we said, while potentially keeping the entire string. I feel\n> that's a better alternative than a die() in the middle of a ref\n> filter..\n>\n> What are other people's thoughts on this?\n\nI am _for_ this. Even I think it'd be better to return an empty string rather\nthan just die in the middle.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"307499","messageId":"CAOLa=ZSuy9NjxaPS4V2LyjPo6Ek3r4gAvXBsW2VZJXgGtSq=kA@mail.gmail.com","threadId":"44642","inReplyTo":"xmqqpol2rn0z.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v8 00/19] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-12T10:43:36Z","receivedAt":"2016-12-12T10:44:12Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Fri, Dec 9, 2016 at 5:28 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Thanks.\n>\n> Will replace, with the attached stylistic fixes squashed in for\n> minor issues that were spotted by my mechanical pre-acceptance\n> filter.\n>\n\nThanks for this. Will add it to my local branch too if there's a need\nfor a re-roll.\n"},{"id":"307503","messageId":"CAOLa=ZSPDLwziGEvyixebAkS2M1JMYidQNHfDbnmYarFCjn80A@mail.gmail.com","threadId":"44642","inReplyTo":"20161209140345.76ybodldmg2lxgbn@sigill.intra.peff.net","subject":"Re: [PATCH v8 18/19] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-12T11:20:20Z","receivedAt":"2016-12-12T11:20:56Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Fri, Dec 9, 2016 at 7:33 PM, Jeff King <peff@peff.net> wrote:\n> On Wed, Dec 07, 2016 at 09:06:26PM +0530, Karthik Nayak wrote:\n>\n>> +const char *quote_literal_for_format(const char *s)\n>>  {\n>> +     struct 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> You should use strbuf_detach() to return the buffer from a strbuf.\n> Otherwise it is undefined whether the pointer is allocated or not (and\n> whether it needs to be freed).\n>\n> In this case, if \"s\" is empty, buf.buf would point to a string literal,\n> but otherwise to allocated memory. strbuf_detach() normalizes that.\n>\n> But...\n>\n>> +                         branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),\n>\n> This caller never stores the return value, and it ends up leaking. So I\n> wonder if you wanted \"static struct strbuf\" in the first place (and that\n> would explain the strbuf_reset() in your function).\n>\n> -Peff\n\nAh! Yes this should be 'static struct strbuf' indeed, I blindly copied Junio's\nsuggestion.\n\nstrbuf_detach() is also a better way to go.\n\nThanks.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"307506","messageId":"20161212121548.aj2ptnmrqt67anlc@sigill.intra.peff.net","threadId":"44642","inReplyTo":"CAOLa=ZSPDLwziGEvyixebAkS2M1JMYidQNHfDbnmYarFCjn80A@mail.gmail.com","subject":"Re: [PATCH v8 18/19] branch: use ref-filter printing APIs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-12-12T12:15:49Z","receivedAt":"2016-12-12T12:15:58Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Dec 12, 2016 at 04:50:20PM +0530, Karthik Nayak wrote:\n\n> > This caller never stores the return value, and it ends up leaking. So I\n> > wonder if you wanted \"static struct strbuf\" in the first place (and that\n> > would explain the strbuf_reset() in your function).\n> \n> Ah! Yes this should be 'static struct strbuf' indeed, I blindly copied Junio's\n> suggestion.\n> \n> strbuf_detach() is also a better way to go.\n\nOne of the other, though. If it's static, then you _don't_ want to\ndetach.\n\n-Peff\n"},{"id":"307508","messageId":"CAOLa=ZT1KN_iw7JzB78hPb-LrY_yaDZk0D+TqY2W9hCOftzumA@mail.gmail.com","threadId":"44642","inReplyTo":"20161212121548.aj2ptnmrqt67anlc@sigill.intra.peff.net","subject":"Re: [PATCH v8 18/19] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-12T16:29:49Z","receivedAt":"2016-12-12T16:30:49Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Mon, Dec 12, 2016 at 5:45 PM, Jeff King <peff@peff.net> wrote:\n> On Mon, Dec 12, 2016 at 04:50:20PM +0530, Karthik Nayak wrote:\n>\n>> > This caller never stores the return value, and it ends up leaking. So I\n>> > wonder if you wanted \"static struct strbuf\" in the first place (and that\n>> > would explain the strbuf_reset() in your function).\n>>\n>> Ah! Yes this should be 'static struct strbuf' indeed, I blindly copied Junio's\n>> suggestion.\n>>\n>> strbuf_detach() is also a better way to go.\n>\n> One of the other, though. If it's static, then you _don't_ want to\n> detach.\n>\n\nWait. Why not? since it gets added to the strbuf's within\nbuild_format() and that\nvalue is not needed again, why do we need to re-allocate? we can use the same\nvariable again (i.e by keeping it as static).\n\nI'm sorry, but I didn't get why these two should be mutually exclusive?\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"307509","messageId":"20161212164020.btrrg2f62haxt7sh@sigill.intra.peff.net","threadId":"44642","inReplyTo":"CAOLa=ZT1KN_iw7JzB78hPb-LrY_yaDZk0D+TqY2W9hCOftzumA@mail.gmail.com","subject":"Re: [PATCH v8 18/19] branch: use ref-filter printing APIs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-12-12T16:40:20Z","receivedAt":"2016-12-12T16:40:29Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Dec 12, 2016 at 09:59:49PM +0530, Karthik Nayak wrote:\n\n> >> > This caller never stores the return value, and it ends up leaking. So I\n> >> > wonder if you wanted \"static struct strbuf\" in the first place (and that\n> >> > would explain the strbuf_reset() in your function).\n> >>\n> >> Ah! Yes this should be 'static struct strbuf' indeed, I blindly copied Junio's\n> >> suggestion.\n> >>\n> >> strbuf_detach() is also a better way to go.\n> >\n> > One of the other, though. If it's static, then you _don't_ want to\n> > detach.\n> >\n> \n> Wait. Why not? since it gets added to the strbuf's within\n> build_format() and that\n> value is not needed again, why do we need to re-allocate? we can use the same\n> variable again (i.e by keeping it as static).\n> \n> I'm sorry, but I didn't get why these two should be mutually exclusive?\n\nWhat is the memory ownership convention for the return value from the\nfunction?\n\nIf the caller should own the memory, then you want to do this:\n\n  char *foo(...)\n  {\n\tstruct strbuf buf = STRBUF_INIT;\n\t... fill up buf ...\n\treturn strbuf_detach(&buf, NULL);\n  }\n\nThe \"detach\" disconnects the memory from the strbuf (which is going out\nof scope anyway), and the only pointer left to it is in the caller. It's\nimportant to use \"detach\" here and not just return the pointer, because\nthat ensures that the returned value is always allocated memory (and\nnever strbuf_slopbuf).\n\nIf the caller should not own the memory, then the function retains\nownership. And you want something like this:\n\n  char *foo(...)\n  {\n\tstatic struct strbuf buf = STRBUF_INIT;\n\tstrbuf_reset(&buf);\n\t... fill up buf ...\n\treturn buf.buf;\n  }\n\nThe same buffer is reused over and over. The \"reset\" call clears any\nleftover bits from the last invocation, and you must _not_ call\nstrbuf_detach() in the return, as it disconnects the memory from the\nstrbuf (and so next time you'd end up allocating again, and each return\nvalue becomes a memory leak).\n\nDoes that make sense?\n\n-Peff\n"},{"id":"307512","messageId":"CAOLa=ZQQfi3W3z1_QcvxkW_4oM=PDx3MZupFehMyxs4YQvTb2w@mail.gmail.com","threadId":"44642","inReplyTo":"20161212164020.btrrg2f62haxt7sh@sigill.intra.peff.net","subject":"Re: [PATCH v8 18/19] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-12-12T17:18:24Z","receivedAt":"2016-12-12T17:19:03Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Mon, Dec 12, 2016 at 10:10 PM, Jeff King <peff@peff.net> wrote:\n> On Mon, Dec 12, 2016 at 09:59:49PM +0530, Karthik Nayak wrote:\n>\n>> >> > This caller never stores the return value, and it ends up leaking. So I\n>> >> > wonder if you wanted \"static struct strbuf\" in the first place (and that\n>> >> > would explain the strbuf_reset() in your function).\n>> >>\n>> >> Ah! Yes this should be 'static struct strbuf' indeed, I blindly copied Junio's\n>> >> suggestion.\n>> >>\n>> >> strbuf_detach() is also a better way to go.\n>> >\n>> > One of the other, though. If it's static, then you _don't_ want to\n>> > detach.\n>> >\n>>\n>> Wait. Why not? since it gets added to the strbuf's within\n>> build_format() and that\n>> value is not needed again, why do we need to re-allocate? we can use the same\n>> variable again (i.e by keeping it as static).\n>>\n>> I'm sorry, but I didn't get why these two should be mutually exclusive?\n>\n> What is the memory ownership convention for the return value from the\n> function?\n>\n> If the caller should own the memory, then you want to do this:\n>\n>   char *foo(...)\n>   {\n>         struct strbuf buf = STRBUF_INIT;\n>         ... fill up buf ...\n>         return strbuf_detach(&buf, NULL);\n>   }\n>\n> The \"detach\" disconnects the memory from the strbuf (which is going out\n> of scope anyway), and the only pointer left to it is in the caller. It's\n> important to use \"detach\" here and not just return the pointer, because\n> that ensures that the returned value is always allocated memory (and\n> never strbuf_slopbuf).\n>\n> If the caller should not own the memory, then the function retains\n> ownership. And you want something like this:\n>\n>   char *foo(...)\n>   {\n>         static struct strbuf buf = STRBUF_INIT;\n>         strbuf_reset(&buf);\n>         ... fill up buf ...\n>         return buf.buf;\n>   }\n>\n> The same buffer is reused over and over. The \"reset\" call clears any\n> leftover bits from the last invocation, and you must _not_ call\n> strbuf_detach() in the return, as it disconnects the memory from the\n> strbuf (and so next time you'd end up allocating again, and each return\n> value becomes a memory leak).\n\nAh! perfect, makes perfect sense. Sorry you had to spell that out for me.\n'strbuf_detach() in the return, as it disconnects the memory from the strbuf'\nthat was what I was missing, thanks.\n\n-- \nRegards,\nKarthik Nayak\n"}]}