{"thread":{"id":"42113","subject":"[PATCH v5 01/16] ref-filter: include reference to 'used_atom' within 'atom_value'","startedAt":"2016-04-22T19:03:51Z","lastAt":"2016-04-22T19:04:07Z","messageCount":17,"participants":["Karthik Nayak"],"isPatch":true,"patchVersion":5,"patchTotal":16},"messages":[{"id":"284175","messageId":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":null,"subject":"[PATCH v5 00/16] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:03:51Z","receivedAt":"2016-04-22T19:03:51Z","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)\n\nChanges in this version:\n1. Rebased on top of jk/branch-shortening-funny-symrefs ($gmane/291295).\n2. Introduced refname_atom_parser_internal() to act as a common ground\nfor ref-printing atoms. Written by Jeff ($gmane/291497).\n3. Ensure that ':dir', 'base', 'strip' is available to all ref-printing\natoms. Written by Jeff.\n4. Fix a memory leak which was reported by Stefan ($gmane/291703).\n\nThanks to Junio, Jeff and Stefan for suggestions on the previous version.\n\nKarthik Nayak (16):\n  ref-filter: include reference to 'used_atom' within 'atom_value'\n  ref-filter: implement %(if:equals=<string>) and\n    %(if:notequals=<string>)\n  ref-filter: modify \"%(objectname:short)\" to take length\n  ref-filter: move get_head_description() from branch.c\n  ref-filter: introduce format_ref_array_item()\n  ref-filter: make %(upstream:track) prints \"[gone]\" for invalid\n    upstreams\n  ref-filter: add support for %(upstream:track,nobracket)\n  ref-filter: make \"%(symref)\" atom work with the ':short' modifier\n  ref-filter: introduce refname_atom_parser_internal()\n  ref-filter: introduce symref_atom_parser() and refname_atom_parser()\n  ref-filter: make remote_ref_atom_parser() use\n    refname_atom_parser_internal()\n  ref-filter: add `:dir` and `:base` options for ref printing atoms\n  ref-filter: allow porcelain to translate messages in the output\n  branch, tag: use porcelain output\n  branch: use ref-filter printing APIs\n  branch: implement '--format' option\n\n Documentation/git-branch.txt       |   7 +-\n Documentation/git-for-each-ref.txt |  42 +++--\n builtin/branch.c                   | 275 +++++++++---------------------\n builtin/tag.c                      |   2 +\n ref-filter.c                       | 333 +++++++++++++++++++++++++++++--------\n ref-filter.h                       |   7 +\n t/t3203-branch-output.sh           |  16 +-\n t/t6040-tracking-info.sh           |   2 +-\n t/t6300-for-each-ref.sh            |  73 +++++++-\n t/t6302-for-each-ref-filter.sh     |  18 ++\n 10 files changed, 488 insertions(+), 287 deletions(-)\n\nInterdiff:\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 0954269..4a5e9ea 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -116,21 +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` in the same way as\n-\t`refname` above.  Additionally respects `:track` to show\n-\t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n-\tversion: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n-\tor \"=\" (in sync). `:track` also prints \"[gone]\" whenever\n-\tunknown upstream ref is encountered. Append `:track,nobracket`\n-\tto show tracking information without brackets (i.e \"ahead N,\n-\tbehind M\").  Has no effect if the ref does not have tracking\n-\tinformation associated with it.\n+\tfrom the displayed ref. Respects `:short`, `:strip`, `:base`\n+\tand `:dir` in the same way as `refname` above.  Additionally\n+\trespects `:track` to show \"[ahead N, behind M]\" and\n+\t`:trackshort` to show the terse version: \">\" (ahead), \"<\"\n+\t(behind), \"<>\" (ahead and behind), or \"=\" (in sync). `:track`\n+\talso prints \"[gone]\" whenever unknown upstream ref is\n+\tencountered. Append `:track,nobracket` to show tracking\n+\tinformation without brackets (i.e \"ahead N, behind M\").  Has\n+\tno effect if the ref does not have tracking information\n+\tassociated with it.\n \n push::\n-\tThe name of a local ref which represents the `@{push}` 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`, `:trackshort`, `:base` and `:dir` options as\n+\t`upstream` does. Produces an empty string if no `@{push}` ref\n+\tis configured.\n \n HEAD::\n \t'*' if HEAD matches current ref (the checked out branch), ' '\n@@ -165,6 +167,12 @@ 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`,\n+\t`:strip`, `:base` and `:dir` options in the same way as\n+\t`refname` above.\n+\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex c9a2e5b..6847ac3 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -288,9 +288,11 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n \n \t\tskip_prefix(it->refname, \"refs/heads/\", &desc);\n \t\tskip_prefix(it->refname, \"refs/remotes/\", &desc);\n-\t\tif (it->kind == FILTER_REFS_DETACHED_HEAD)\n-\t\t\tw = strlen(get_head_description());\n-\t\telse\n+\t\tif (it->kind == FILTER_REFS_DETACHED_HEAD) {\n+\t\t\tchar *head_desc = get_head_description();\n+\t\t\tw = strlen(head_desc);\n+\t\t\tfree(head_desc);\n+\t\t} else\n \t\t\tw = utf8_strwidth(desc);\n \n \t\tif (it->kind == FILTER_REFS_REMOTES)\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 3435df1..74c4869 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -50,6 +50,11 @@ struct if_then_else {\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+};\n+\n /*\n  * An atom is a valid field atom listed below, possibly prefixed with\n  * a \"*\" to denote deref_tag().\n@@ -67,7 +72,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@@ -82,11 +88,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\tenum { S_FULL, S_SHORT } symref;\n-\t\tstruct {\n-\t\t\tenum { R_BASE, R_DIR, R_NORMAL, R_SHORT, R_STRIP } option;\n-\t\t\tunsigned int strip;\n-\t\t} refname;\n+\t\tstruct refname_atom refname;\n \t} u;\n } *used_atom;\n static int used_atom_cnt, need_tagged, need_symref;\n@@ -100,13 +102,34 @@ 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 if (!strcmp(arg, \"dir\"))\n+\t\tatom->option = R_DIR;\n+\telse if (!strcmp(arg, \"base\"))\n+\t\tatom->option = R_BASE;\n+\telse\n+\t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n+}\n+\n static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n {\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\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@@ -116,16 +139,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@@ -170,7 +194,7 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg)\n \telse if (!strcmp(arg, \"short\"))\n \t\tatom->u.objectname.option = O_SHORT;\n \telse if (skip_prefix(arg, \"short=\", &arg)) {\n-\t\tatom->u.contents.option = O_LENGTH;\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@@ -180,6 +204,16 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg)\n \t\tdie(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n }\n \n+static void symref_atom_parser(struct used_atom *atom, const char *arg)\n+{\n+\treturn refname_atom_parser_internal(&atom->u.refname, arg, atom->name);\n+}\n+\n+static void refname_atom_parser(struct used_atom *atom, const char *arg)\n+{\n+\treturn refname_atom_parser_internal(&atom->u.refname, arg, atom->name);\n+}\n+\n static align_type parse_align_position(const char *s)\n {\n \tif (!strcmp(s, \"right\"))\n@@ -242,41 +276,12 @@ static void if_atom_parser(struct used_atom *atom, const char *arg)\n \t\tdie(_(\"unrecognized %%(if) argument: %s\"), arg);\n }\n \n-static void symref_atom_parser(struct used_atom *atom, const char *arg)\n-{\n-\tif (!arg)\n-\t\tatom->u.symref = S_FULL;\n-\telse if (!strcmp(arg, \"short\"))\n-\t\tatom->u.symref = S_SHORT;\n-\telse\n-\t\tdie(_(\"unrecognized %%(symref) argument: %s\"), arg);\n-}\n-\n-static void refname_atom_parser(struct used_atom *atom, const char *arg)\n-{\n-\tif (!arg)\n-\t\tatom->u.refname.option = R_NORMAL;\n-\telse if (!strcmp(arg, \"short\"))\n-\t\tatom->u.refname.option = R_SHORT;\n-\telse if (skip_prefix(arg, \"strip=\", &arg)) {\n-\t\tatom->u.contents.option = R_STRIP;\n-\t\tif (strtoul_ui(arg, 10, &atom->u.refname.strip) ||\n-\t\t\tatom->u.refname.strip <= 0)\n-\t\t\tdie(_(\"positive value expected refname:strip=%s\"), arg);\n-\t} else if (!strcmp(arg, \"dir\"))\n-\t\tatom->u.contents.option = R_DIR;\n-\telse if (!strcmp(arg, \"base\"))\n-\t\tatom->u.contents.option = R_BASE;\n-\telse\n-\t\tdie(_(\"unrecognized %%(refname) argument: %s\"), arg);\n-}\n-\n static struct {\n \tconst char *name;\n \tcmp_type cmp_type;\n \tvoid (*parser)(struct used_atom *atom, const char *arg);\n } valid_atom[] = {\n-\t{ \"refname\", FIELD_STR, refname_atom_parser },\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@@ -1098,7 +1103,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 :strip\"),\n \t\t\t    refname, len);\n \t\tcase '/':\n \t\t\tremaining--;\n@@ -1108,12 +1113,40 @@ static const char *strip_ref_components(const char *refname, unsigned int len)\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 if (atom->option == R_BASE) {\n+\t\tconst char *sp, *ep;\n+\npp+\t\tif (skip_prefix(refname, \"refs/\", &sp)) {\n+\t\t\tep = strchr(sp, '/');\n+\t\t\tif (!ep)\n+\t\t\t\treturn \"\";\n+\t\t\treturn xstrndup(sp, ep - sp);\n+\t\t}\n+\t\treturn \"\";\n+\t} else if (atom->option == R_DIR) {\n+\t\tconst char *sp, *ep;\n+\n+\t\tsp = refname;\n+\t\tep = strrchr(sp, '/');\n+\t\tif (!ep)\n+\t\t\treturn \"\";\n+\t\treturn xstrndup(sp, ep - sp);\n+\t} else\n+\t\treturn refname;\n+}\n+\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 \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@@ -1145,8 +1178,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)\n@@ -1184,41 +1217,15 @@ static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref\n {\n \tif (!ref->symref)\n \t\treturn \"\";\n-\telse if (atom->u.symref == S_SHORT)\n-\t\treturn shorten_unambiguous_ref(ref->symref,\n-\t\t\t\t\t       warn_ambiguous_refs);\n \telse\n-\t\treturn ref->symref;\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-\tif (atom->u.refname.option == R_SHORT)\n-\t\treturn shorten_unambiguous_ref(ref->refname, warn_ambiguous_refs);\n-\telse if (atom->u.refname.option == R_STRIP)\n-\t\treturn strip_ref_components(ref->refname, atom->u.refname.strip);\n-\telse if (atom->u.refname.option == R_BASE) {\n-\t\tconst char *sp, *ep;\n-\n-\t\tif (skip_prefix(ref->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->u.refname.option == R_DIR) {\n-\t\tconst char *sp, *ep;\n-\n-\t\tsp = ref->refname;\n-\t\tep = strrchr(sp, '/');\n-\t\tif (!ep)\n-\t\t\treturn \"\";\n-\t\treturn xstrndup(sp, ep - sp);\n-\t} else\n-\t\treturn ref->refname;\n+\treturn show_ref(&atom->u.refname, ref->refname);\n }\n \n /*\ndiff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh\nindex 0bd6368..d8edaf2 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 &&\n@@ -203,6 +203,8 @@ test_expect_success 'git branch --format option' '\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\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 36d32d7..8ff6568 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -57,8 +57,14 @@ test_atom head refname:dir refs/heads\n test_atom head refname:base heads\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n+test_atom head upstream:strip=2 origin/master\n+test_atom head upstream:dir refs/remotes/origin\n+test_atom head upstream:base remotes\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n+test_atom head push:strip=1 remotes/myfork/master\n+test_atom head push:dir refs/remotes/myfork\n+test_atom head push:base remotes\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n@@ -591,4 +597,31 @@ 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+cat >expected <<EOF\n+refs/heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:dir) atom' '\n+\tgit for-each-ref --format=\"%(symref:dir)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:base) atom' '\n+\tgit for-each-ref --format=\"%(symref:base)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n\n\n-- \n2.8.0\n"},{"id":"284169","messageId":"1461351847-22903-2-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 01/16] ref-filter: include reference to 'used_atom' within 'atom_value'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:03:52Z","receivedAt":"2016-04-22T19:03:52Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Ensure that each 'atom_value' has a reference to its corresponding\n'used_atom'. This let's us use values within 'used_atom' in the\n'handler' function.\n\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 41e73f0..12e646c 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -230,11 +230,9 @@ struct ref_formatting_state {\n \n struct atom_value {\n \tconst char *s;\n-\tunion {\n-\t\tstruct align align;\n-\t} u;\n \tvoid (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n+\tstruct used_atom *atom;\n };\n \n /*\n@@ -370,7 +368,7 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s\n \tpush_stack_element(&state->stack);\n \tnew = state->stack;\n \tnew->at_end = end_align_handler;\n-\tnew->at_end_data = &atomv->u.align;\n+\tnew->at_end_data = &atomv->atom->u.align;\n }\n \n static void if_then_else_handler(struct ref_formatting_stack **stack)\n@@ -1069,6 +1067,7 @@ static void populate_value(struct ref_array_item *ref)\n \t\tstruct branch *branch = NULL;\n \n \t\tv->handler = append_atom;\n+\t\tv->atom = atom;\n \n \t\tif (*name == '*') {\n \t\t\tderef = 1;\n@@ -1133,7 +1132,6 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\t\tv->s = \" \";\n \t\t\tcontinue;\n \t\t} else if (starts_with(name, \"align\")) {\n-\t\t\tv->u.align = atom->u.align;\n \t\t\tv->handler = align_atom_handler;\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"end\")) {\n-- \n2.8.0\n"},{"id":"284172","messageId":"1461351847-22903-3-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 02/16] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:03:53Z","receivedAt":"2016-04-22T19:03:53Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Implement %(if:equals=<string>) wherein the if condition is only\nsatisfied if the value obtained between the %(if:...) and %(then) atom\nis the same as the given '<string>'.\n\nSimilarly, implement (if:notequals=<string>) wherein the if condition\nis only satisfied if the value obtained between the %(if:...) and\n%(then) atom is differnt from the given '<string>'.\n\nThis is done by introducing 'if_atom_parser()' which parses the given\n%(if) atom and then stores the data in used_atom which is later passed\non to the used_atom of the %(then) atom, so that it can do the required\ncomparisons.\n\nAdd tests and Documentation for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  3 +++\n ref-filter.c                       | 43 +++++++++++++++++++++++++++++++++-----\n t/t6302-for-each-ref-filter.sh     | 18 ++++++++++++++++\n 3 files changed, 59 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 473b6bf..3e7db10 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -152,6 +152,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 12e646c..857a8b5 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -22,6 +22,8 @@ struct align {\n };\n \n struct if_then_else {\n+\tconst char *if_equals,\n+\t\t*not_equals;\n \tunsigned int then_atom_seen : 1,\n \t\telse_atom_seen : 1,\n \t\tcondition_satisfied : 1;\n@@ -49,6 +51,10 @@ static struct used_atom {\n \t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n \t\t\tunsigned int nlines;\n \t\t} contents;\n+\t\tstruct {\n+\t\t\tconst char *if_equals,\n+\t\t\t\t*not_equals;\n+\t\t} if_then_else;\n \t\tenum { O_FULL, O_SHORT } objectname;\n \t} u;\n } *used_atom;\n@@ -169,6 +175,19 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)\n \tstring_list_clear(&params, 0);\n }\n \n+static void if_atom_parser(struct used_atom *atom, const char *arg)\n+{\n+\tif (!arg)\n+\t\treturn;\n+\telse if (skip_prefix(arg, \"equals=\", &atom->u.if_then_else.if_equals))\n+\t\t ;\n+\telse if (skip_prefix(arg, \"notequals=\", &atom->u.if_then_else.not_equals))\n+\t\t;\n+\telse\n+\t\tdie(_(\"unrecognized %%(if) argument: %s\"), arg);\n+}\n+\n+\n static struct {\n \tconst char *name;\n \tcmp_type cmp_type;\n@@ -209,7 +228,7 @@ static struct {\n \t{ \"color\", FIELD_STR, color_atom_parser },\n \t{ \"align\", FIELD_STR, align_atom_parser },\n \t{ \"end\" },\n-\t{ \"if\" },\n+\t{ \"if\", FIELD_STR, if_atom_parser },\n \t{ \"then\" },\n \t{ \"else\" },\n };\n@@ -410,6 +429,9 @@ static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_stat\n \tstruct ref_formatting_stack *new;\n \tstruct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);\n \n+\tif_then_else->if_equals = atomv->atom->u.if_then_else.if_equals;\n+\tif_then_else->not_equals = atomv->atom->u.if_then_else.not_equals;\n+\n \tpush_stack_element(&state->stack);\n \tnew = state->stack;\n \tnew->at_end = if_then_else_handler;\n@@ -441,10 +463,17 @@ static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_st\n \t\tdie(_(\"format: %%(then) atom used after %%(else)\"));\n \tif_then_else->then_atom_seen = 1;\n \t/*\n-\t * If there exists non-empty string between the 'if' and\n-\t * 'then' atom then the 'if' condition is satisfied.\n+\t * If the 'equals' or 'notequals' attribute is used then\n+\t * perform the required comparison. If not, only non-empty\n+\t * strings satisfy the 'if' condition.\n \t */\n-\tif (cur->output.len && !is_empty(cur->output.buf))\n+\tif (if_then_else->if_equals) {\n+\t\tif (!strcmp(if_then_else->if_equals, cur->output.buf))\n+\t\t\tif_then_else->condition_satisfied = 1;\n+\t} else \tif (if_then_else->not_equals) {\n+\t\tif (strcmp(if_then_else->not_equals, cur->output.buf))\n+\t\t\tif_then_else->condition_satisfied = 1;\n+\t} else if (cur->output.len && !is_empty(cur->output.buf))\n \t\tif_then_else->condition_satisfied = 1;\n \tstrbuf_reset(&cur->output);\n }\n@@ -1137,7 +1166,11 @@ static void populate_value(struct ref_array_item *ref)\n \t\t} else if (!strcmp(name, \"end\")) {\n \t\t\tv->handler = end_atom_handler;\n \t\t\tcontinue;\n-\t\t} else if (!strcmp(name, \"if\")) {\n+\t\t} else if (starts_with(name, \"if\")) {\n+\t\t\tconst char *s;\n+\n+\t\t\tif (skip_prefix(name, \"if:\", &s))\n+\t\t\t\tv->s = xstrdup(s);\n \t\t\tv->handler = if_atom_handler;\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"then\")) {\ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex 05935b3..206ad67 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -404,4 +404,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.8.0\n"},{"id":"284170","messageId":"1461351847-22903-4-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 03/16] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:03:54Z","receivedAt":"2016-04-22T19:03:54Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add support for %(objectname:short=<length>) which would print the\nabbreviated unique objectname of given length. When no length is\nspecified, the length is 'DEFAULT_ABBREV'. The minimum length is\n'MINIMUM_ABBREV'. The length may be exceeded to ensure that the provided\nobject name is unique.\n\nAdd tests and documentation for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nHelped-by: Jacob Keller <jacob.keller@gmail.com>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  4 ++++\n ref-filter.c                       | 25 +++++++++++++++++++------\n t/t6300-for-each-ref.sh            | 10 ++++++++++\n 3 files changed, 33 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 3e7db10..5370fe5 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -107,6 +107,10 @@ objectsize::\n objectname::\n \tThe object name (aka SHA-1).\n \tFor a non-ambiguous abbreviation of the object name append `:short`.\n+\tFor an abbreviation of the object name with desired length append\n+\t`:short=<length>`, where the minimum length is MINIMUM_ABBREV. The\n+\tlength may be exceeded to ensure unique object names.\n+\n \n upstream::\n \tThe name of a local ref which can be considered ``upstream''\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 857a8b5..7d3af1c 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -55,7 +55,10 @@ static struct used_atom {\n \t\t\tconst char *if_equals,\n \t\t\t\t*not_equals;\n \t\t} if_then_else;\n-\t\tenum { O_FULL, O_SHORT } objectname;\n+\t\tstruct {\n+\t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n+\t\t\tunsigned int length;\n+\t\t} objectname;\n \t} u;\n } *used_atom;\n static int used_atom_cnt, need_tagged, need_symref;\n@@ -118,10 +121,17 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)\n static void objectname_atom_parser(struct used_atom *atom, const char *arg)\n {\n \tif (!arg)\n-\t\tatom->u.objectname = O_FULL;\n+\t\tatom->u.objectname.option = O_FULL;\n \telse if (!strcmp(arg, \"short\"))\n-\t\tatom->u.objectname = O_SHORT;\n-\telse\n+\t\tatom->u.objectname.option = O_SHORT;\n+\telse if (skip_prefix(arg, \"short=\", &arg)) {\n+\t\tatom->u.objectname.option = O_LENGTH;\n+\t\tif (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n+\t\t    atom->u.objectname.length == 0)\n+\t\t\tdie(_(\"positive value expected objectname:short=%s\"), arg);\n+\t\tif (atom->u.objectname.length < MINIMUM_ABBREV)\n+\t\t\tatom->u.objectname.length = MINIMUM_ABBREV;\n+\t} else\n \t\tdie(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n }\n \n@@ -591,12 +601,15 @@ static int grab_objectname(const char *name, const unsigned char *sha1,\n \t\t\t   struct atom_value *v, struct used_atom *atom)\n {\n \tif (starts_with(name, \"objectname\")) {\n-\t\tif (atom->u.objectname == O_SHORT) {\n+\t\tif (atom->u.objectname.option == O_SHORT) {\n \t\t\tv->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));\n \t\t\treturn 1;\n-\t\t} else if (atom->u.objectname == O_FULL) {\n+\t\t} else if (atom->u.objectname.option == O_FULL) {\n \t\t\tv->s = xstrdup(sha1_to_hex(sha1));\n \t\t\treturn 1;\n+\t\t} else if (atom->u.objectname.option == O_LENGTH) {\n+\t\t\tv->s = xstrdup(find_unique_abbrev(sha1, atom->u.objectname.length));\n+\t\t\treturn 1;\n \t\t} else\n \t\t\tdie(\"BUG: unknown %%(objectname) option\");\n \t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 19a2823..2be0a3f 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -60,6 +60,8 @@ test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n test_atom head objectname:short $(git rev-parse --short refs/heads/master)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom head tree $(git rev-parse refs/heads/master^{tree})\n test_atom head parent ''\n test_atom head numparent 0\n@@ -99,6 +101,8 @@ test_atom tag objecttype tag\n test_atom tag objectsize 154\n test_atom tag objectname $(git rev-parse refs/tags/testtag)\n test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n test_atom tag tree ''\n test_atom tag parent ''\n test_atom tag numparent ''\n@@ -164,6 +168,12 @@ test_expect_success 'Check invalid format specifiers are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n '\n \n+test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n+\ttest_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n+'\n+\n test_date () {\n \tf=$1 &&\n \tcommitter_date=$2 &&\n-- \n2.8.0\n"},{"id":"284171","messageId":"1461351847-22903-5-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 04/16] ref-filter: move get_head_description() from branch.c","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:03:55Z","receivedAt":"2016-04-22T19:03:55Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Move 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 | 31 -------------------------------\n ref-filter.c     | 38 ++++++++++++++++++++++++++++++++++++--\n ref-filter.h     |  2 ++\n 3 files changed, 38 insertions(+), 33 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 0adba62..d467840 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -361,37 +361,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\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 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 7d3af1c..fcb3353 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -13,6 +13,7 @@\n #include \"utf8.h\"\n #include \"git-compat-util.h\"\n #include \"version.h\"\n+#include \"wt-status.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n@@ -1077,6 +1078,37 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t*s = refname;\n }\n \n+char *get_head_description(void)\n+{\n+\tstruct strbuf desc = STRBUF_INIT;\n+\tstruct wt_status_state state;\n+\tmemset(&state, 0, sizeof(state));\n+\twt_status_get_state(&state, 1);\n+\tif (state.rebase_in_progress ||\n+\t    state.rebase_interactive_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, rebasing %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.bisect_in_progress)\n+\t\tstrbuf_addf(&desc, _(\"(no branch, bisect started on %s)\"),\n+\t\t\t    state.branch);\n+\telse if (state.detached_from) {\n+\t\t/* TRANSLATORS: make sure these match _(\"HEAD detached at \")\n+\t\t   and _(\"HEAD detached from \") in wt-status.c */\n+\t\tif (state.detached_at)\n+\t\t\tstrbuf_addf(&desc, _(\"(HEAD detached at %s)\"),\n+\t\t\t\tstate.detached_from);\n+\t\telse\n+\t\t\tstrbuf_addf(&desc, _(\"(HEAD detached from %s)\"),\n+\t\t\t\tstate.detached_from);\n+\t}\n+\telse\n+\t\tstrbuf_addstr(&desc, _(\"(no branch)\"));\n+\tfree(state.branch);\n+\tfree(state.onto);\n+\tfree(state.detached_from);\n+\treturn strbuf_detach(&desc, NULL);\n+}\n+\n /*\n  * Parse the object referred by ref, and grab needed value.\n  */\n@@ -1116,9 +1148,11 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\tname++;\n \t\t}\n \n-\t\tif (starts_with(name, \"refname\"))\n+\t\tif (starts_with(name, \"refname\")) {\n \t\t\trefname = ref->refname;\n-\t\telse if (starts_with(name, \"symref\"))\n+\t\t\tif (ref->kind & FILTER_REFS_DETACHED_HEAD)\n+\t\t\t\trefname = get_head_description();\n+\t\t} else if (starts_with(name, \"symref\"))\n \t\t\trefname = ref->symref ? ref->symref : \"\";\n \t\telse if (starts_with(name, \"upstream\")) {\n \t\t\tconst char *branch_name;\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 14d435e..4aea594 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -106,5 +106,7 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);\n struct ref_sorting *ref_default_sorting(void);\n /*  Function to parse --merged and --no-merged options */\n int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);\n+/*  Get the current HEAD's description */\n+char *get_head_description(void);\n \n #endif /*  REF_FILTER_H  */\n-- \n2.8.0\n"},{"id":"284173","messageId":"1461351847-22903-6-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 05/16] ref-filter: introduce format_ref_array_item()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:03:56Z","receivedAt":"2016-04-22T19:03:56Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"To 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 fcb3353..da2b9ee 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1813,10 +1813,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@@ -1846,9 +1846,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.8.0\n"},{"id":"284174","messageId":"1461351847-22903-7-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 06/16] ref-filter: make %(upstream:track) prints \"[gone]\" for invalid upstreams","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:03:57Z","receivedAt":"2016-04-22T19:03:57Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Borrowing 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 5370fe5..f1e46a7 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -119,7 +119,8 @@ upstream::\n \t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n \tversion: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n \tor \"=\" (in sync).  Has no effect if the ref does not have\n-\ttracking information associated with it.\n+\ttracking information associated with it. `:track` also prints\n+\t\"[gone]\" whenever unknown upstream ref is encountered.\n \n push::\n \tThe name of a local ref which represents the `@{push}` location\ndiff --git a/ref-filter.c b/ref-filter.c\nindex da2b9ee..3bb6923 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1049,8 +1049,10 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t*s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n \telse if (atom->u.remote_ref == RR_TRACK) {\n \t\tif (stat_tracking_info(branch, &num_ours,\n-\t\t\t\t       &num_theirs, NULL))\n+\t\t\t\t       &num_theirs, NULL)) {\n+\t\t\t*s = \"[gone]\";\n \t\t\treturn;\n+\t\t}\n \n \t\tif (!num_ours && !num_theirs)\n \t\t\t*s = \"\";\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 2be0a3f..a92b36f 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -382,7 +382,7 @@ test_expect_success 'Check that :track[short] cannot be used with other atoms' '\n \n test_expect_success 'Check that :track[short] works when upstream is invalid' '\n \tcat >expected <<-\\EOF &&\n-\n+\t[gone]\n \n \tEOF\n \ttest_when_finished \"git config branch.master.merge refs/heads/master\" &&\n-- \n2.8.0\n"},{"id":"284178","messageId":"1461351847-22903-8-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 07/16] ref-filter: add support for %(upstream:track,nobracket)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:03:58Z","receivedAt":"2016-04-22T19:03:58Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add support for %(upstream:track,nobracket) which will print the\ntracking information without the brackets (i.e. \"ahead N, behind M\").\nThis is needed when we port branch.c to use ref-filter's printing APIs.\n\nAdd test and documentation for the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  8 +++--\n ref-filter.c                       | 67 +++++++++++++++++++++++++-------------\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 51 insertions(+), 26 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex f1e46a7..5c85f27 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -118,9 +118,11 @@ upstream::\n \t`refname` above.  Additionally respects `:track` to show\n \t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n \tversion: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n-\tor \"=\" (in sync).  Has no effect if the ref does not have\n-\ttracking information associated with it. `:track` also prints\n-\t\"[gone]\" whenever unknown upstream ref is encountered.\n+\tor \"=\" (in sync). `:track` also prints \"[gone]\" whenever\n+\tunknown upstream ref is encountered. Append `:track,nobracket`\n+\tto show tracking information without brackets (i.e \"ahead N,\n+\tbehind M\").  Has no effect if the ref does not have tracking\n+\tinformation associated with it.\n \n push::\n \tThe name of a local ref which represents the `@{push}` location\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 3bb6923..b898bcd 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -46,8 +46,10 @@ static struct used_atom {\n \tunion {\n \t\tchar color[COLOR_MAXLEN];\n \t\tstruct align align;\n-\t\tenum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }\n-\t\t\tremote_ref;\n+\t\tstruct {\n+\t\t\tenum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option;\n+\t\t\tunsigned int nobracket: 1;\n+\t\t} remote_ref;\n \t\tstruct {\n \t\t\tenum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;\n \t\t\tunsigned int nlines;\n@@ -75,16 +77,33 @@ static void color_atom_parser(struct used_atom *atom, const char *color_value)\n \n static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n {\n-\tif (!arg)\n-\t\tatom->u.remote_ref = RR_NORMAL;\n-\telse if (!strcmp(arg, \"short\"))\n-\t\tatom->u.remote_ref = RR_SHORTEN;\n-\telse if (!strcmp(arg, \"track\"))\n-\t\tatom->u.remote_ref = RR_TRACK;\n-\telse if (!strcmp(arg, \"trackshort\"))\n-\t\tatom->u.remote_ref = RR_TRACKSHORT;\n-\telse\n-\t\tdie(_(\"unrecognized format: %%(%s)\"), atom->name);\n+\tstruct string_list params = STRING_LIST_INIT_DUP;\n+\tint i;\n+\n+\tif (!arg) {\n+\t\tatom->u.remote_ref.option = RR_NORMAL;\n+\t\treturn;\n+\t}\n+\n+\tatom->u.remote_ref.nobracket = 0;\n+\tstring_list_split(&params, arg, ',', -1);\n+\n+\tfor (i = 0; i < params.nr; i++) {\n+\t\tconst char *s = params.items[i].string;\n+\n+\t\tif (!strcmp(s, \"short\"))\n+\t\t\tatom->u.remote_ref.option = RR_SHORTEN;\n+\t\telse if (!strcmp(s, \"track\"))\n+\t\t\tatom->u.remote_ref.option = RR_TRACK;\n+\t\telse if (!strcmp(s, \"trackshort\"))\n+\t\t\tatom->u.remote_ref.option = RR_TRACKSHORT;\n+\t\telse if (!strcmp(s, \"nobracket\"))\n+\t\t\tatom->u.remote_ref.nobracket = 1;\n+\t\telse\n+\t\t\tdie(_(\"unrecognized format: %%(%s)\"), atom->name);\n+\t}\n+\n+\tstring_list_clear(&params, 0);\n }\n \n static void body_atom_parser(struct used_atom *atom, const char *arg)\n@@ -1045,25 +1064,27 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t\t\t    struct branch *branch, const char **s)\n {\n \tint num_ours, num_theirs;\n-\tif (atom->u.remote_ref == RR_SHORTEN)\n+\tif (atom->u.remote_ref.option == RR_SHORTEN)\n \t\t*s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n-\telse if (atom->u.remote_ref == RR_TRACK) {\n+\telse if (atom->u.remote_ref.option == RR_TRACK) {\n \t\tif (stat_tracking_info(branch, &num_ours,\n \t\t\t\t       &num_theirs, NULL)) {\n-\t\t\t*s = \"[gone]\";\n-\t\t\treturn;\n-\t\t}\n-\n-\t\tif (!num_ours && !num_theirs)\n+\t\t\t*s = xstrdup(\"gone\");\n+\t\t} else if (!num_ours && !num_theirs)\n \t\t\t*s = \"\";\n \t\telse if (!num_ours)\n-\t\t\t*s = xstrfmt(\"[behind %d]\", num_theirs);\n+\t\t\t*s = xstrfmt(\"behind %d\", num_theirs);\n \t\telse if (!num_theirs)\n-\t\t\t*s = xstrfmt(\"[ahead %d]\", num_ours);\n+\t\t\t*s = xstrfmt(\"ahead %d\", num_ours);\n \t\telse\n-\t\t\t*s = xstrfmt(\"[ahead %d, behind %d]\",\n+\t\t\t*s = xstrfmt(\"ahead %d, behind %d\",\n \t\t\t\t     num_ours, num_theirs);\n-\t} else if (atom->u.remote_ref == RR_TRACKSHORT) {\n+\t\tif (!atom->u.remote_ref.nobracket && *s[0]) {\n+\t\t\tconst char *to_free = *s;\n+\t\t\t*s = xstrfmt(\"[%s]\", *s);\n+\t\t\tfree((void *)to_free);\n+\t\t}\n+\t} else if (atom->u.remote_ref.option == RR_TRACKSHORT) {\n \t\tif (stat_tracking_info(branch, &num_ours,\n \t\t\t\t       &num_theirs, NULL))\n \t\t\treturn;\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex a92b36f..2c5f177 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -372,6 +372,8 @@ test_expect_success 'setup for upstream:track[short]' '\n \n test_atom head upstream:track '[ahead 1]'\n test_atom head upstream:trackshort '>'\n+test_atom head upstream:track,nobracket 'ahead 1'\n+test_atom head upstream:nobracket,track 'ahead 1'\n test_atom head push:track '[ahead 1]'\n test_atom head push:trackshort '>'\n \n-- \n2.8.0\n"},{"id":"284176","messageId":"1461351847-22903-9-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 08/16] ref-filter: make \"%(symref)\" atom work with the ':short' modifier","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:03:59Z","receivedAt":"2016-04-22T19:03:59Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"The \"%(symref)\" atom doesn't work when used with the ':short' modifier\nbecause we strictly match only 'symref' for setting the 'need_symref'\nindicator. Fix this by using comparing with valid_atom rather than used_atom.\n\nAdd tests for %(symref) and %(symref:short) while we're here.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n ref-filter.c            |  2 +-\n t/t6300-for-each-ref.sh | 24 ++++++++++++++++++++++++\n 2 files changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex b898bcd..083cc27 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -338,7 +338,7 @@ int parse_ref_filter_atom(const char *atom, const char *ep)\n \t\tvalid_atom[i].parser(&used_atom[at], arg);\n \tif (*atom == '*')\n \t\tneed_tagged = 1;\n-\tif (!strcmp(used_atom[at].name, \"symref\"))\n+\tif (!strcmp(valid_atom[i].name, \"symref\"))\n \t\tneed_symref = 1;\n \treturn at;\n }\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 2c5f177..b06ea1c 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -38,6 +38,7 @@ test_atom() {\n \tcase \"$1\" in\n \t\thead) ref=refs/heads/master ;;\n \t\t tag) ref=refs/tags/testtag ;;\n+\t\t sym) ref=refs/heads/sym ;;\n \t\t   *) ref=$1 ;;\n \tesac\n \tprintf '%s\\n' \"$3\" >expected\n@@ -565,4 +566,27 @@ test_expect_success 'Verify sort with multiple keys' '\n \t\trefs/tags/bogo refs/tags/master > actual &&\n \ttest_cmp expected actual\n '\n+\n+test_expect_success 'Add symbolic ref for the following tests' '\n+\tgit symbolic-ref refs/heads/sym refs/heads/master\n+'\n+\n+cat >expected <<EOF\n+refs/heads/master\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref) atom' '\n+\tgit for-each-ref --format=\"%(symref)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+heads/master\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:short) atom' '\n+\tgit for-each-ref --format=\"%(symref:short)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.8.0\n"},{"id":"284177","messageId":"1461351847-22903-10-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 09/16] ref-filter: introduce refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:04:00Z","receivedAt":"2016-04-22T19:04:00Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Since 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 083cc27..778fe02 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -30,6 +30,11 @@ struct if_then_else {\n \t\tcondition_satisfied : 1;\n };\n \n+struct refname_atom {\n+\tenum { R_NORMAL, R_SHORT, R_STRIP } option;\n+\tunsigned int strip;\n+};\n+\n /*\n  * An atom is a valid field atom listed below, possibly prefixed with\n  * a \"*\" to denote deref_tag().\n@@ -62,6 +67,7 @@ static struct used_atom {\n \t\t\tenum { O_FULL, O_LENGTH, O_SHORT } option;\n \t\t\tunsigned int length;\n \t\t} objectname;\n+\t\tstruct refname_atom refname;\n \t} u;\n } *used_atom;\n static int used_atom_cnt, need_tagged, need_symref;\n@@ -75,6 +81,21 @@ static void color_atom_parser(struct used_atom *atom, const char *color_value)\n \t\tdie(_(\"unrecognized color: %%(color:%s)\"), color_value);\n }\n \n+static void refname_atom_parser_internal(struct refname_atom *atom,\n+\t\t\t\t\t const char *arg, const char *name)\n+{\n+\tif (!arg)\n+\t\tatom->option = R_NORMAL;\n+\telse if (!strcmp(arg, \"short\"))\n+\t\tatom->option = R_SHORT;\n+\telse if (skip_prefix(arg, \"strip=\", &arg)) {\n+\t\tatom->option = R_STRIP;\n+\t\tif (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0)\n+\t\t\tdie(_(\"positive value expected refname:strip=%s\"), arg);\n+\t} \telse\n+\t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n+}\n+\n static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n {\n \tstruct string_list params = STRING_LIST_INIT_DUP;\n-- \n2.8.0\n"},{"id":"284179","messageId":"1461351847-22903-11-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 10/16] ref-filter: introduce symref_atom_parser() and refname_atom_parser()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:04:01Z","receivedAt":"2016-04-22T19:04:01Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Using refname_atom_parser_internal(), introduce symref_atom_parser() and\nrefname_atom_parser() which will parse the atoms %(symref) and\n%(refname) respectively. Store the parsed information into the\n'used_atom' structure based on the modifiers used along with the atoms.\n\nNow the '%(symref)' atom supports the ':strip' atom modifier. Update the\nDocumentation and tests to reflect this.\n\nHelped-by: Jeff King <peff@peff.net>\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  5 +++\n ref-filter.c                       | 78 ++++++++++++++++++++++----------------\n t/t6300-for-each-ref.sh            |  9 +++++\n 3 files changed, 59 insertions(+), 33 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 5c85f27..e0c0a12 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -163,6 +163,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 778fe02..933f8ca 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -176,6 +176,16 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg)\n \t\tdie(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n }\n \n+static void symref_atom_parser(struct used_atom *atom, const char *arg)\n+{\n+\treturn refname_atom_parser_internal(&atom->u.refname, arg, atom->name);\n+}\n+\n+static void refname_atom_parser(struct used_atom *atom, const char *arg)\n+{\n+\treturn refname_atom_parser_internal(&atom->u.refname, arg, atom->name);\n+}\n+\n static align_type parse_align_position(const char *s)\n {\n \tif (!strcmp(s, \"right\"))\n@@ -244,7 +254,7 @@ static struct {\n \tcmp_type cmp_type;\n \tvoid (*parser)(struct used_atom *atom, const char *arg);\n } valid_atom[] = {\n-\t{ \"refname\" },\n+\t{ \"refname\" , FIELD_STR, refname_atom_parser },\n \t{ \"objecttype\" },\n \t{ \"objectsize\", FIELD_ULONG },\n \t{ \"objectname\", FIELD_STR, objectname_atom_parser },\n@@ -273,7 +283,7 @@ static struct {\n \t{ \"contents\", FIELD_STR, contents_atom_parser },\n \t{ \"upstream\", FIELD_STR, remote_ref_atom_parser },\n \t{ \"push\", FIELD_STR, remote_ref_atom_parser },\n-\t{ \"symref\" },\n+\t{ \"symref\", FIELD_STR, symref_atom_parser },\n \t{ \"flag\" },\n \t{ \"HEAD\" },\n \t{ \"color\", FIELD_STR, color_atom_parser },\n@@ -1058,21 +1068,16 @@ static inline char *copy_advance(char *dst, const char *src)\n \treturn dst;\n }\n \n-static const char *strip_ref_components(const char *refname, const char *nr_arg)\n+static const char *strip_ref_components(const char *refname, unsigned int len)\n {\n-\tchar *end;\n-\tlong nr = strtol(nr_arg, &end, 10);\n-\tlong remaining = nr;\n+\tlong remaining = len;\n \tconst char *start = refname;\n \n-\tif (nr < 1 || *end != '\\0')\n-\t\tdie(_(\":strip= requires a positive integer argument\"));\n-\n \twhile (remaining) {\n \t\tswitch (*start++) {\n \t\tcase '\\0':\n-\t\t\tdie(_(\"ref '%s' does not have %ld components to :strip\"),\n-\t\t\t    refname, nr);\n+\t\t\tdie(_(\"ref '%s' does not have %ud components to :strip\"),\n+\t\t\t    refname, len);\n \t\tcase '/':\n \t\t\tremaining--;\n \t\t\tbreak;\n@@ -1081,6 +1086,16 @@ static const char *strip_ref_components(const char *refname, const char *nr_arg)\n \treturn start;\n }\n \n+static const char *show_ref(struct refname_atom *atom, const char *refname)\n+{\n+\tif (atom->option == R_SHORT)\n+\t\treturn shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n+\telse if (atom->option == R_STRIP)\n+\t\treturn strip_ref_components(refname, atom->strip);\n+\telse\n+\t\treturn refname;\n+}\n+\n static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t\t\t    struct branch *branch, const char **s)\n {\n@@ -1153,6 +1168,21 @@ char *get_head_description(void)\n \treturn strbuf_detach(&desc, NULL);\n }\n \n+static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref)\n+{\n+\tif (!ref->symref)\n+\t\treturn \"\";\n+\telse\n+\t\treturn show_ref(&atom->u.refname, ref->symref);\n+}\n+\n+static const char *get_refname(struct used_atom *atom, struct ref_array_item *ref)\n+{\n+\tif (ref->kind & FILTER_REFS_DETACHED_HEAD)\n+\t\treturn get_head_description();\n+\treturn show_ref(&atom->u.refname, ref->refname);\n+}\n+\n /*\n  * Parse the object referred by ref, and grab needed value.\n  */\n@@ -1181,7 +1211,6 @@ static void populate_value(struct ref_array_item *ref)\n \t\tstruct atom_value *v = &ref->value[i];\n \t\tint deref = 0;\n \t\tconst char *refname;\n-\t\tconst char *formatp;\n \t\tstruct branch *branch = NULL;\n \n \t\tv->handler = append_atom;\n@@ -1192,12 +1221,10 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\tname++;\n \t\t}\n \n-\t\tif (starts_with(name, \"refname\")) {\n-\t\t\trefname = ref->refname;\n-\t\t\tif (ref->kind & FILTER_REFS_DETACHED_HEAD)\n-\t\t\t\trefname = get_head_description();\n-\t\t} else if (starts_with(name, \"symref\"))\n-\t\t\trefname = ref->symref ? ref->symref : \"\";\n+\t\tif (starts_with(name, \"refname\"))\n+\t\t\trefname = get_refname(atom, ref);\n+\t\telse if (starts_with(name, \"symref\"))\n+\t\t\trefname = get_symref(atom, ref);\n \t\telse if (starts_with(name, \"upstream\")) {\n \t\t\tconst char *branch_name;\n \t\t\t/* only local branches may have an upstream */\n@@ -1273,21 +1300,6 @@ static void populate_value(struct ref_array_item *ref)\n \t\t} else\n \t\t\tcontinue;\n \n-\t\tformatp = strchr(name, ':');\n-\t\tif (formatp) {\n-\t\t\tconst char *arg;\n-\n-\t\t\tformatp++;\n-\t\t\tif (!strcmp(formatp, \"short\"))\n-\t\t\t\trefname = shorten_unambiguous_ref(refname,\n-\t\t\t\t\t\t      warn_ambiguous_refs);\n-\t\t\telse if (skip_prefix(formatp, \"strip=\", &arg))\n-\t\t\t\trefname = strip_ref_components(refname, arg);\n-\t\t\telse\n-\t\t\t\tdie(_(\"unknown %.*s format %s\"),\n-\t\t\t\t    (int)(formatp - name), name, formatp);\n-\t\t}\n-\n \t\tif (!deref)\n \t\t\tv->s = refname;\n \t\telse\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex b06ea1c..3d28234 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -589,4 +589,13 @@ test_expect_success 'Verify usage of %(symref:short) atom' '\n \ttest_cmp expected actual\n '\n \n+cat >expected <<EOF\n+master\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:strip) atom' '\n+\tgit for-each-ref --format=\"%(symref:strip=2)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.8.0\n"},{"id":"284180","messageId":"1461351847-22903-12-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 11/16] ref-filter: make remote_ref_atom_parser() use refname_atom_parser_internal()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:04:02Z","receivedAt":"2016-04-22T19:04:02Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Use the recently introduced refname_atom_parser_internal() within\nremote_ref_atom_parser(), this provides a common base for all the ref\nprinting atoms, allowing %(upstream) and %(push) to also use the\n':strip' option.\n\nThe atoms '%(push)' and '%(upstream)' will retain the ':track' and\n':trackshort' atom modifiers to themselves as they have no meaning in\ncontext to the '%(refname)' and '%(symref)' atoms.\n\nUpdate the documentation and tests to reflect the same.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 27 ++++++++++++++-------------\n ref-filter.c                       | 26 +++++++++++++++-----------\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 31 insertions(+), 24 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex e0c0a12..3eaf770 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -114,21 +114,22 @@ objectname::\n \n upstream::\n \tThe name of a local ref which can be considered ``upstream''\n-\tfrom the displayed ref. Respects `:short` in the same way as\n-\t`refname` above.  Additionally respects `:track` to show\n-\t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n-\tversion: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n-\tor \"=\" (in sync). `:track` also prints \"[gone]\" whenever\n-\tunknown upstream ref is encountered. Append `:track,nobracket`\n-\tto show tracking information without brackets (i.e \"ahead N,\n-\tbehind M\").  Has no effect if the ref does not have tracking\n-\tinformation associated with it.\n+\tfrom the displayed ref. Respects `:short` and `:strip` in the\n+\tsame way as `refname` above.  Additionally respects `:track`\n+\tto show \"[ahead N, behind M]\" and `:trackshort` to show the\n+\tterse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n+\tbehind), or \"=\" (in sync). `:track` also prints \"[gone]\"\n+\twhenever unknown upstream ref is encountered. Append\n+\t`:track,nobracket` to show tracking information without\n+\tbrackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n+\tdoes not have tracking information associated with it.\n \n push::\n-\tThe name of a local ref which represents the `@{push}` location\n-\tfor the displayed ref. Respects `:short`, `:track`, and\n-\t`:trackshort` options as `upstream` does. Produces an empty\n-\tstring if no `@{push}` ref is configured.\n+\tThe name of a local ref which represents the `@{push}`\n+\tlocation for the displayed ref. Respects `:short`, `:strip`,\n+\t`:track`, and `:trackshort` options as `upstream`\n+\tdoes. Produces an empty string if no `@{push}` ref is\n+\tconfigured.\n \n HEAD::\n \t'*' if HEAD matches current ref (the checked out branch), ' '\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 933f8ca..3b42aab 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -52,7 +52,8 @@ static struct used_atom {\n \t\tchar color[COLOR_MAXLEN];\n \t\tstruct align align;\n \t\tstruct {\n-\t\t\tenum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option;\n+\t\t\tenum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;\n+\t\t\tstruct refname_atom refname;\n \t\t\tunsigned int nobracket: 1;\n \t\t} remote_ref;\n \t\tstruct {\n@@ -102,7 +103,9 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n \tint i;\n \n \tif (!arg) {\n-\t\tatom->u.remote_ref.option = RR_NORMAL;\n+\t\tatom->u.remote_ref.option = RR_REF;\n+\t\trefname_atom_parser_internal(&atom->u.remote_ref.refname,\n+\t\t\t\t\t     arg, atom->name);\n \t\treturn;\n \t}\n \n@@ -112,16 +115,17 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)\n \tfor (i = 0; i < params.nr; i++) {\n \t\tconst char *s = params.items[i].string;\n \n-\t\tif (!strcmp(s, \"short\"))\n-\t\t\tatom->u.remote_ref.option = RR_SHORTEN;\n-\t\telse if (!strcmp(s, \"track\"))\n+\t\tif (!strcmp(s, \"track\"))\n \t\t\tatom->u.remote_ref.option = RR_TRACK;\n \t\telse if (!strcmp(s, \"trackshort\"))\n \t\t\tatom->u.remote_ref.option = RR_TRACKSHORT;\n \t\telse if (!strcmp(s, \"nobracket\"))\n \t\t\tatom->u.remote_ref.nobracket = 1;\n-\t\telse\n-\t\t\tdie(_(\"unrecognized format: %%(%s)\"), atom->name);\n+\t\telse {\n+\t\t\tatom->u.remote_ref.option = RR_REF;\n+\t\t\trefname_atom_parser_internal(&atom->u.remote_ref.refname,\n+\t\t\t\t\t\t     arg, atom->name);\n+\t\t}\n \t}\n \n \tstring_list_clear(&params, 0);\n@@ -1100,8 +1104,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t\t\t    struct branch *branch, const char **s)\n {\n \tint num_ours, num_theirs;\n-\tif (atom->u.remote_ref.option == RR_SHORTEN)\n-\t\t*s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n+\tif (atom->u.remote_ref.option == RR_REF)\n+\t\t*s = show_ref(&atom->u.remote_ref.refname, refname);\n \telse if (atom->u.remote_ref.option == RR_TRACK) {\n \t\tif (stat_tracking_info(branch, &num_ours,\n \t\t\t\t       &num_theirs, NULL)) {\n@@ -1133,8 +1137,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t\t*s = \">\";\n \t\telse\n \t\t\t*s = \"<>\";\n-\t} else /* RR_NORMAL */\n-\t\t*s = refname;\n+\t} else\n+\t\tdie(\"BUG: unhandled RR_* enum\");\n }\n \n char *get_head_description(void)\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 3d28234..7ca0a12 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -55,8 +55,10 @@ test_atom head refname:strip=1 heads/master\n test_atom head refname:strip=2 master\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n+test_atom head upstream:strip=2 origin/master\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n+test_atom head push:strip=1 remotes/myfork/master\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n-- \n2.8.0\n"},{"id":"284181","messageId":"1461351847-22903-13-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 12/16] ref-filter: add `:dir` and `:base` options for ref printing atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:04:03Z","receivedAt":"2016-04-22T19:04:03Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add the options `:dir` and `:base` to all ref printing ('%(refname)',\n'%(symref)', '%(push)' and '%(upstream)') atoms. The `:dir` option gives\nthe directory (the part after $GIT_DIR/) of the ref without the\nrefname. The `:base` option gives the base directory of the given\nref (i.e. the directory following $GIT_DIR/refs/).\n\nAdd tests and documentation for the same.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt | 34 +++++++++++++++++++---------------\n ref-filter.c                       | 29 +++++++++++++++++++++++++----\n t/t6300-for-each-ref.sh            | 24 ++++++++++++++++++++++++\n 3 files changed, 68 insertions(+), 19 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 3eaf770..4a5e9ea 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -96,7 +96,9 @@ refname::\n \tslash-separated path components from the front of the refname\n \t(e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.\n \t`<N>` must be a positive integer.  If a displayed ref has fewer\n-\tcomponents than `<N>`, the command aborts with an error.\n+\tcomponents than `<N>`, the command aborts with an error. For the base\n+\tdirectory of the ref (i.e. foo in refs/foo/bar/boz) append\n+\t`:base`. For the entire directory path append `:dir`.\n \n objecttype::\n \tThe type of the object (`blob`, `tree`, `commit`, `tag`).\n@@ -114,22 +116,23 @@ objectname::\n \n upstream::\n \tThe name of a local ref which can be considered ``upstream''\n-\tfrom the displayed ref. Respects `:short` and `:strip` in the\n-\tsame way as `refname` above.  Additionally respects `:track`\n-\tto show \"[ahead N, behind M]\" and `:trackshort` to show the\n-\tterse version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and\n-\tbehind), or \"=\" (in sync). `:track` also prints \"[gone]\"\n-\twhenever unknown upstream ref is encountered. Append\n-\t`:track,nobracket` to show tracking information without\n-\tbrackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n-\tdoes not have tracking information associated with it.\n+\tfrom the displayed ref. Respects `:short`, `:strip`, `:base`\n+\tand `:dir` in the same way as `refname` above.  Additionally\n+\trespects `:track` to show \"[ahead N, behind M]\" and\n+\t`:trackshort` to show the terse version: \">\" (ahead), \"<\"\n+\t(behind), \"<>\" (ahead and behind), or \"=\" (in sync). `:track`\n+\talso prints \"[gone]\" whenever unknown upstream ref is\n+\tencountered. Append `:track,nobracket` to show tracking\n+\tinformation without brackets (i.e \"ahead N, behind M\").  Has\n+\tno effect if the ref does not have tracking information\n+\tassociated with it.\n \n push::\n \tThe name of a local ref which represents the `@{push}`\n \tlocation for the displayed ref. Respects `:short`, `:strip`,\n-\t`:track`, and `:trackshort` options as `upstream`\n-\tdoes. Produces an empty string if no `@{push}` ref is\n-\tconfigured.\n+\t`:track`, `:trackshort`, `:base` and `:dir` options as\n+\t`upstream` does. Produces an empty string if no `@{push}` ref\n+\tis configured.\n \n HEAD::\n \t'*' if HEAD matches current ref (the checked out branch), ' '\n@@ -166,8 +169,9 @@ if::\n \n symref::\n \tThe ref which the given symbolic ref refers to. If not a\n-\tsymbolic ref, nothing is printed. Respects the `:short` and\n-\t`:strip` options in the same way as `refname` above.\n+\tsymbolic ref, nothing is printed. Respects the `:short`,\n+\t`:strip`, `:base` and `:dir` options in the same way as\n+\t`refname` above.\n \n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 3b42aab..97977a5 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -31,7 +31,7 @@ struct if_then_else {\n };\n \n struct refname_atom {\n-\tenum { R_NORMAL, R_SHORT, R_STRIP } option;\n+\tenum { R_BASE, R_DIR, R_NORMAL, R_SHORT, R_STRIP } option;\n \tunsigned int strip;\n };\n \n@@ -93,7 +93,11 @@ static void refname_atom_parser_internal(struct refname_atom *atom,\n \t\tatom->option = R_STRIP;\n \t\tif (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0)\n \t\t\tdie(_(\"positive value expected refname:strip=%s\"), arg);\n-\t} \telse\n+\t} else if (!strcmp(arg, \"dir\"))\n+\t\tatom->option = R_DIR;\n+\telse if (!strcmp(arg, \"base\"))\n+\t\tatom->option = R_BASE;\n+\telse\n \t\tdie(_(\"unrecognized %%(%s) argument: %s\"), name, arg);\n }\n \n@@ -252,7 +256,6 @@ static void if_atom_parser(struct used_atom *atom, const char *arg)\n \t\tdie(_(\"unrecognized %%(if) argument: %s\"), arg);\n }\n \n-\n static struct {\n \tconst char *name;\n \tcmp_type cmp_type;\n@@ -1096,7 +1099,25 @@ static const char *show_ref(struct refname_atom *atom, const char *refname)\n \t\treturn shorten_unambiguous_ref(refname, warn_ambiguous_refs);\n \telse if (atom->option == R_STRIP)\n \t\treturn strip_ref_components(refname, atom->strip);\n-\telse\n+\telse if (atom->option == R_BASE) {\n+\t\tconst char *sp, *ep;\n+\n+\t\tif (skip_prefix(refname, \"refs/\", &sp)) {\n+\t\t\tep = strchr(sp, '/');\n+\t\t\tif (!ep)\n+\t\t\t\treturn \"\";\n+\t\t\treturn xstrndup(sp, ep - sp);\n+\t\t}\n+\t\treturn \"\";\n+\t} else if (atom->option == R_DIR) {\n+\t\tconst char *sp, *ep;\n+\n+\t\tsp = refname;\n+\t\tep = strrchr(sp, '/');\n+\t\tif (!ep)\n+\t\t\treturn \"\";\n+\t\treturn xstrndup(sp, ep - sp);\n+\t} else\n \t\treturn refname;\n }\n \ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 7ca0a12..8ff6568 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -53,12 +53,18 @@ test_atom head refname refs/heads/master\n test_atom head refname:short master\n test_atom head refname:strip=1 heads/master\n test_atom head refname:strip=2 master\n+test_atom head refname:dir refs/heads\n+test_atom head refname:base heads\n test_atom head upstream refs/remotes/origin/master\n test_atom head upstream:short origin/master\n test_atom head upstream:strip=2 origin/master\n+test_atom head upstream:dir refs/remotes/origin\n+test_atom head upstream:base remotes\n test_atom head push refs/remotes/myfork/master\n test_atom head push:short myfork/master\n test_atom head push:strip=1 remotes/myfork/master\n+test_atom head push:dir refs/remotes/myfork\n+test_atom head push:base remotes\n test_atom head objecttype commit\n test_atom head objectsize 171\n test_atom head objectname $(git rev-parse refs/heads/master)\n@@ -600,4 +606,22 @@ test_expect_success 'Verify usage of %(symref:strip) atom' '\n \ttest_cmp expected actual\n '\n \n+cat >expected <<EOF\n+refs/heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:dir) atom' '\n+\tgit for-each-ref --format=\"%(symref:dir)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+heads\n+EOF\n+\n+test_expect_success 'Verify usage of %(symref:base) atom' '\n+\tgit for-each-ref --format=\"%(symref:base)\" refs/heads/sym > actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.8.0\n"},{"id":"284182","messageId":"1461351847-22903-14-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 13/16] ref-filter: allow porcelain to translate messages in the output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:04:04Z","receivedAt":"2016-04-22T19:04:04Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce setup_ref_filter_porcelain_msg() so that the messages used in\nthe atom %(upstream:track) can be translated if needed. This is needed\nas we port branch.c to use ref-filter's printing API's.\n\nWritten-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n ref-filter.c | 28 ++++++++++++++++++++++++----\n ref-filter.h |  2 ++\n 2 files changed, 26 insertions(+), 4 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 97977a5..74c4869 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -15,6 +15,26 @@\n #include \"version.h\"\n #include \"wt-status.h\"\n \n+static struct ref_msg {\n+\tconst char *gone;\n+\tconst char *ahead;\n+\tconst char *behind;\n+\tconst char *ahead_behind;\n+} msgs = {\n+\t\"gone\",\n+\t\"ahead %d\",\n+\t\"behind %d\",\n+\t\"ahead %d, behind %d\"\n+};\n+\n+void setup_ref_filter_porcelain_msg(void)\n+{\n+\tmsgs.gone = _(\"gone\");\n+\tmsgs.ahead = _(\"ahead %d\");\n+\tmsgs.behind = _(\"behind %d\");\n+\tmsgs.ahead_behind = _(\"ahead %d, behind %d\");\n+}\n+\n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n struct align {\n@@ -1130,15 +1150,15 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \telse if (atom->u.remote_ref.option == RR_TRACK) {\n \t\tif (stat_tracking_info(branch, &num_ours,\n \t\t\t\t       &num_theirs, NULL)) {\n-\t\t\t*s = xstrdup(\"gone\");\n+\t\t\t*s = xstrdup(msgs.gone);\n \t\t} else if (!num_ours && !num_theirs)\n \t\t\t*s = \"\";\n \t\telse if (!num_ours)\n-\t\t\t*s = xstrfmt(\"behind %d\", num_theirs);\n+\t\t\t*s = xstrfmt(msgs.behind, num_theirs);\n \t\telse if (!num_theirs)\n-\t\t\t*s = xstrfmt(\"ahead %d\", num_ours);\n+\t\t\t*s = xstrfmt(msgs.ahead, num_ours);\n \t\telse\n-\t\t\t*s = xstrfmt(\"ahead %d, behind %d\",\n+\t\t\t*s = xstrfmt(msgs.ahead_behind,\n \t\t\t\t     num_ours, num_theirs);\n \t\tif (!atom->u.remote_ref.nobracket && *s[0]) {\n \t\t\tconst char *to_free = *s;\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 0014b92..da17145 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -111,5 +111,7 @@ struct ref_sorting *ref_default_sorting(void);\n int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);\n /*  Get the current HEAD's description */\n char *get_head_description(void);\n+/*  Set up translated strings in the output. */\n+void setup_ref_filter_porcelain_msg(void);\n \n #endif /*  REF_FILTER_H  */\n-- \n2.8.0\n"},{"id":"284183","messageId":"1461351847-22903-15-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 14/16] branch, tag: use porcelain output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:04:05Z","receivedAt":"2016-04-22T19:04:05Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Call 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 d467840..ecd7ffc 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -632,6 +632,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 528a1ba..3b51be1 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -379,6 +379,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.8.0\n"},{"id":"284185","messageId":"1461351847-22903-16-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 15/16] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:04:06Z","receivedAt":"2016-04-22T19:04:06Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Port branch.c to use ref-filter APIs for printing. This clears out\nmost of the code used in branch.c for printing and replaces them with\ncalls made to the ref-filter library.\n\nIntroduce build_format() which gets the format required for printing\nof refs. Make amendments to print_ref_list() to reflect these changes.\n\nChange calc_maxwidth() to also account for the length of HEAD ref, by\ncalling ref-filter:get_head_discription().\n\nAlso change the test in t6040 to reflect the changes.\n\nBefore this patch, all cross-prefix symrefs weren't shortened. Since\nwe're using ref-filter APIs, we shorten all symrefs by default. We also\nallow the user to change the format if needed with the introduction of\nthe '--format' option in the next patch.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nHelped-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/branch.c         | 234 ++++++++++++++---------------------------------\n t/t3203-branch-output.sh |   2 +-\n t/t6040-tracking-info.sh |   2 +-\n 3 files changed, 70 insertions(+), 168 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex ecd7ffc..07068d2 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@@ -277,162 +277,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,\n \treturn(ret);\n }\n \n-static void fill_tracking_info(struct strbuf *stat, const char *branch_name,\n-\t\tint show_upstream_ref)\n-{\n-\tint ours, theirs;\n-\tchar *ref = NULL;\n-\tstruct branch *branch = branch_get(branch_name);\n-\tconst char *upstream;\n-\tstruct strbuf fancy = STRBUF_INIT;\n-\tint upstream_is_gone = 0;\n-\tint added_decoration = 1;\n-\n-\tif (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {\n-\t\tif (!upstream)\n-\t\t\treturn;\n-\t\tupstream_is_gone = 1;\n-\t}\n-\n-\tif (show_upstream_ref) {\n-\t\tref = shorten_unambiguous_ref(upstream, 0);\n-\t\tif (want_color(branch_use_color))\n-\t\t\tstrbuf_addf(&fancy, \"%s%s%s\",\n-\t\t\t\t\tbranch_get_color(BRANCH_COLOR_UPSTREAM),\n-\t\t\t\t\tref, branch_get_color(BRANCH_COLOR_RESET));\n-\t\telse\n-\t\t\tstrbuf_addstr(&fancy, ref);\n-\t}\n-\n-\tif (upstream_is_gone) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: gone]\"), fancy.buf);\n-\t\telse\n-\t\t\tadded_decoration = 0;\n-\t} else if (!ours && !theirs) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s]\"), fancy.buf);\n-\t\telse\n-\t\t\tadded_decoration = 0;\n-\t} else if (!ours) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: behind %d]\"), fancy.buf, theirs);\n-\t\telse\n-\t\t\tstrbuf_addf(stat, _(\"[behind %d]\"), theirs);\n-\n-\t} else if (!theirs) {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: ahead %d]\"), fancy.buf, ours);\n-\t\telse\n-\t\t\tstrbuf_addf(stat, _(\"[ahead %d]\"), ours);\n-\t} else {\n-\t\tif (show_upstream_ref)\n-\t\t\tstrbuf_addf(stat, _(\"[%s: ahead %d, behind %d]\"),\n-\t\t\t\t    fancy.buf, ours, theirs);\n-\t\telse\n-\t\t\tstrbuf_addf(stat, _(\"[ahead %d, behind %d]\"),\n-\t\t\t\t    ours, theirs);\n-\t}\n-\tstrbuf_release(&fancy);\n-\tif (added_decoration)\n-\t\tstrbuf_addch(stat, ' ');\n-\tfree(ref);\n-}\n-\n-static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,\n-\t\t\t     struct ref_filter *filter, const char *refname)\n-{\n-\tstruct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;\n-\tconst char *sub = _(\" **** invalid ref ****\");\n-\tstruct commit *commit = item->commit;\n-\n-\tif (!parse_commit(commit)) {\n-\t\tpp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);\n-\t\tsub = subject.buf;\n-\t}\n-\n-\tif (item->kind == FILTER_REFS_BRANCHES)\n-\t\tfill_tracking_info(&stat, refname, filter->verbose > 1);\n-\n-\tstrbuf_addf(out, \" %s %s%s\",\n-\t\tfind_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),\n-\t\tstat.buf, sub);\n-\tstrbuf_release(&stat);\n-\tstrbuf_release(&subject);\n-}\n-\n-static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,\n-\t\t\t\t      struct ref_filter *filter, const char *remote_prefix)\n-{\n-\tchar c;\n-\tint current = 0;\n-\tint color;\n-\tstruct strbuf out = STRBUF_INIT, name = STRBUF_INIT;\n-\tconst char *prefix_to_show = \"\";\n-\tconst char *prefix_to_skip = NULL;\n-\tconst char *desc = item->refname;\n-\tchar *to_free = NULL;\n-\n-\tswitch (item->kind) {\n-\tcase FILTER_REFS_BRANCHES:\n-\t\tprefix_to_skip = \"refs/heads/\";\n-\t\tskip_prefix(desc, prefix_to_skip, &desc);\n-\t\tif (!filter->detached && !strcmp(desc, head))\n-\t\t\tcurrent = 1;\n-\t\telse\n-\t\t\tcolor = BRANCH_COLOR_LOCAL;\n-\t\tbreak;\n-\tcase FILTER_REFS_REMOTES:\n-\t\tprefix_to_skip = \"refs/remotes/\";\n-\t\tskip_prefix(desc, prefix_to_skip, &desc);\n-\t\tcolor = BRANCH_COLOR_REMOTE;\n-\t\tprefix_to_show = remote_prefix;\n-\t\tbreak;\n-\tcase FILTER_REFS_DETACHED_HEAD:\n-\t\tdesc = to_free = get_head_description();\n-\t\tcurrent = 1;\n-\t\tbreak;\n-\tdefault:\n-\t\tcolor = BRANCH_COLOR_PLAIN;\n-\t\tbreak;\n-\t}\n-\n-\tc = ' ';\n-\tif (current) {\n-\t\tc = '*';\n-\t\tcolor = BRANCH_COLOR_CURRENT;\n-\t}\n-\n-\tstrbuf_addf(&name, \"%s%s\", prefix_to_show, desc);\n-\tif (filter->verbose) {\n-\t\tint utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);\n-\t\tstrbuf_addf(&out, \"%c %s%-*s%s\", c, branch_get_color(color),\n-\t\t\t    maxwidth + utf8_compensation, name.buf,\n-\t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n-\t} else\n-\t\tstrbuf_addf(&out, \"%c %s%s%s\", c, branch_get_color(color),\n-\t\t\t    name.buf, branch_get_color(BRANCH_COLOR_RESET));\n-\n-\tif (item->symref) {\n-\t\tconst char *symref = item->symref;\n-\t\tif (prefix_to_skip)\n-\t\t\tskip_prefix(symref, prefix_to_skip, &symref);\n-\t\tstrbuf_addf(&out, \" -> %s\", symref);\n-\t}\n-\telse if (filter->verbose)\n-\t\t/* \" f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h\" */\n-\t\tadd_verbose_info(&out, item, filter, desc);\n-\tif (column_active(colopts)) {\n-\t\tassert(!filter->verbose && \"--column and --verbose are incompatible\");\n-\t\tstring_list_append(&output, out.buf);\n-\t} else {\n-\t\tprintf(\"%s\\n\", out.buf);\n-\t}\n-\tstrbuf_release(&name);\n-\tstrbuf_release(&out);\n-\tfree(to_free);\n-}\n-\n static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n {\n \tint i, max = 0;\n@@ -443,7 +287,12 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n \n \t\tskip_prefix(it->refname, \"refs/heads/\", &desc);\n \t\tskip_prefix(it->refname, \"refs/remotes/\", &desc);\n-\t\tw = utf8_strwidth(desc);\n+\t\tif (it->kind == FILTER_REFS_DETACHED_HEAD) {\n+\t\t\tchar *head_desc = get_head_description();\n+\t\t\tw = strlen(head_desc);\n+\t\t\tfree(head_desc);\n+\t\t} else\n+\t\t\tw = utf8_strwidth(desc);\n \n \t\tif (it->kind == FILTER_REFS_REMOTES)\n \t\t\tw += remote_bonus;\n@@ -453,12 +302,52 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)\n \treturn max;\n }\n \n+static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)\n+{\n+\tstruct strbuf fmt = STRBUF_INIT;\n+\tstruct strbuf local = STRBUF_INIT;\n+\tstruct strbuf remote = STRBUF_INIT;\n+\n+\tstrbuf_addf(&fmt, \"%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)\", branch_get_color(BRANCH_COLOR_CURRENT));\n+\n+\tif (filter->verbose) {\n+\t\tstrbuf_addf(&local, \"%%(align:%d,left)%%(refname:strip=2)%%(end)\", maxwidth);\n+\t\tstrbuf_addf(&local, \"%s\", branch_get_color(BRANCH_COLOR_RESET));\n+\t\tstrbuf_addf(&local, \" %%(objectname:short=7) \");\n+\n+\t\tif (filter->verbose > 1)\n+\t\t\tstrbuf_addf(&local, \"%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)\"\n+\t\t\t\t    \"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)\",\n+\t\t\t\t    branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));\n+\t\telse\n+\t\t\tstrbuf_addf(&local, \"%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)\");\n+\n+\t\tstrbuf_addf(&remote, \"%s%%(align:%d,left)%s%%(refname:strip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)\"\n+\t\t\t    \"%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), maxwidth,\n+\t\t\t    remote_prefix, branch_get_color(BRANCH_COLOR_RESET));\n+\t} else {\n+\t\tstrbuf_addf(&local, \"%%(refname:strip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_RESET));\n+\t\tstrbuf_addf(&remote, \"%s%s%%(refname:strip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)\",\n+\t\t\t    branch_get_color(BRANCH_COLOR_REMOTE), remote_prefix, branch_get_color(BRANCH_COLOR_RESET));\n+\t}\n+\n+\tstrbuf_addf(&fmt, \"%%(if:notequals=remotes)%%(refname:base)%%(then)%s%%(else)%s%%(end)\", local.buf, remote.buf);\n+\n+\tstrbuf_release(&local);\n+\tstrbuf_release(&remote);\n+\treturn strbuf_detach(&fmt, NULL);\n+}\n+\n static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)\n {\n \tint i;\n \tstruct ref_array array;\n \tint maxwidth = 0;\n \tconst char *remote_prefix = \"\";\n+\tstruct strbuf out = STRBUF_INIT;\n+\tchar *format;\n \n \t/*\n \t * If we are listing more than just remote branches,\n@@ -470,12 +359,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@@ -487,10 +378,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 rename_branch(const char *oldname, const char *newname, int force)\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.8.0\n"},{"id":"284184","messageId":"1461351847-22903-17-git-send-email-Karthik.188@gmail.com","threadId":"42113","inReplyTo":"1461351847-22903-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v5 16/16] branch: implement '--format' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-04-22T19:04:07Z","receivedAt":"2016-04-22T19:04:07Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Implement 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 4a7037f..8af132f 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 07068d2..6847ac3 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@@ -340,14 +341,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@@ -364,7 +365,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@@ -392,7 +394,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 rename_branch(const char *oldname, const char *newname, int force)\n@@ -491,6 +493,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@@ -531,6 +534,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@@ -591,7 +595,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.8.0\n"}]}