{"thread":{"id":"41689","subject":"[PATCH v2 01/16] ref-filter: implement %(if), %(then), and %(else) atoms","startedAt":"2016-03-15T16:47:00Z","lastAt":"2016-03-18T18:43:21Z","messageCount":21,"participants":["Karthik Nayak","Jacob Keller"],"isPatch":true,"patchVersion":2,"patchTotal":16},"messages":[{"id":"280790","messageId":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":null,"subject":"[PATCH v2 00/16] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:00Z","receivedAt":"2016-03-15T16:47:00Z","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)\n\nChanges in this version:\n\n1. Implement parser atoms for %(if:equals=...) and %(if:notequals=...)\n2. Include a pointer to the corresponding used_atom within atom_value.\n3. add missing documentation for %(upstream:track).\n4. Fix commit message.\n\nThanks to Jacob and Junio for their suggestions on the first iteration.\n\nKarthik Nayak (16):\n  ref-filter: implement %(if), %(then), and %(else) atoms\n  ref-filter: include reference to 'used_atom' within 'atom_value'\n  ref-filter: implement %(if:equals=<string>) and\n    %(if:notequals=<string>)\n  ref-filter: modify \"%(objectname:short)\" to take length\n  ref-filter: move get_head_description() from branch.c\n  ref-filter: introduce format_ref_array_item()\n  ref-filter: make %(upstream:track) prints \"[gone]\" for invalid\n    upstreams\n  ref-filter: add support for %(upstream:track,nobracket)\n  ref-filter: make \"%(symref)\" atom work with the ':short' modifier\n  ref-filter: introduce symref_atom_parser()\n  ref-filter: introduce refname_atom_parser()\n  ref-filter: add support for %(refname:dir) and %(refname:base)\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 |  58 ++++-\n builtin/branch.c                   | 267 ++++++----------------\n builtin/tag.c                      |   2 +\n ref-filter.c                       | 447 +++++++++++++++++++++++++++++++------\n ref-filter.h                       |   7 +\n t/t3203-branch-output.sh           |  12 +\n t/t6040-tracking-info.sh           |   2 +-\n t/t6300-for-each-ref.sh            |  40 +++-\n t/t6302-for-each-ref-filter.sh     |  88 ++++++++\n 10 files changed, 653 insertions(+), 277 deletions(-)\n\nInterdiff:\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 193e99e..578bbd1 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -116,10 +116,12 @@ 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).  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+\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. 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\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 76ea7c3..3435df1 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -75,6 +75,10 @@ static struct used_atom {\n \t\t\tunsigned int nlines;\n \t\t} contents;\n \t\tstruct {\n+\t\t\tconst char *if_equals,\n+\t\t\t\t*not_equals;\n+\t\t} if_then_else;\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@@ -226,6 +230,18 @@ 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 static void symref_atom_parser(struct used_atom *atom, const char *arg)\n {\n \tif (!arg)\n@@ -295,7 +311,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@@ -316,11 +332,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@@ -377,7 +391,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 (starts_with(used_atom[at].name, \"symref\"))\n+\tif (!strcmp(valid_atom[i].name, \"symref\"))\n \t\tneed_symref = 1;\n \treturn at;\n }\n@@ -456,7 +470,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@@ -497,14 +511,9 @@ static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_stat\n {\n \tstruct ref_formatting_stack *new;\n \tstruct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);\n-\tconst char *valp;\n \n-\tif (skip_prefix(atomv->s, \"equals=\", &valp))\n-\t\tif_then_else->if_equals = valp;\n-\telse if (skip_prefix(atomv->s, \"notequals=\", &valp))\n-\t\tif_then_else->not_equals = valp;\n-\telse if (atomv->s[0])\n-\t\tdie(_(\"format: unknown format if:%s\"), atomv->s);\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@@ -1243,6 +1252,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@@ -1307,7 +1317,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\n\n-- \n2.7.3\n"},{"id":"280789","messageId":"1458060436-1215-2-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 01/16] ref-filter: implement %(if), %(then), and %(else) atoms","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:01Z","receivedAt":"2016-03-15T16:47:01Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Implement %(if), %(then) and %(else) atoms. Used as\n%(if)...%(then)...%(end) or %(if)...%(then)...%(else)...%(end). If the\nformat string between %(if) and %(then) expands to an empty string, or\nto only whitespaces, then the whole %(if)...%(end) expands to the string\nfollowing %(then). Otherwise, it expands to the string following\n%(else), if any. Nesting of this construct is possible.\n\nThis is in preparation for porting over `git branch -l` to use\nref-filter APIs for printing.\n\nAdd Documentation and tests regarding the same.\n\nMentored-by: Christian Couder <christian.couder@gmail.com>\nMentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  45 +++++++++++--\n ref-filter.c                       | 133 +++++++++++++++++++++++++++++++++++--\n t/t6302-for-each-ref-filter.sh     |  70 +++++++++++++++++++\n 3 files changed, 237 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 012e8f9..d048561 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -141,10 +141,17 @@ align::\n \t\"width=\" and/or \"position=\" prefixes may be omitted, and bare\n \t<width> and <position> used instead.  For instance,\n \t`%(align:<width>,<position>)`. If the contents length is more\n-\tthan the width then no alignment is performed. If used with\n-\t'--quote' everything in between %(align:...) and %(end) is\n-\tquoted, but if nested then only the topmost level performs\n-\tquoting.\n+\tthan the width then no alignment is performed.\n+\n+if::\n+\tUsed as %(if)...%(then)...(%end) or\n+\t%(if)...%(then)...%(else)...%(end).  If there is an atom with\n+\tvalue or string literal after the %(if) then everything after\n+\tthe %(then) is printed, else if the %(else) atom is used, then\n+\teverything after %(else) is printed. We ignore space when\n+\tevaluating the string before %(then), this is useful when we\n+\tuse the %(HEAD) atom which prints either \"*\" or \" \" and we\n+\twant to apply the 'if' condition only on the 'HEAD' ref.\n \n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n@@ -181,6 +188,20 @@ As a special case for the date-type fields, you may specify a format for\n the date by adding `:` followed by date format name (see the\n values the `--date` option to linkgit::git-rev-list[1] takes).\n \n+Some atoms like %(align) and %(if) always require a matching %(end).\n+We call them \"opening atoms\" and sometimes denote them as %($open).\n+\n+When a scripting language specific quoting is in effect (i.e. one of\n+`--shell`, `--perl`, `--python`, `--tcl` is used), except for opening\n+atoms, replacement from every %(atom) is quoted when and only when it\n+appears at the top-level (that is, when it appears outside\n+%($open)...%(end)).\n+\n+When a scripting language specific quoting is in effect, everything\n+between a top-level opening atom and its matching %(end) is evaluated\n+according to the semantics of the opening atom and its result is\n+quoted.\n+\n \n EXAMPLES\n --------\n@@ -268,6 +289,22 @@ eval=`git for-each-ref --shell --format=\"$fmt\" \\\n eval \"$eval\"\n ------------\n \n+\n+An example to show the usage of %(if)...%(then)...%(else)...%(end).\n+This prefixes the current branch with a star.\n+\n+------------\n+git for-each-ref --format=\"%(if)%(HEAD)%(then)* %(else)  %(end)%(refname:short)\" refs/heads/\n+------------\n+\n+\n+An example to show the usage of %(if)...%(then)...%(end).\n+This prints the authorname, if present.\n+\n+------------\n+git for-each-ref --format=\"%(refname)%(if)%(authorname)%(then) %(color:red)Authored by: %(authorname)%(end)\"\n+------------\n+\n SEE ALSO\n --------\n linkgit:git-show-ref[1]\ndiff --git a/ref-filter.c b/ref-filter.c\nindex bc551a7..41e73f0 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -21,6 +21,12 @@ struct align {\n \tunsigned int width;\n };\n \n+struct if_then_else {\n+\tunsigned int then_atom_seen : 1,\n+\t\telse_atom_seen : 1,\n+\t\tcondition_satisfied : 1;\n+};\n+\n /*\n  * An atom is a valid field atom listed below, possibly prefixed with\n  * a \"*\" to denote deref_tag().\n@@ -203,6 +209,9 @@ static struct {\n \t{ \"color\", FIELD_STR, color_atom_parser },\n \t{ \"align\", FIELD_STR, align_atom_parser },\n \t{ \"end\" },\n+\t{ \"if\" },\n+\t{ \"then\" },\n+\t{ \"else\" },\n };\n \n #define REF_FORMATTING_STATE_INIT  { 0, NULL }\n@@ -210,7 +219,7 @@ static struct {\n struct ref_formatting_stack {\n \tstruct ref_formatting_stack *prev;\n \tstruct strbuf output;\n-\tvoid (*at_end)(struct ref_formatting_stack *stack);\n+\tvoid (*at_end)(struct ref_formatting_stack **stack);\n \tvoid *at_end_data;\n };\n \n@@ -343,13 +352,14 @@ static void pop_stack_element(struct ref_formatting_stack **stack)\n \t*stack = prev;\n }\n \n-static void end_align_handler(struct ref_formatting_stack *stack)\n+static void end_align_handler(struct ref_formatting_stack **stack)\n {\n-\tstruct align *align = (struct align *)stack->at_end_data;\n+\tstruct ref_formatting_stack *cur = *stack;\n+\tstruct align *align = (struct align *)cur->at_end_data;\n \tstruct strbuf s = STRBUF_INIT;\n \n-\tstrbuf_utf8_align(&s, align->position, align->width, stack->output.buf);\n-\tstrbuf_swap(&stack->output, &s);\n+\tstrbuf_utf8_align(&s, align->position, align->width, cur->output.buf);\n+\tstrbuf_swap(&cur->output, &s);\n \tstrbuf_release(&s);\n }\n \n@@ -363,6 +373,103 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s\n \tnew->at_end_data = &atomv->u.align;\n }\n \n+static void if_then_else_handler(struct ref_formatting_stack **stack)\n+{\n+\tstruct ref_formatting_stack *cur = *stack;\n+\tstruct ref_formatting_stack *prev = cur->prev;\n+\tstruct if_then_else *if_then_else = (struct if_then_else *)cur->at_end_data;\n+\n+\tif (!if_then_else->then_atom_seen)\n+\t\tdie(_(\"format: %%(if) atom used without a %%(then) atom\"));\n+\n+\tif (if_then_else->else_atom_seen) {\n+\t\t/*\n+\t\t * There is an %(else) atom: we need to drop one state from the\n+\t\t * stack, either the %(else) branch if the condition is satisfied, or\n+\t\t * the %(then) branch if it isn't.\n+\t\t */\n+\t\tif (if_then_else->condition_satisfied) {\n+\t\t\tstrbuf_reset(&cur->output);\n+\t\t\tpop_stack_element(&cur);\n+\t\t} else {\n+\t\t\tstrbuf_swap(&cur->output, &prev->output);\n+\t\t\tstrbuf_reset(&cur->output);\n+\t\t\tpop_stack_element(&cur);\n+\t\t}\n+\t} else if (!if_then_else->condition_satisfied)\n+\t\t/*\n+\t\t * No %(else) atom: just drop the %(then) branch if the\n+\t\t * condition is not satisfied.\n+\t\t */\n+\t\tstrbuf_reset(&cur->output);\n+\n+\t*stack = cur;\n+\tfree(if_then_else);\n+}\n+\n+static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n+{\n+\tstruct ref_formatting_stack *new;\n+\tstruct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);\n+\n+\tpush_stack_element(&state->stack);\n+\tnew = state->stack;\n+\tnew->at_end = if_then_else_handler;\n+\tnew->at_end_data = if_then_else;\n+}\n+\n+static int is_empty(const char *s)\n+{\n+\twhile (*s != '\\0') {\n+\t\tif (!isspace(*s))\n+\t\t\treturn 0;\n+\t\ts++;\n+\t}\n+\treturn 1;\n+}\n+\n+static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n+{\n+\tstruct ref_formatting_stack *cur = state->stack;\n+\tstruct if_then_else *if_then_else = NULL;\n+\n+\tif (cur->at_end == if_then_else_handler)\n+\t\tif_then_else = (struct if_then_else *)cur->at_end_data;\n+\tif (!if_then_else)\n+\t\tdie(_(\"format: %%(then) atom used without an %%(if) atom\"));\n+\tif (if_then_else->then_atom_seen)\n+\t\tdie(_(\"format: %%(then) atom used more than once\"));\n+\tif (if_then_else->else_atom_seen)\n+\t\tdie(_(\"format: %%(then) atom used after %%(else)\"));\n+\tif_then_else->then_atom_seen = 1;\n+\t/*\n+\t * If there exists non-empty string between the 'if' and\n+\t * 'then' atom then the 'if' condition is satisfied.\n+\t */\n+\tif (cur->output.len && !is_empty(cur->output.buf))\n+\t\tif_then_else->condition_satisfied = 1;\n+\tstrbuf_reset(&cur->output);\n+}\n+\n+static void else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n+{\n+\tstruct ref_formatting_stack *prev = state->stack;\n+\tstruct if_then_else *if_then_else = NULL;\n+\n+\tif (prev->at_end == if_then_else_handler)\n+\t\tif_then_else = (struct if_then_else *)prev->at_end_data;\n+\tif (!if_then_else)\n+\t\tdie(_(\"format: %%(else) atom used without an %%(if) atom\"));\n+\tif (!if_then_else->then_atom_seen)\n+\t\tdie(_(\"format: %%(else) atom used without a %%(then) atom\"));\n+\tif (if_then_else->else_atom_seen)\n+\t\tdie(_(\"format: %%(else) atom used more than once\"));\n+\tif_then_else->else_atom_seen = 1;\n+\tpush_stack_element(&state->stack);\n+\tstate->stack->at_end_data = prev->at_end_data;\n+\tstate->stack->at_end = prev->at_end;\n+}\n+\n static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)\n {\n \tstruct ref_formatting_stack *current = state->stack;\n@@ -370,14 +477,17 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta\n \n \tif (!current->at_end)\n \t\tdie(_(\"format: %%(end) atom used without corresponding atom\"));\n-\tcurrent->at_end(current);\n+\tcurrent->at_end(&state->stack);\n+\n+\t/*  Stack may have been popped within at_end(), hence reset the current pointer */\n+\tcurrent = state->stack;\n \n \t/*\n \t * Perform quote formatting when the stack element is that of\n \t * a supporting atom. If nested then perform quote formatting\n \t * only on the topmost supporting atom.\n \t */\n-\tif (!state->stack->prev->prev) {\n+\tif (!current->prev->prev) {\n \t\tquote_formatting(&s, current->output.buf, state->quote_style);\n \t\tstrbuf_swap(&current->output, &s);\n \t}\n@@ -1029,6 +1139,15 @@ static void populate_value(struct ref_array_item *ref)\n \t\t} else if (!strcmp(name, \"end\")) {\n \t\t\tv->handler = end_atom_handler;\n \t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"if\")) {\n+\t\t\tv->handler = if_atom_handler;\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"then\")) {\n+\t\t\tv->handler = then_atom_handler;\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"else\")) {\n+\t\t\tv->handler = else_atom_handler;\n+\t\t\tcontinue;\n \t\t} else\n \t\t\tcontinue;\n \ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex bcf472b..954b12a 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -297,4 +297,74 @@ test_expect_success 'reverse version sort' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'improper usage of %(if), %(then), %(else) and %(end) atoms' '\n+\ttest_must_fail git for-each-ref --format=\"%(if)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(then) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(then) %(then) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(then) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(then) %(else)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(else) %(then) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(then) %(else) %(else) %(end)\" &&\n+\ttest_must_fail git for-each-ref --format=\"%(if) %(end)\"\n+'\n+\n+test_expect_success 'check %(if)...%(then)...%(end) atoms' '\n+\tgit for-each-ref --format=\"%(if)%(authorname)%(then)%(authorname): %(refname)%(end)\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tA U Thor: refs/heads/master\n+\tA U Thor: refs/heads/side\n+\tA U Thor: refs/odd/spot\n+\t\n+\tA U Thor: refs/tags/foo1.10\n+\tA U Thor: refs/tags/foo1.3\n+\tA U Thor: refs/tags/foo1.6\n+\tA U Thor: refs/tags/four\n+\tA U Thor: refs/tags/one\n+\t\n+\tA U Thor: refs/tags/three\n+\tA U Thor: refs/tags/two\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'check %(if)...%(then)...%(else)...%(end) atoms' '\n+\tgit for-each-ref --format=\"%(if)%(authorname)%(then)%(authorname)%(else)No author%(end): %(refname)\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tA U Thor: refs/heads/master\n+\tA U Thor: refs/heads/side\n+\tA U Thor: refs/odd/spot\n+\tNo author: refs/tags/double-tag\n+\tA U Thor: refs/tags/foo1.10\n+\tA U Thor: refs/tags/foo1.3\n+\tA U Thor: refs/tags/foo1.6\n+\tA U Thor: refs/tags/four\n+\tA U Thor: refs/tags/one\n+\tNo author: refs/tags/signed-tag\n+\tA U Thor: refs/tags/three\n+\tA U Thor: refs/tags/two\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+test_expect_success 'ignore spaces in %(if) atom usage' '\n+\tgit for-each-ref --format=\"%(refname:short): %(if)%(HEAD)%(then)Head ref%(else)Not Head ref%(end)\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tmaster: Head ref\n+\tside: Not Head ref\n+\todd/spot: Not Head ref\n+\tdouble-tag: Not Head ref\n+\tfoo1.10: Not Head ref\n+\tfoo1.3: Not Head ref\n+\tfoo1.6: Not Head ref\n+\tfour: Not Head ref\n+\tone: Not Head ref\n+\tsigned-tag: Not Head ref\n+\tthree: Not Head ref\n+\ttwo: Not Head ref\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.7.3\n"},{"id":"280791","messageId":"1458060436-1215-3-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 02/16] ref-filter: include reference to 'used_atom' within 'atom_value'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:02Z","receivedAt":"2016-03-15T16:47:02Z","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.7.3\n"},{"id":"280792","messageId":"1458060436-1215-4-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 03/16] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:03Z","receivedAt":"2016-03-15T16:47:03Z","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 d048561..e1b1a66 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 954b12a..83716e4 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -367,4 +367,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.7.3\n"},{"id":"280795","messageId":"1458060436-1215-5-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 04/16] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:04Z","receivedAt":"2016-03-15T16:47:04Z","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 7 is used. The minimum length is 'MINIMUM_ABBREV'.\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 ref-filter.c            | 25 +++++++++++++++++++------\n t/t6300-for-each-ref.sh | 10 ++++++++++\n 2 files changed, 29 insertions(+), 6 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 857a8b5..17f781d 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.contents.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.7.3\n"},{"id":"280794","messageId":"1458060436-1215-6-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 05/16] ref-filter: move get_head_description() from branch.c","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:05Z","receivedAt":"2016-03-15T16:47:05Z","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 7b45b6b..460f32f 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -355,37 +355,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 17f781d..7004bf0 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.7.3\n"},{"id":"280793","messageId":"1458060436-1215-7-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 06/16] ref-filter: introduce format_ref_array_item()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:06Z","receivedAt":"2016-03-15T16:47:06Z","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 7004bf0..3bb474f 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.7.3\n"},{"id":"280796","messageId":"1458060436-1215-8-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 07/16] ref-filter: make %(upstream:track) prints \"[gone]\" for invalid upstreams","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:07Z","receivedAt":"2016-03-15T16:47:07Z","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 e1b1a66..6dbd7bd 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -115,7 +115,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 3bb474f..4d7e0c0 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.7.3\n"},{"id":"280798","messageId":"1458060436-1215-9-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 08/16] ref-filter: add support for %(upstream:track,nobracket)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:08Z","receivedAt":"2016-03-15T16:47:08Z","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 |  5 ++-\n ref-filter.c                       | 67 +++++++++++++++++++++++++-------------\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 50 insertions(+), 24 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 6dbd7bd..4e41cee 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -116,7 +116,10 @@ upstream::\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+\t\"[gone]\" whenever 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\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 4d7e0c0..8c97cdb 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.7.3\n"},{"id":"280797","messageId":"1458060436-1215-10-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 09/16] ref-filter: make \"%(symref)\" atom work with the ':short' modifier","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:09Z","receivedAt":"2016-03-15T16:47:09Z","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 8c97cdb..5c59b17 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.7.3\n"},{"id":"280801","messageId":"1458060436-1215-11-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 10/16] ref-filter: introduce symref_atom_parser()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:10Z","receivedAt":"2016-03-15T16:47:10Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce symref_atom_parser() which will parse the '%(symref)' atom and\nstore information into the 'used_atom' structure based on the modifiers\nused along with the atom.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n ref-filter.c | 25 +++++++++++++++++++++++--\n 1 file changed, 23 insertions(+), 2 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 5c59b17..7b35e4f 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -62,6 +62,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} u;\n } *used_atom;\n static int used_atom_cnt, need_tagged, need_symref;\n@@ -217,6 +218,15 @@ 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 struct {\n \tconst char *name;\n@@ -252,7 +262,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@@ -1132,6 +1142,17 @@ 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 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+}\n+\n /*\n  * Parse the object referred by ref, and grab needed value.\n  */\n@@ -1176,7 +1197,7 @@ static void populate_value(struct ref_array_item *ref)\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\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-- \n2.7.3\n"},{"id":"280799","messageId":"1458060436-1215-12-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 11/16] ref-filter: introduce refname_atom_parser()","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:11Z","receivedAt":"2016-03-15T16:47:11Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce refname_atom_parser() which will parse the '%(refname)' atom\nand store information into the 'used_atom' structure based on the\nmodifiers used along with the atom.\n\nSigned-off-by: Karthik Nayak <Karthik.188@gmail.com>\n---\n ref-filter.c | 70 +++++++++++++++++++++++++++++++++---------------------------\n 1 file changed, 39 insertions(+), 31 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7b35e4f..dc1e404 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -63,6 +63,10 @@ static struct used_atom {\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_NORMAL, R_SHORT, R_STRIP } option;\n+\t\t\tunsigned int strip;\n+\t\t} refname;\n \t} u;\n } *used_atom;\n static int used_atom_cnt, need_tagged, need_symref;\n@@ -228,12 +232,27 @@ static void symref_atom_parser(struct used_atom *atom, const char *arg)\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\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\" },\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@@ -1047,21 +1066,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@@ -1153,6 +1167,18 @@ static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref\n \t\treturn 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\n+\t\treturn ref->refname;\n+}\n+\n /*\n  * Parse the object referred by ref, and grab needed value.\n  */\n@@ -1181,7 +1207,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,11 +1217,9 @@ 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\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@@ -1273,21 +1296,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\n-- \n2.7.3\n"},{"id":"280802","messageId":"1458060436-1215-13-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 12/16] ref-filter: add support for %(refname:dir) and %(refname:base)","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:12Z","receivedAt":"2016-03-15T16:47:12Z","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 the %(refname) atom. The `:dir`\noption gives the directory (the part after $GIT_DIR/) of the ref without\nthe refname. 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 |  4 +++-\n ref-filter.c                       | 28 +++++++++++++++++++++++++---\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 30 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 4e41cee..578bbd1 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`).\ndiff --git a/ref-filter.c b/ref-filter.c\nindex dc1e404..73e0a7f 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -64,7 +64,7 @@ static struct used_atom {\n \t\t} objectname;\n \t\tenum { S_FULL, S_SHORT } symref;\n \t\tstruct {\n-\t\t\tenum { R_NORMAL, R_SHORT, R_STRIP } option;\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} u;\n@@ -243,7 +243,11 @@ static void refname_atom_parser(struct used_atom *atom, const char *arg)\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\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@@ -1175,7 +1179,25 @@ static const char *get_refname(struct used_atom *atom, struct ref_array_item *re\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\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 }\n \ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex b06ea1c..36d32d7 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -53,6 +53,8 @@ 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 push refs/remotes/myfork/master\n-- \n2.7.3\n"},{"id":"280803","messageId":"1458060436-1215-14-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 13/16] ref-filter: allow porcelain to translate messages in the output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:13Z","receivedAt":"2016-03-15T16:47:13Z","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 73e0a7f..3435df1 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@@ -1097,15 +1117,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.7.3\n"},{"id":"280800","messageId":"1458060436-1215-15-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 14/16] branch, tag: use porcelain output","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:14Z","receivedAt":"2016-03-15T16:47:14Z","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 460f32f..8747d82 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -622,6 +622,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 1705c94..82a04ed 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -373,6 +373,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\tsetup_ref_filter_porcelain_msg();\n+\n \tgit_config(git_tag_config, sorting_tail);\n \n \tmemset(&opt, 0, sizeof(opt));\n-- \n2.7.3\n"},{"id":"280804","messageId":"1458060436-1215-16-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 15/16] branch: use ref-filter printing APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:15Z","receivedAt":"2016-03-15T16:47:15Z","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\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         | 226 ++++++++++++++---------------------------------\n t/t6040-tracking-info.sh |   2 +-\n 2 files changed, 66 insertions(+), 162 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 8747d82..29cd206 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -35,12 +35,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@@ -271,157 +271,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 = \"\";\n-\tconst char *desc = item->refname;\n-\tchar *to_free = NULL;\n-\n-\tswitch (item->kind) {\n-\tcase FILTER_REFS_BRANCHES:\n-\t\tskip_prefix(desc, \"refs/heads/\", &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\tskip_prefix(desc, \"refs/remotes/\", &desc);\n-\t\tcolor = BRANCH_COLOR_REMOTE;\n-\t\tprefix = 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, 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\tskip_prefix(item->symref, \"refs/remotes/\", &desc);\n-\t\tstrbuf_addf(&out, \" -> %s\", desc);\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@@ -432,7 +281,10 @@ 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\tw = strlen(get_head_description());\n+\t\telse\n+\t\t\tw = utf8_strwidth(desc);\n \n \t\tif (it->kind == FILTER_REFS_REMOTES)\n \t\t\tw += remote_bonus;\n@@ -442,12 +294,51 @@ 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\", 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@@ -459,12 +350,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@@ -476,10 +369,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/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.7.3\n"},{"id":"280805","messageId":"1458060436-1215-17-git-send-email-Karthik.188@gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v2 16/16] branch: implement '--format' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-15T16:47:16Z","receivedAt":"2016-03-15T16:47:16Z","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     | 12 ++++++++++++\n 3 files changed, 27 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 29cd206..fb05b39 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -27,6 +27,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@@ -331,14 +332,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@@ -355,7 +356,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@@ -383,7 +385,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@@ -483,6 +485,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@@ -523,6 +526,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@@ -583,7 +587,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 4261403..c33a3f3 100755\n--- a/t/t3203-branch-output.sh\n+++ b/t/t3203-branch-output.sh\n@@ -184,4 +184,16 @@ test_expect_success 'ambiguous branch/tag not marked' '\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+\tEOF\n+\tgit branch --format=\"Refname is %(refname)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.7.3\n"},{"id":"280851","messageId":"CA+P7+xoHy0oBGdYqhCmA3h=fv-YhDSyZV=kJyizwNnNcm37jBA@mail.gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-1-git-send-email-Karthik.188@gmail.com","subject":"Re: [PATCH v2 00/16] port branch.c to use ref-filter's printing options","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-03-15T22:00:41Z","receivedAt":"2016-03-15T22:00:41Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Mar 15, 2016 at 9:47 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index 193e99e..578bbd1 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -116,10 +116,12 @@ upstream::\n>         `refname` above.  Additionally respects `:track` to show\n>         \"[ahead N, behind M]\" and `:trackshort` to show the terse\n>         version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n> -       or \"=\" (in sync).  Append `:track,nobracket` to show tracking\n> -       information without brackets (i.e \"ahead N, behind M\").  Has\n> -       no effect if the ref does not have tracking information\n> -       associated with it.\n> +       or \"=\" (in sync).  Has no effect if the ref does not have\n> +       tracking information associated with it. `:track` also prints\n> +       \"[gone]\" whenever unknown upstream ref is encountered. Append\n> +       `:track,nobracket` to show tracking information without\n> +       brackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n> +       does not have tracking information associated with it.\n>\n\nIt looks like you duplicated \"Has no effect if the ref does not have\ntracking information associated with it\"\n"},{"id":"280853","messageId":"CA+P7+xofBUBOWLMdYi7V7Np00V0e7kcBgDDgjT-g4dnk2ag22g@mail.gmail.com","threadId":"41689","inReplyTo":"1458060436-1215-5-git-send-email-Karthik.188@gmail.com","subject":"Re: [PATCH v2 04/16] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-03-15T22:11:55Z","receivedAt":"2016-03-15T22:11:55Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Tue, Mar 15, 2016 at 9:47 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n> Add support for %(objectname:short=<length>) which would print the\n> abbreviated unique objectname of given length. When no length is\n> specified 7 is used. The minimum length is 'MINIMUM_ABBREV'.\n>\n\nIsn't the default abbreviation value used here, not hard coded to 7?\nThus user can configure this?\n\nDescription should also mention that length may be exceeded if it is\nnot long enough to be a unique identifier.\n\n> Add tests and documentation for the same.\n>\n\nAlso I didn't see any update to the documentation here..\n\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  ref-filter.c            | 25 +++++++++++++++++++------\n>  t/t6300-for-each-ref.sh | 10 ++++++++++\n>  2 files changed, 29 insertions(+), 6 deletions(-)\n>\n> diff --git a/ref-filter.c b/ref-filter.c\n> index 857a8b5..17f781d 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -55,7 +55,10 @@ static struct used_atom {\n>                         const char *if_equals,\n>                                 *not_equals;\n>                 } if_then_else;\n> -               enum { O_FULL, O_SHORT } objectname;\n> +               struct {\n> +                       enum { O_FULL, O_LENGTH, O_SHORT } option;\n> +                       unsigned int length;\n> +               } objectname;\n>         } u;\n>  } *used_atom;\n>  static int used_atom_cnt, need_tagged, need_symref;\n> @@ -118,10 +121,17 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)\n>  static void objectname_atom_parser(struct used_atom *atom, const char *arg)\n>  {\n>         if (!arg)\n> -               atom->u.objectname = O_FULL;\n> +               atom->u.objectname.option = O_FULL;\n>         else if (!strcmp(arg, \"short\"))\n> -               atom->u.objectname = O_SHORT;\n> -       else\n> +               atom->u.objectname.option = O_SHORT;\n> +       else if (skip_prefix(arg, \"short=\", &arg)) {\n> +               atom->u.contents.option = O_LENGTH;\n> +               if (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n> +                   atom->u.objectname.length == 0)\n> +                       die(_(\"positive value expected objectname:short=%s\"), arg);\n> +               if (atom->u.objectname.length < MINIMUM_ABBREV)\n> +                       atom->u.objectname.length = MINIMUM_ABBREV;\n\nShould this error instead of accepting and upgrading it to the minimum?\n\n> +       } else\n>                 die(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n>  }\n>\n> @@ -591,12 +601,15 @@ static int grab_objectname(const char *name, const unsigned char *sha1,\n>                            struct atom_value *v, struct used_atom *atom)\n>  {\n>         if (starts_with(name, \"objectname\")) {\n> -               if (atom->u.objectname == O_SHORT) {\n> +               if (atom->u.objectname.option == O_SHORT) {\n>                         v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));\n\nYes, here we are using DEFAULT_ABBREV, so you should mention that\ninstead of using 7 in the commit message.\n\n>                         return 1;\n> -               } else if (atom->u.objectname == O_FULL) {\n> +               } else if (atom->u.objectname.option == O_FULL) {\n>                         v->s = xstrdup(sha1_to_hex(sha1));\n>                         return 1;\n> +               } else if (atom->u.objectname.option == O_LENGTH) {\n> +                       v->s = xstrdup(find_unique_abbrev(sha1, atom->u.objectname.length));\n> +                       return 1;\n>                 } else\n>                         die(\"BUG: unknown %%(objectname) option\");\n>         }\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index 19a2823..2be0a3f 100755\n> --- a/t/t6300-for-each-ref.sh\n> +++ b/t/t6300-for-each-ref.sh\n> @@ -60,6 +60,8 @@ test_atom head objecttype commit\n>  test_atom head objectsize 171\n>  test_atom head objectname $(git rev-parse refs/heads/master)\n>  test_atom head objectname:short $(git rev-parse --short refs/heads/master)\n> +test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n> +test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n>  test_atom head tree $(git rev-parse refs/heads/master^{tree})\n>  test_atom head parent ''\n>  test_atom head numparent 0\n> @@ -99,6 +101,8 @@ test_atom tag objecttype tag\n>  test_atom tag objectsize 154\n>  test_atom tag objectname $(git rev-parse refs/tags/testtag)\n>  test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n> +test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n> +test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n>  test_atom tag tree ''\n>  test_atom tag parent ''\n>  test_atom tag numparent ''\n> @@ -164,6 +168,12 @@ test_expect_success 'Check invalid format specifiers are errors' '\n>         test_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n>  '\n>\n> +test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n> +       test_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n> +       test_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n> +'\n> +\n>  test_date () {\n>         f=$1 &&\n>         committer_date=$2 &&\n> --\n> 2.7.3\n>\n"},{"id":"281166","messageId":"CAOLa=ZQCWk46cMy0A-wpquVtCUXL4Eyo_uVLqxQfnpqo9r1OhA@mail.gmail.com","threadId":"41689","inReplyTo":"CA+P7+xofBUBOWLMdYi7V7Np00V0e7kcBgDDgjT-g4dnk2ag22g@mail.gmail.com","subject":"Re: [PATCH v2 04/16] ref-filter: modify \"%(objectname:short)\" to take length","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-18T18:38:09Z","receivedAt":"2016-03-18T18:38:09Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hello\n\nOn Wed, Mar 16, 2016 at 3:41 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Mar 15, 2016 at 9:47 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> Add support for %(objectname:short=<length>) which would print the\n>> abbreviated unique objectname of given length. When no length is\n>> specified 7 is used. The minimum length is 'MINIMUM_ABBREV'.\n>>\n>\n> Isn't the default abbreviation value used here, not hard coded to 7?\n> Thus user can configure this?\n>\n\nDEFAULT_ABBREV is set to 7. But I think it makes for sense to state\nDEFAULT_ABBREV\nrather than mention 7 without any base information.\n\n> Description should also mention that length may be exceeded if it is\n> not long enough to be a unique identifier.\n>\n\nWill add that in.\n\n>> Add tests and documentation for the same.\n>>\n>\n> Also I didn't see any update to the documentation here..\n\nI totally missed that out, will add it in\n\n>\n>> Mentored-by: Christian Couder <christian.couder@gmail.com>\n>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>> ---\n>>  ref-filter.c            | 25 +++++++++++++++++++------\n>>  t/t6300-for-each-ref.sh | 10 ++++++++++\n>>  2 files changed, 29 insertions(+), 6 deletions(-)\n>>\n>> diff --git a/ref-filter.c b/ref-filter.c\n>> index 857a8b5..17f781d 100644\n>> --- a/ref-filter.c\n>> +++ b/ref-filter.c\n>> @@ -55,7 +55,10 @@ static struct used_atom {\n>>                         const char *if_equals,\n>>                                 *not_equals;\n>>                 } if_then_else;\n>> -               enum { O_FULL, O_SHORT } objectname;\n>> +               struct {\n>> +                       enum { O_FULL, O_LENGTH, O_SHORT } option;\n>> +                       unsigned int length;\n>> +               } objectname;\n>>         } u;\n>>  } *used_atom;\n>>  static int used_atom_cnt, need_tagged, need_symref;\n>> @@ -118,10 +121,17 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)\n>>  static void objectname_atom_parser(struct used_atom *atom, const char *arg)\n>>  {\n>>         if (!arg)\n>> -               atom->u.objectname = O_FULL;\n>> +               atom->u.objectname.option = O_FULL;\n>>         else if (!strcmp(arg, \"short\"))\n>> -               atom->u.objectname = O_SHORT;\n>> -       else\n>> +               atom->u.objectname.option = O_SHORT;\n>> +       else if (skip_prefix(arg, \"short=\", &arg)) {\n>> +               atom->u.contents.option = O_LENGTH;\n>> +               if (strtoul_ui(arg, 10, &atom->u.objectname.length) ||\n>> +                   atom->u.objectname.length == 0)\n>> +                       die(_(\"positive value expected objectname:short=%s\"), arg);\n>> +               if (atom->u.objectname.length < MINIMUM_ABBREV)\n>> +                       atom->u.objectname.length = MINIMUM_ABBREV;\n>\n> Should this error instead of accepting and upgrading it to the minimum?\n>\n\nI prefer that it doesn't error out so that someone who's not aware of\nthe absolute\nvalue of MINIMUM_ABBREV would still be able to give a value  which is\n< MINIMUM_ABRREV\nand still expect results.\n\n>> +       } else\n>>                 die(_(\"unrecognized %%(objectname) argument: %s\"), arg);\n>>  }\n>>\n>> @@ -591,12 +601,15 @@ static int grab_objectname(const char *name, const unsigned char *sha1,\n>>                            struct atom_value *v, struct used_atom *atom)\n>>  {\n>>         if (starts_with(name, \"objectname\")) {\n>> -               if (atom->u.objectname == O_SHORT) {\n>> +               if (atom->u.objectname.option == O_SHORT) {\n>>                         v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));\n>\n> Yes, here we are using DEFAULT_ABBREV, so you should mention that\n> instead of using 7 in the commit message.\n>\n\nWill do.\n\n>>                         return 1;\n>> -               } else if (atom->u.objectname == O_FULL) {\n>> +               } else if (atom->u.objectname.option == O_FULL) {\n>>                         v->s = xstrdup(sha1_to_hex(sha1));\n>>                         return 1;\n>> +               } else if (atom->u.objectname.option == O_LENGTH) {\n>> +                       v->s = xstrdup(find_unique_abbrev(sha1, atom->u.objectname.length));\n>> +                       return 1;\n>>                 } else\n>>                         die(\"BUG: unknown %%(objectname) option\");\n>>         }\n>> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n>> index 19a2823..2be0a3f 100755\n>> --- a/t/t6300-for-each-ref.sh\n>> +++ b/t/t6300-for-each-ref.sh\n>> @@ -60,6 +60,8 @@ test_atom head objecttype commit\n>>  test_atom head objectsize 171\n>>  test_atom head objectname $(git rev-parse refs/heads/master)\n>>  test_atom head objectname:short $(git rev-parse --short refs/heads/master)\n>> +test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n>> +test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n>>  test_atom head tree $(git rev-parse refs/heads/master^{tree})\n>>  test_atom head parent ''\n>>  test_atom head numparent 0\n>> @@ -99,6 +101,8 @@ test_atom tag objecttype tag\n>>  test_atom tag objectsize 154\n>>  test_atom tag objectname $(git rev-parse refs/tags/testtag)\n>>  test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)\n>> +test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)\n>> +test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)\n>>  test_atom tag tree ''\n>>  test_atom tag parent ''\n>>  test_atom tag numparent ''\n>> @@ -164,6 +168,12 @@ test_expect_success 'Check invalid format specifiers are errors' '\n>>         test_must_fail git for-each-ref --format=\"%(authordate:INVALID)\" refs/heads\n>>  '\n>>\n>> +test_expect_success 'arguments to %(objectname:short=) must be positive integers' '\n>> +       test_must_fail git for-each-ref --format=\"%(objectname:short=0)\" &&\n>> +       test_must_fail git for-each-ref --format=\"%(objectname:short=-1)\" &&\n>> +       test_must_fail git for-each-ref --format=\"%(objectname:short=foo)\"\n>> +'\n>> +\n>>  test_date () {\n>>         f=$1 &&\n>>         committer_date=$2 &&\n>> --\n>> 2.7.3\n>>\n\nThanks for your suggestions.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"281167","messageId":"CAOLa=ZS7nwqnjciN2MkaRcuhf2NwOrDDgtqKDsDUJURuP8R1=A@mail.gmail.com","threadId":"41689","inReplyTo":"CA+P7+xoHy0oBGdYqhCmA3h=fv-YhDSyZV=kJyizwNnNcm37jBA@mail.gmail.com","subject":"Re: [PATCH v2 00/16] port branch.c to use ref-filter's printing options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2016-03-18T18:43:21Z","receivedAt":"2016-03-18T18:43:21Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Hello,\n\nOn Wed, Mar 16, 2016 at 3:30 AM, Jacob Keller <jacob.keller@gmail.com> wrote:\n> On Tue, Mar 15, 2016 at 9:47 AM, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n>> index 193e99e..578bbd1 100644\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -116,10 +116,12 @@ upstream::\n>>         `refname` above.  Additionally respects `:track` to show\n>>         \"[ahead N, behind M]\" and `:trackshort` to show the terse\n>>         version: \">\" (ahead), \"<\" (behind), \"<>\" (ahead and behind),\n>> -       or \"=\" (in sync).  Append `:track,nobracket` to show tracking\n>> -       information without brackets (i.e \"ahead N, behind M\").  Has\n>> -       no effect if the ref does not have tracking information\n>> -       associated with it.\n>> +       or \"=\" (in sync).  Has no effect if the ref does not have\n>> +       tracking information associated with it. `:track` also prints\n>> +       \"[gone]\" whenever unknown upstream ref is encountered. Append\n>> +       `:track,nobracket` to show tracking information without\n>> +       brackets (i.e \"ahead N, behind M\").  Has no effect if the ref\n>> +       does not have tracking information associated with it.\n>>\n>\n> It looks like you duplicated \"Has no effect if the ref does not have\n> tracking information associated with it\"\n\nWill fix that, thanks.\n\n-- \nRegards,\nKarthik Nayak\n"}]}