{"thread":{"id":"39951","subject":"[PATCH v6 0/10] port tag.c to use ref-filter APIs","startedAt":"2015-07-28T06:32:00Z","lastAt":"2015-07-30T15:35:52Z","messageCount":42,"participants":["Karthik Nayak","Matthieu Moy","Eric Sunshine","Junio C Hamano"],"isPatch":true,"patchVersion":6,"patchTotal":10},"messages":[{"id":"266879","messageId":"CAOLa=ZR6_2NBB4v0Ynq391=8Jk2RZON6R0YG=HKUNwKx249b7Q@mail.gmail.com","threadId":"39951","inReplyTo":null,"subject":"[PATCH v6 0/10] port tag.c to use ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:32:00Z","receivedAt":"2015-07-28T06:32:00Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"This is part of my GSoC project to unify git tag -l, git branch -l,\ngit for-each-ref.  This patch series is continued from: Git (next)\nhttps://github.com/git/git/commit/bf5418f49ff0cebc6e5ce04ad1417e1a47c81b61\n\nVersion 5 can be found here:\nhttp://article.gmane.org/gmane.comp.version-control.git/274650\n\nChanges in v6:\n* Change the flow of commits, introduce rest_formatting_state.\n* Rename\nref_formatting -> apply_formatting_state\napply_pseudo_state -> store_formatting_state\n* Remove the patch for making color a pseudo atom, and rename\npseudo_atom to modifier_atom.\n* Small grammatical changes.\n\nSide Note: --format=\"%(padright:X)\" applies to the next available atom\nand not to the next span. I find this more accurate as I don't see why\nwe'd want to pad something of known length. But its up for discussion\n:D\n\nInterdiff:\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 597b189..0a34924 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -664,8 +664,6 @@ static void populate_value(struct ref_array_item *ref)\n             if (color_parse(name + 6, color) < 0)\n                 die(_(\"unable to parse format\"));\n             v->s = xstrdup(color);\n-            v->pseudo_atom = 1;\n-            v->color = 1;\n             continue;\n         } else if (!strcmp(name, \"flag\")) {\n             char buf[256], *cp = buf;\n@@ -702,7 +700,7 @@ static void populate_value(struct ref_array_item *ref)\n             if (strtoul_ui(valp, 10, (unsigned int *)&v->ul))\n                 die(_(\"positive integer expected after ':' in padright:%u\\n\"),\n                     (unsigned int)v->ul);\n-            v->pseudo_atom = 1;\n+            v->modifier_atom = 1;\n             v->pad_to_right = 1;\n             continue;\n         } else\n@@ -973,8 +971,8 @@ static int match_pattern(const char **patterns,\nconst char *refname)\n /*\n  * Return 1 if the refname matches one of the patterns, otherwise 0.\n  * A pattern can be path prefix (e.g. a refname \"refs/heads/master\"\n- * matches a pattern \"refs/heads/\") or a wildcard (e.g. the same ref\n- * matches \"refs/heads/m*\",too).\n+ * matches a pattern \"refs/heads/\" but not \"refs/heads/m\") or a\n+ * wildcard (e.g. the same ref matches \"refs/heads/m*\", too).\n  */\n static int match_name_as_path(const char **pattern, const char *refname)\n {\n@@ -1246,14 +1244,9 @@ void ref_array_sort(struct ref_sorting\n*sorting, struct ref_array *array)\n     qsort(array->items, array->nr, sizeof(struct ref_array_item *),\ncompare_refs);\n }\n\n-static void ref_formatting(struct ref_formatting_state *state,\n-               struct atom_value *v, struct strbuf *value)\n+static void apply_formatting_state(struct ref_formatting_state *state,\n+                   struct atom_value *v, struct strbuf *value)\n {\n-    if (state->color) {\n-        strbuf_addstr(value, state->color);\n-        free(state->color);\n-        state->color = NULL;\n-    }\n     if (state->pad_to_right) {\n         if (!is_utf8(v->s))\n             strbuf_addf(value, \"%-*s\", state->pad_to_right, v->s);\n@@ -1263,15 +1256,22 @@ static void ref_formatting(struct\nref_formatting_state *state,\n         }\n         return;\n     }\n-    strbuf_addf(value, \"%s\", v->s);\n+\n+    strbuf_addstr(value, v->s);\n }\n\n-static void print_value(struct ref_formatting_state *state, struct\natom_value *v)\n+static void print_value(struct atom_value *v, struct\nref_formatting_state *state)\n {\n     struct strbuf value = STRBUF_INIT;\n     struct strbuf formatted = STRBUF_INIT;\n\n-    ref_formatting(state, v, &value);\n+    /*\n+     * Some (pesudo) atoms have no immediate side effect, but only\n+     * affect the next atom. Store the relevant information from\n+     * these atoms in the 'state' variable for use when displaying\n+     * the next atom.\n+     */\n+    apply_formatting_state(state, v, &value);\n\n     switch (state->quote_style) {\n     case QUOTE_NONE:\n@@ -1292,8 +1292,8 @@ static void print_value(struct\nref_formatting_state *state, struct atom_value *v\n     }\n     if (state->quote_style != QUOTE_NONE)\n         fputs(formatted.buf, stdout);\n-    strbuf_release(&formatted);\n     strbuf_release(&value);\n+    strbuf_release(&formatted);\n }\n\n static int hex1(char ch)\n@@ -1334,13 +1334,18 @@ static void emit(const char *cp, const char *ep)\n     }\n }\n\n-static void apply_pseudo_state(struct ref_formatting_state *state,\n-                   struct atom_value *v)\n+static void store_formatting_state(struct ref_formatting_state *state,\n+                   struct atom_value *atomv)\n {\n-    if (v->color)\n-        state->color = (char *)v->s;\n-    if (v->pad_to_right)\n-        state->pad_to_right = v->ul;\n+    if (atomv->pad_to_right)\n+        state->pad_to_right = atomv->ul;\n+}\n+\n+static void reset_formatting_state(struct ref_formatting_state *state)\n+{\n+    int quote_style = state->quote_style;\n+    memset(state, 0, sizeof(*state));\n+    state->quote_style = quote_style;\n }\n\n /*\n@@ -1402,10 +1407,12 @@ void show_ref_array_item(struct ref_array_item\n*info, const char *format,\n         if (cp < sp)\n             emit(cp, sp);\n         get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n-        if (atomv->pseudo_atom)\n-            apply_pseudo_state(&state, atomv);\n-        else\n-            print_value(&state, atomv);\n+        if (atomv->modifier_atom)\n+            store_formatting_state(&state, atomv);\n+        else {\n+            print_value(atomv, &state);\n+            reset_formatting_state(&state);\n+        }\n     }\n     if (*cp) {\n         sp = cp + strlen(cp);\n@@ -1418,7 +1425,7 @@ void show_ref_array_item(struct ref_array_item\n*info, const char *format,\n         if (color_parse(\"reset\", color) < 0)\n             die(\"BUG: couldn't parse 'reset' as a color\");\n         resetv.s = color;\n-        print_value(&state, &resetv);\n+        print_value(&resetv, &state);\n     }\n     if (lines > 0) {\n         struct object_id oid;\ndiff --git a/ref-filter.h b/ref-filter.h\nindex a27745f..fcf469e 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -20,14 +20,12 @@\n struct atom_value {\n     const char *s;\n     unsigned long ul; /* used for sorting when not FIELD_STR */\n-    unsigned int pseudo_atom : 1, /*  atoms which aren't placeholders\nfor ref attributes */\n-        color : 1,\n+    unsigned int modifier_atom : 1, /*  atoms which act as modifiers\nfor the next atom */\n         pad_to_right : 1;\n };\n\n struct ref_formatting_state {\n     int quote_style;\n-    char *color;\n     unsigned int pad_to_right;\n };\n\ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex de872db..68688a9 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -83,17 +83,17 @@ test_expect_success 'filtering with --contains' '\n\n test_expect_success 'padding to the right using `padright`' '\n     cat >expect <<-\\EOF &&\n-    refs/heads/master        |\n-    refs/heads/side          |\n-    refs/odd/spot            |\n-    refs/tags/double-tag     |\n-    refs/tags/four           |\n-    refs/tags/one            |\n-    refs/tags/signed-tag     |\n-    refs/tags/three          |\n-    refs/tags/two            |\n+    refs/heads/master|refs/heads/master        |refs/heads/master|\n+    refs/heads/side|refs/heads/side          |refs/heads/side|\n+    refs/odd/spot|refs/odd/spot            |refs/odd/spot|\n+    refs/tags/double-tag|refs/tags/double-tag     |refs/tags/double-tag|\n+    refs/tags/four|refs/tags/four           |refs/tags/four|\n+    refs/tags/one|refs/tags/one            |refs/tags/one|\n+    refs/tags/signed-tag|refs/tags/signed-tag     |refs/tags/signed-tag|\n+    refs/tags/three|refs/tags/three          |refs/tags/three|\n+    refs/tags/two|refs/tags/two            |refs/tags/two|\n     EOF\n-    git for-each-ref --format=\"%(padright:25)%(refname)|\" >actual &&\n+    git for-each-ref\n--format=\"%(refname)%(padright:25)|%(refname)|%(refname)|\" >actual &&\n     test_cmp expect actual\n '\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"266880","messageId":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"CAOLa=ZR6_2NBB4v0Ynq391=8Jk2RZON6R0YG=HKUNwKx249b7Q@mail.gmail.com","subject":"[PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:33:22Z","receivedAt":"2015-07-28T06:33:22Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce 'ref_formatting' structure to hold values of pseudo atoms\nwhich help only in formatting. This will eventually be used by atoms\nlike `color` and the `padright` atom which will be introduced in a\nlater patch.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\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 | 71 +++++++++++++++++++++++++++++++++++++++++++++++-------------\n ref-filter.h |  5 +++++\n 2 files changed, 61 insertions(+), 15 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7561727..a919a14 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -10,6 +10,8 @@\n #include \"quote.h\"\n #include \"ref-filter.h\"\n #include \"revision.h\"\n+#include \"utf8.h\"\n+#include \"git-compat-util.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n@@ -620,7 +622,7 @@ static void populate_value(struct ref_array_item *ref)\n \t\tconst char *name = used_atom[i];\n \t\tstruct atom_value *v = &ref->value[i];\n \t\tint deref = 0;\n-\t\tconst char *refname;\n+\t\tconst char *refname = NULL;\n \t\tconst char *formatp;\n \t\tstruct branch *branch = NULL;\n \n@@ -1190,30 +1192,47 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n \tqsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);\n }\n \n-static void print_value(struct atom_value *v, int quote_style)\n+static void apply_formatting_state(struct ref_formatting_state *state,\n+\t\t\t\t   struct atom_value *v, struct strbuf *value)\n {\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tswitch (quote_style) {\n+\t/*  Eventually we'll formatt based on the ref_formatting_state */\n+\tstrbuf_addstr(value, v->s);\n+}\n+\n+static void print_value(struct atom_value *v, struct ref_formatting_state *state)\n+{\n+\tstruct strbuf value = STRBUF_INIT;\n+\tstruct strbuf formatted = STRBUF_INIT;\n+\n+\t/*\n+\t * Some (pesudo) atoms have no immediate side effect, but only\n+\t * affect the next atom. Store the relevant information from\n+\t * these atoms in the 'state' variable for use when displaying\n+\t * the next atom.\n+\t */\n+\tapply_formatting_state(state, v, &value);\n+\n+\tswitch (state->quote_style) {\n \tcase QUOTE_NONE:\n-\t\tfputs(v->s, stdout);\n+\t\tfputs(value.buf, stdout);\n \t\tbreak;\n \tcase QUOTE_SHELL:\n-\t\tsq_quote_buf(&sb, v->s);\n+\t\tsq_quote_buf(&formatted, value.buf);\n \t\tbreak;\n \tcase QUOTE_PERL:\n-\t\tperl_quote_buf(&sb, v->s);\n+\t\tperl_quote_buf(&formatted, value.buf);\n \t\tbreak;\n \tcase QUOTE_PYTHON:\n-\t\tpython_quote_buf(&sb, v->s);\n+\t\tpython_quote_buf(&formatted, value.buf);\n \t\tbreak;\n \tcase QUOTE_TCL:\n-\t\ttcl_quote_buf(&sb, v->s);\n+\t\ttcl_quote_buf(&formatted, value.buf);\n \t\tbreak;\n \t}\n-\tif (quote_style != QUOTE_NONE) {\n-\t\tfputs(sb.buf, stdout);\n-\t\tstrbuf_release(&sb);\n-\t}\n+\tif (state->quote_style != QUOTE_NONE)\n+\t\tfputs(formatted.buf, stdout);\n+\tstrbuf_release(&value);\n+\tstrbuf_release(&formatted);\n }\n \n static int hex1(char ch)\n@@ -1254,9 +1273,26 @@ static void emit(const char *cp, const char *ep)\n \t}\n }\n \n+static void store_formatting_state(struct ref_formatting_state *state,\n+\t\t\t\t   struct atom_value *atomv)\n+{\n+\t/*  Here the 'ref_formatting_state' variable will be filled */\n+}\n+\n+static void reset_formatting_state(struct ref_formatting_state *state)\n+{\n+\tint quote_style = state->quote_style;\n+\tmemset(state, 0, sizeof(*state));\n+\tstate->quote_style = quote_style;\n+}\n+\n void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n {\n \tconst char *cp, *sp, *ep;\n+\tstruct ref_formatting_state state;\n+\n+\tmemset(&state, 0, sizeof(state));\n+\tstate.quote_style = quote_style;\n \n \tfor (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n \t\tstruct atom_value *atomv;\n@@ -1265,7 +1301,12 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n \t\tif (cp < sp)\n \t\t\temit(cp, sp);\n \t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n-\t\tprint_value(atomv, quote_style);\n+\t\tif (atomv->modifier_atom)\n+\t\t\tstore_formatting_state(&state, atomv);\n+\t\telse {\n+\t\t\tprint_value(atomv, &state);\n+\t\t\treset_formatting_state(&state);\n+\t\t}\n \t}\n \tif (*cp) {\n \t\tsp = cp + strlen(cp);\n@@ -1278,7 +1319,7 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n \t\tif (color_parse(\"reset\", color) < 0)\n \t\t\tdie(\"BUG: couldn't parse 'reset' as a color\");\n \t\tresetv.s = color;\n-\t\tprint_value(&resetv, quote_style);\n+\t\tprint_value(&resetv, &state);\n \t}\n \tputchar('\\n');\n }\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 6bf27d8..12e6a6b 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -19,6 +19,11 @@\n struct atom_value {\n \tconst char *s;\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n+\tunsigned int modifier_atom : 1; /*  atoms which act as modifiers for the next atom */\n+};\n+\n+struct ref_formatting_state {\n+\tint quote_style;\n };\n \n struct ref_sorting {\n-- \n2.4.6\n"},{"id":"266883","messageId":"1438065211-3777-2-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v6 02/10] ref-filter: add option to pad atoms to the right","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:33:23Z","receivedAt":"2015-07-28T06:33:23Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add a new atom \"padright\" and support %(padright:X) where X is a\nnumber.  This will align the succeeding atom value to the left\nfollowed by spaces for a total length of X characters. If X is less\nthan the item size, the entire atom value is printed.\n\nAdd tests and documentation for the same.\n\nHelped-by: Duy Nguyen <pclouds@gmail.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\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 |  6 ++++++\n ref-filter.c                       | 27 +++++++++++++++++++++++++--\n ref-filter.h                       |  4 +++-\n t/t6302-for-each-ref-filter.sh     | 16 ++++++++++++++++\n 4 files changed, 50 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex e49d578..45dd7f8 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -127,6 +127,12 @@ color::\n \tChange output color.  Followed by `:<colorname>`, where names\n \tare described in `color.branch.*`.\n \n+padright::\n+\tPad succeeding atom to the right. Followed by `:<value>`,\n+\twhere `value` states the total length of atom including the\n+\tpadding. If the `value` is greater than the atom length, then\n+\tno padding is performed.\n+\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\ndiff --git a/ref-filter.c b/ref-filter.c\nindex a919a14..e5cd97b 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -55,6 +55,7 @@ static struct {\n \t{ \"flag\" },\n \t{ \"HEAD\" },\n \t{ \"color\" },\n+\t{ \"padright\" },\n };\n \n /*\n@@ -689,6 +690,18 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\telse\n \t\t\t\tv->s = \" \";\n \t\t\tcontinue;\n+\t\t} else if (starts_with(name, \"padright:\")) {\n+\t\t\tconst char *valp = NULL;\n+\n+\t\t\tskip_prefix(name, \"padright:\", &valp);\n+\t\t\tif (!valp[0])\n+\t\t\t\tdie(_(\"no value given with 'padright:'\"));\n+\t\t\tif (strtoul_ui(valp, 10, (unsigned int *)&v->ul))\n+\t\t\t\tdie(_(\"positive integer expected after ':' in padright:%u\\n\"),\n+\t\t\t\t    (unsigned int)v->ul);\n+\t\t\tv->modifier_atom = 1;\n+\t\t\tv->pad_to_right = 1;\n+\t\t\tcontinue;\n \t\t} else\n \t\t\tcontinue;\n \n@@ -1195,7 +1208,16 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n static void apply_formatting_state(struct ref_formatting_state *state,\n \t\t\t\t   struct atom_value *v, struct strbuf *value)\n {\n-\t/*  Eventually we'll formatt based on the ref_formatting_state */\n+\tif (state->pad_to_right) {\n+\t\tif (!is_utf8(v->s))\n+\t\t\tstrbuf_addf(value, \"%-*s\", state->pad_to_right, v->s);\n+\t\telse {\n+\t\t\tint utf8_compensation = strlen(v->s) - utf8_strwidth(v->s);\n+\t\t\tstrbuf_addf(value, \"%-*s\", state->pad_to_right + utf8_compensation, v->s);\n+\t\t}\n+\t\treturn;\n+\t}\n+\n \tstrbuf_addstr(value, v->s);\n }\n \n@@ -1276,7 +1298,8 @@ static void emit(const char *cp, const char *ep)\n static void store_formatting_state(struct ref_formatting_state *state,\n \t\t\t\t   struct atom_value *atomv)\n {\n-\t/*  Here the 'ref_formatting_state' variable will be filled */\n+\tif (atomv->pad_to_right)\n+\t\tstate->pad_to_right = atomv->ul;\n }\n \n static void reset_formatting_state(struct ref_formatting_state *state)\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 12e6a6b..23d8574 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -19,11 +19,13 @@\n struct atom_value {\n \tconst char *s;\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n-\tunsigned int modifier_atom : 1; /*  atoms which act as modifiers for the next atom */\n+\tunsigned int modifier_atom : 1, /*  atoms which act as modifiers for the next atom */\n+\t\tpad_to_right : 1;\n };\n \n struct ref_formatting_state {\n \tint quote_style;\n+\tunsigned int pad_to_right;\n };\n \n struct ref_sorting {\ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex 505a360..19ac480 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -81,4 +81,20 @@ test_expect_success 'filtering with --contains' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'padding to the right using `padright`' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/master|refs/heads/master        |refs/heads/master|\n+\trefs/heads/side|refs/heads/side          |refs/heads/side|\n+\trefs/odd/spot|refs/odd/spot            |refs/odd/spot|\n+\trefs/tags/double-tag|refs/tags/double-tag     |refs/tags/double-tag|\n+\trefs/tags/four|refs/tags/four           |refs/tags/four|\n+\trefs/tags/one|refs/tags/one            |refs/tags/one|\n+\trefs/tags/signed-tag|refs/tags/signed-tag     |refs/tags/signed-tag|\n+\trefs/tags/three|refs/tags/three          |refs/tags/three|\n+\trefs/tags/two|refs/tags/two            |refs/tags/two|\n+\tEOF\n+\tgit for-each-ref --format=\"%(refname)%(padright:25)|%(refname)|%(refname)|\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.6\n"},{"id":"266881","messageId":"1438065211-3777-3-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v6 03/10] ref-filter: add option to filter only tags","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:33:24Z","receivedAt":"2015-07-28T06:33:24Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nAdd a functions called 'for_each_tag_ref_fullpath()' to refs.{c,h}\nwhich iterates through each tag ref without trimming the path.\n\nAdd an option in 'filter_refs()' to use 'for_each_tag_ref_fullpath()'\nand filter refs. This type checking is done by adding a\n'FILTER_REFS_TAGS' in 'ref-filter.h'\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 | 2 ++\n ref-filter.h | 1 +\n refs.c       | 5 +++++\n refs.h       | 1 +\n 4 files changed, 9 insertions(+)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex e5cd97b..515655b 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1150,6 +1150,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int\n \t\tret = for_each_rawref(ref_filter_handler, &ref_cbdata);\n \telse if (type & FILTER_REFS_ALL)\n \t\tret = for_each_ref(ref_filter_handler, &ref_cbdata);\n+\telse if (type & FILTER_REFS_TAGS)\n+\t\tret = for_each_tag_ref_fullpath(ref_filter_handler, &ref_cbdata);\n \telse if (type)\n \t\tdie(\"filter_refs: invalid type\");\n \ndiff --git a/ref-filter.h b/ref-filter.h\nindex 23d8574..1956f67 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -15,6 +15,7 @@\n \n #define FILTER_REFS_INCLUDE_BROKEN 0x1\n #define FILTER_REFS_ALL 0x2\n+#define FILTER_REFS_TAGS 0x4\n \n struct atom_value {\n \tconst char *s;\ndiff --git a/refs.c b/refs.c\nindex 0b96ece..23ce483 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2108,6 +2108,11 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)\n \treturn for_each_ref_in(\"refs/tags/\", fn, cb_data);\n }\n \n+int for_each_tag_ref_fullpath(each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_ref(&ref_cache, \"refs/tags/\", fn, 0, 0, cb_data);\n+}\n+\n int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n \treturn for_each_ref_in_submodule(submodule, \"refs/tags/\", fn, cb_data);\ndiff --git a/refs.h b/refs.h\nindex e4e46c3..9eee2de 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -174,6 +174,7 @@ extern int head_ref(each_ref_fn fn, void *cb_data);\n extern int for_each_ref(each_ref_fn fn, void *cb_data);\n extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);\n extern int for_each_tag_ref(each_ref_fn fn, void *cb_data);\n+extern int for_each_tag_ref_fullpath(each_ref_fn fn, void *cb_data);\n extern int for_each_branch_ref(each_ref_fn fn, void *cb_data);\n extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);\n extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);\n-- \n2.4.6\n"},{"id":"266884","messageId":"1438065211-3777-4-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v6 04/10] ref-filter: support printing N lines from tag annotation","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:33:25Z","receivedAt":"2015-07-28T06:33:25Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nIn 'tag.c' we can print N lines from the annotation of the tag using\nthe '-n<num>' option. Copy code from 'tag.c' to 'ref-filter' and\nmodify 'ref-filter' to support printing of N lines from the annotation\nof tags.\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/for-each-ref.c |  2 +-\n builtin/tag.c          |  4 ++++\n ref-filter.c           | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-\n ref-filter.h           |  9 +++++++--\n 4 files changed, 62 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 40f343b..e4a4f8a 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -74,7 +74,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \tif (!maxcount || array.nr < maxcount)\n \t\tmaxcount = array.nr;\n \tfor (i = 0; i < maxcount; i++)\n-\t\tshow_ref_array_item(array.items[i], format, quote_style);\n+\t\tshow_ref_array_item(array.items[i], format, quote_style, 0);\n \tref_array_clear(&array);\n \treturn 0;\n }\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 471d6b1..0fc7557 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -185,6 +185,10 @@ static enum contains_result contains(struct commit *candidate,\n \treturn contains_test(candidate, want);\n }\n \n+/*\n+ * Currently duplicated in ref-filter, will eventually be removed as\n+ * we port tag.c to use ref-filter APIs.\n+ */\n static void show_tag_lines(const struct object_id *oid, int lines)\n {\n \tint i;\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 515655b..9bdfef0 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1311,7 +1311,51 @@ static void reset_formatting_state(struct ref_formatting_state *state)\n \tstate->quote_style = quote_style;\n }\n \n-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n+/*\n+ * If 'lines' is greater than 0, print that many lines from the given\n+ * object_id 'oid'.\n+ */\n+static void show_tag_lines(const struct object_id *oid, int lines)\n+{\n+\tint i;\n+\tunsigned long size;\n+\tenum object_type type;\n+\tchar *buf, *sp, *eol;\n+\tsize_t len;\n+\n+\tbuf = read_sha1_file(oid->hash, &type, &size);\n+\tif (!buf)\n+\t\tdie_errno(\"unable to read object %s\", oid_to_hex(oid));\n+\tif (type != OBJ_COMMIT && type != OBJ_TAG)\n+\t\tgoto free_return;\n+\tif (!size)\n+\t\tdie(\"an empty %s object %s?\",\n+\t\t    typename(type), oid_to_hex(oid));\n+\n+\t/* skip header */\n+\tsp = strstr(buf, \"\\n\\n\");\n+\tif (!sp)\n+\t\tgoto free_return;\n+\n+\t/* only take up to \"lines\" lines, and strip the signature from a tag */\n+\tif (type == OBJ_TAG)\n+\t\tsize = parse_signature(buf, size);\n+\tfor (i = 0, sp += 2; i < lines && sp < buf + size; i++) {\n+\t\tif (i)\n+\t\t\tprintf(\"\\n    \");\n+\t\teol = memchr(sp, '\\n', size - (sp - buf));\n+\t\tlen = eol ? eol - sp : size - (sp - buf);\n+\t\tfwrite(sp, len, 1, stdout);\n+\t\tif (!eol)\n+\t\t\tbreak;\n+\t\tsp = eol + 1;\n+\t}\n+free_return:\n+\tfree(buf);\n+}\n+\n+void show_ref_array_item(struct ref_array_item *info, const char *format,\n+\t\t\t int quote_style, unsigned int lines)\n {\n \tconst char *cp, *sp, *ep;\n \tstruct ref_formatting_state state;\n@@ -1346,6 +1390,11 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n \t\tresetv.s = color;\n \t\tprint_value(&resetv, &state);\n \t}\n+\tif (lines > 0) {\n+\t\tstruct object_id oid;\n+\t\thashcpy(oid.hash, info->objectname);\n+\t\tshow_tag_lines(&oid, lines);\n+\t}\n \tputchar('\\n');\n }\n \ndiff --git a/ref-filter.h b/ref-filter.h\nindex 1956f67..3e05fa0 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -62,6 +62,7 @@ struct ref_filter {\n \tstruct commit *merge_commit;\n \n \tunsigned int with_commit_tag_algo : 1;\n+\tunsigned int lines;\n };\n \n struct ref_filter_cbdata {\n@@ -93,8 +94,12 @@ 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-/*  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+/*\n+ * Print the ref using the given format and quote_style. If 'lines' > 0,\n+ * print that many lines of the the given ref.\n+ */\n+void show_ref_array_item(struct ref_array_item *info, const char *format,\n+\t\t\t int quote_style, unsigned int lines);\n /*  Callback function for parsing the sort option */\n int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);\n /*  Default sort option based on refname */\n-- \n2.4.6\n"},{"id":"266882","messageId":"1438065211-3777-5-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v6 05/10] ref-filter: add support to sort by version","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:33:26Z","receivedAt":"2015-07-28T06:33:26Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nAdd support to sort by version using the \"v:refname\" and\n\"version:refname\" option. This is achieved by using the 'versioncmp()'\nfunction as the comparing function for qsort.\n\nThis option is included to support sorting by versions in `git tag -l`\nwhich will eventaully be ported to use ref-filter APIs.\n\nAdd documentation and tests 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                       | 14 +++++++++-----\n ref-filter.h                       |  3 ++-\n t/t6302-for-each-ref-filter.sh     | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 50 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 45dd7f8..2b60aee 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -151,6 +151,9 @@ For sorting purposes, fields with numeric values sort in numeric\n order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).\n All other fields are used to sort in their byte-value order.\n \n+There is also an option to sort by versions, this can be done by using\n+the fieldname `version:refname` or in short `v:refname`.\n+\n In any case, a field name that refers to a field inapplicable to\n the object referred by the ref does not cause an error.  It\n returns an empty string instead.\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 9bdfef0..2dbb2b6 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -12,6 +12,7 @@\n #include \"revision.h\"\n #include \"utf8.h\"\n #include \"git-compat-util.h\"\n+#include \"version.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n@@ -1170,19 +1171,19 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru\n \n \tget_ref_atom_value(a, s->atom, &va);\n \tget_ref_atom_value(b, s->atom, &vb);\n-\tswitch (cmp_type) {\n-\tcase FIELD_STR:\n+\tif (s->version)\n+\t\tcmp = versioncmp(va->s, vb->s);\n+\telse if (cmp_type == FIELD_STR)\n \t\tcmp = strcmp(va->s, vb->s);\n-\t\tbreak;\n-\tdefault:\n+\telse {\n \t\tif (va->ul < vb->ul)\n \t\t\tcmp = -1;\n \t\telse if (va->ul == vb->ul)\n \t\t\tcmp = 0;\n \t\telse\n \t\t\tcmp = 1;\n-\t\tbreak;\n \t}\n+\n \treturn (s->reverse) ? -cmp : cmp;\n }\n \n@@ -1427,6 +1428,9 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)\n \t\ts->reverse = 1;\n \t\targ++;\n \t}\n+\tif (skip_prefix(arg, \"version:\", &arg) ||\n+\t    skip_prefix(arg, \"v:\", &arg))\n+\t\ts->version = 1;\n \tlen = strlen(arg);\n \ts->atom = parse_ref_filter_atom(arg, arg+len);\n \treturn 0;\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 3e05fa0..05a3cab 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -32,7 +32,8 @@ struct ref_formatting_state {\n struct ref_sorting {\n \tstruct ref_sorting *next;\n \tint atom; /* index into used_atom array (internal) */\n-\tunsigned reverse : 1;\n+\tunsigned reverse : 1,\n+\t\tversion : 1;\n };\n \n struct ref_array_item {\ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex 19ac480..68688a9 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -97,4 +97,40 @@ test_expect_success 'padding to the right using `padright`' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'setup for version sort' '\n+\ttest_commit foo1.3 &&\n+\ttest_commit foo1.6 &&\n+\ttest_commit foo1.10\n+'\n+\n+test_expect_success 'version sort' '\n+\tgit for-each-ref --sort=version:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tfoo1.3\n+\tfoo1.6\n+\tfoo1.10\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'version sort (shortened)' '\n+\tgit for-each-ref --sort=v:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tfoo1.3\n+\tfoo1.6\n+\tfoo1.10\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'reverse version sort' '\n+\tgit for-each-ref --sort=-version:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tfoo1.10\n+\tfoo1.6\n+\tfoo1.3\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.6\n"},{"id":"266885","messageId":"1438065211-3777-6-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v6 06/10] ref-filter: add option to match literal pattern","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:33:27Z","receivedAt":"2015-07-28T06:33:27Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nSince 'ref-filter' only has an option to match path names add an\noption for plain fnmatch pattern-matching.\n\nThis is to support the pattern matching options which are used in `git\ntag -l` and `git branch -l` where we can match patterns like `git tag\n-l foo*` which would match all tags which has a \"foo*\" pattern.\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/for-each-ref.c |  1 +\n ref-filter.c           | 42 +++++++++++++++++++++++++++++++++++++++---\n ref-filter.h           |  3 ++-\n 3 files changed, 42 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex e4a4f8a..3ad6a64 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -68,6 +68,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \tgit_config(git_default_config, NULL);\n \n \tfilter.name_patterns = argv;\n+\tfilter.match_as_path = 1;\n \tfilter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);\n \tref_array_sort(sorting, &array);\n \ndiff --git a/ref-filter.c b/ref-filter.c\nindex 2dbb2b6..0a34924 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -944,9 +944,35 @@ static int commit_contains(struct ref_filter *filter, struct commit *commit)\n \n /*\n  * Return 1 if the refname matches one of the patterns, otherwise 0.\n+ * A pattern can be a literal prefix (e.g. a refname \"refs/heads/master\"\n+ * matches a pattern \"refs/heads/mas\") or a wildcard (e.g. the same ref\n+ * matches \"refs/heads/mas*\", too).\n+ */\n+static int match_pattern(const char **patterns, const char *refname)\n+{\n+\t/*\n+\t * When no '--format' option is given we need to skip the prefix\n+\t * for matching refs of tags and branches.\n+\t */\n+\tif (skip_prefix(refname, \"refs/tags/\", &refname))\n+\t\t;\n+\telse if (skip_prefix(refname, \"refs/heads/\", &refname))\n+\t\t;\n+\telse if (skip_prefix(refname, \"refs/remotes/\", &refname))\n+\t\t;\n+\n+\tfor (; *patterns; patterns++) {\n+\t\tif (!wildmatch(*patterns, refname, 0, NULL))\n+\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Return 1 if the refname matches one of the patterns, otherwise 0.\n  * A pattern can be path prefix (e.g. a refname \"refs/heads/master\"\n- * matches a pattern \"refs/heads/\") or a wildcard (e.g. the same ref\n- * matches \"refs/heads/m*\",too).\n+ * matches a pattern \"refs/heads/\" but not \"refs/heads/m\") or a\n+ * wildcard (e.g. the same ref matches \"refs/heads/m*\", too).\n  */\n static int match_name_as_path(const char **pattern, const char *refname)\n {\n@@ -967,6 +993,16 @@ static int match_name_as_path(const char **pattern, const char *refname)\n \treturn 0;\n }\n \n+/* Return 1 if the refname matches one of the patterns, otherwise 0. */\n+static int filter_pattern_match(struct ref_filter *filter, const char *refname)\n+{\n+\tif (!*filter->name_patterns)\n+\t\treturn 1; /* No pattern always matches */\n+\tif (filter->match_as_path)\n+\t\treturn match_name_as_path(filter->name_patterns, refname);\n+\treturn match_pattern(filter->name_patterns, refname);\n+}\n+\n /*\n  * Given a ref (sha1, refname), check if the ref belongs to the array\n  * of sha1s. If the given ref is a tag, check if the given tag points\n@@ -1035,7 +1071,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,\n \t\treturn 0;\n \t}\n \n-\tif (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))\n+\tif (!filter_pattern_match(filter, refname))\n \t\treturn 0;\n \n \tif (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname))\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 05a3cab..fcf469e 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -62,7 +62,8 @@ struct ref_filter {\n \t} merge;\n \tstruct commit *merge_commit;\n \n-\tunsigned int with_commit_tag_algo : 1;\n+\tunsigned int with_commit_tag_algo : 1,\n+\t\tmatch_as_path : 1;\n \tunsigned int lines;\n };\n \n-- \n2.4.6\n"},{"id":"266886","messageId":"1438065211-3777-7-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v6 07/10] tag.c: use 'ref-filter' data structures","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:33:28Z","receivedAt":"2015-07-28T06:33:28Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nMake 'tag.c' use 'ref-filter' data structures and make changes to\nsupport the new data structures. This is a part of the process\nof porting 'tag.c' to use 'ref-filter' APIs.\n\nThis is a temporary step before porting 'tag.c' to use 'ref-filter'\ncompletely. As this is a temporary step, most of the code\nintroduced here will be removed when 'tag.c' is ported over to use\n'ref-filter' APIs\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/tag.c | 106 +++++++++++++++++++++++++++++++---------------------------\n 1 file changed, 57 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 0fc7557..e96bae2 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -17,6 +17,7 @@\n #include \"gpg-interface.h\"\n #include \"sha1-array.h\"\n #include \"column.h\"\n+#include \"ref-filter.h\"\n \n static const char * const git_tag_usage[] = {\n \tN_(\"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]\"),\n@@ -34,15 +35,6 @@ static const char * const git_tag_usage[] = {\n \n static int tag_sort;\n \n-struct tag_filter {\n-\tconst char **patterns;\n-\tint lines;\n-\tint sort;\n-\tstruct string_list tags;\n-\tstruct commit_list *with_commit;\n-};\n-\n-static struct sha1_array points_at;\n static unsigned int colopts;\n \n static int match_pattern(const char **patterns, const char *ref)\n@@ -61,19 +53,20 @@ static int match_pattern(const char **patterns, const char *ref)\n  * removed as we port tag.c to use the ref-filter APIs.\n  */\n static const unsigned char *match_points_at(const char *refname,\n-\t\t\t\t\t    const unsigned char *sha1)\n+\t\t\t\t\t    const unsigned char *sha1,\n+\t\t\t\t\t    struct sha1_array *points_at)\n {\n \tconst unsigned char *tagged_sha1 = NULL;\n \tstruct object *obj;\n \n-\tif (sha1_array_lookup(&points_at, sha1) >= 0)\n+\tif (sha1_array_lookup(points_at, sha1) >= 0)\n \t\treturn sha1;\n \tobj = parse_object(sha1);\n \tif (!obj)\n \t\tdie(_(\"malformed object at '%s'\"), refname);\n \tif (obj->type == OBJ_TAG)\n \t\ttagged_sha1 = ((struct tag *)obj)->tagged->sha1;\n-\tif (tagged_sha1 && sha1_array_lookup(&points_at, tagged_sha1) >= 0)\n+\tif (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)\n \t\treturn tagged_sha1;\n \treturn NULL;\n }\n@@ -228,12 +221,24 @@ free_return:\n \tfree(buf);\n }\n \n+static void ref_array_append(struct ref_array *array, const char *refname)\n+{\n+\tsize_t len = strlen(refname);\n+\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);\n+\tmemcpy(ref->refname, refname, len);\n+\tref->refname[len] = '\\0';\n+\tREALLOC_ARRAY(array->items, array->nr + 1);\n+\tarray->items[array->nr++] = ref;\n+}\n+\n static int show_reference(const char *refname, const struct object_id *oid,\n \t\t\t  int flag, void *cb_data)\n {\n-\tstruct tag_filter *filter = cb_data;\n+\tstruct ref_filter_cbdata *data = cb_data;\n+\tstruct ref_array *array = data->array;\n+\tstruct ref_filter *filter = data->filter;\n \n-\tif (match_pattern(filter->patterns, refname)) {\n+\tif (match_pattern(filter->name_patterns, refname)) {\n \t\tif (filter->with_commit) {\n \t\t\tstruct commit *commit;\n \n@@ -244,12 +249,12 @@ static int show_reference(const char *refname, const struct object_id *oid,\n \t\t\t\treturn 0;\n \t\t}\n \n-\t\tif (points_at.nr && !match_points_at(refname, oid->hash))\n+\t\tif (filter->points_at.nr && !match_points_at(refname, oid->hash, &filter->points_at))\n \t\t\treturn 0;\n \n \t\tif (!filter->lines) {\n-\t\t\tif (filter->sort)\n-\t\t\t\tstring_list_append(&filter->tags, refname);\n+\t\t\tif (tag_sort)\n+\t\t\t\tref_array_append(array, refname);\n \t\t\telse\n \t\t\t\tprintf(\"%s\\n\", refname);\n \t\t\treturn 0;\n@@ -264,36 +269,36 @@ static int show_reference(const char *refname, const struct object_id *oid,\n \n static int sort_by_version(const void *a_, const void *b_)\n {\n-\tconst struct string_list_item *a = a_;\n-\tconst struct string_list_item *b = b_;\n-\treturn versioncmp(a->string, b->string);\n+\tconst struct ref_array_item *a = *((struct ref_array_item **)a_);\n+\tconst struct ref_array_item *b = *((struct ref_array_item **)b_);\n+\treturn versioncmp(a->refname, b->refname);\n }\n \n-static int list_tags(const char **patterns, int lines,\n-\t\t     struct commit_list *with_commit, int sort)\n+static int list_tags(struct ref_filter *filter, int sort)\n {\n-\tstruct tag_filter filter;\n+\tstruct ref_array array;\n+\tstruct ref_filter_cbdata data;\n+\n+\tmemset(&array, 0, sizeof(array));\n+\tdata.array = &array;\n+\tdata.filter = filter;\n \n-\tfilter.patterns = patterns;\n-\tfilter.lines = lines;\n-\tfilter.sort = sort;\n-\tfilter.with_commit = with_commit;\n-\tmemset(&filter.tags, 0, sizeof(filter.tags));\n-\tfilter.tags.strdup_strings = 1;\n+\tif (filter->lines == -1)\n+\t\tfilter->lines = 0;\n \n-\tfor_each_tag_ref(show_reference, (void *)&filter);\n+\tfor_each_tag_ref(show_reference, &data);\n \tif (sort) {\n \t\tint i;\n \t\tif ((sort & SORT_MASK) == VERCMP_SORT)\n-\t\t\tqsort(filter.tags.items, filter.tags.nr,\n-\t\t\t      sizeof(struct string_list_item), sort_by_version);\n+\t\t\tqsort(array.items, array.nr,\n+\t\t\t      sizeof(struct ref_array_item *), sort_by_version);\n \t\tif (sort & REVERSE_SORT)\n-\t\t\tfor (i = filter.tags.nr - 1; i >= 0; i--)\n-\t\t\t\tprintf(\"%s\\n\", filter.tags.items[i].string);\n+\t\t\tfor (i = array.nr - 1; i >= 0; i--)\n+\t\t\t\tprintf(\"%s\\n\", array.items[i]->refname);\n \t\telse\n-\t\t\tfor (i = 0; i < filter.tags.nr; i++)\n-\t\t\t\tprintf(\"%s\\n\", filter.tags.items[i].string);\n-\t\tstring_list_clear(&filter.tags, 0);\n+\t\t\tfor (i = 0; i < array.nr; i++)\n+\t\t\t\tprintf(\"%s\\n\", array.items[i]->refname);\n+\t\tref_array_clear(&array);\n \t}\n \treturn 0;\n }\n@@ -574,17 +579,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tconst char *object_ref, *tag;\n \tstruct create_tag_options opt;\n \tchar *cleanup_arg = NULL;\n-\tint annotate = 0, force = 0, lines = -1;\n \tint create_reflog = 0;\n+\tint annotate = 0, force = 0;\n \tint cmdmode = 0;\n \tconst char *msgfile = NULL, *keyid = NULL;\n \tstruct msg_arg msg = { 0, STRBUF_INIT };\n-\tstruct commit_list *with_commit = NULL;\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n+\tstruct ref_filter filter;\n \tstruct option options[] = {\n \t\tOPT_CMDMODE('l', \"list\", &cmdmode, N_(\"list tag names\"), 'l'),\n-\t\t{ OPTION_INTEGER, 'n', NULL, &lines, N_(\"n\"),\n+\t\t{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_(\"n\"),\n \t\t\t\tN_(\"print <n> lines of each tag message\"),\n \t\t\t\tPARSE_OPT_OPTARG, NULL, 1 },\n \t\tOPT_CMDMODE('d', \"delete\", &cmdmode, N_(\"delete tags\"), 'd'),\n@@ -606,14 +611,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \n \t\tOPT_GROUP(N_(\"Tag listing options\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show tag list in columns\")),\n-\t\tOPT_CONTAINS(&with_commit, N_(\"print only tags that contain the commit\")),\n-\t\tOPT_WITH(&with_commit, N_(\"print only tags that contain the commit\")),\n+\t\tOPT_CONTAINS(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n+\t\tOPT_WITH(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n \t\t{\n \t\t\tOPTION_CALLBACK, 0, \"sort\", &tag_sort, N_(\"type\"), N_(\"sort tags\"),\n \t\t\tPARSE_OPT_NONEG, parse_opt_sort\n \t\t},\n \t\t{\n-\t\t\tOPTION_CALLBACK, 0, \"points-at\", &points_at, N_(\"object\"),\n+\t\t\tOPTION_CALLBACK, 0, \"points-at\", &filter.points_at, N_(\"object\"),\n \t\t\tN_(\"print only tags of the object\"), 0, parse_opt_object_name\n \t\t},\n \t\tOPT_END()\n@@ -622,6 +627,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tgit_config(git_tag_config, NULL);\n \n \tmemset(&opt, 0, sizeof(opt));\n+\tmemset(&filter, 0, sizeof(filter));\n+\tfilter.lines = -1;\n \n \targc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);\n \n@@ -638,7 +645,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_tag_usage, options);\n \n \tfinalize_colopts(&colopts, -1);\n-\tif (cmdmode == 'l' && lines != -1) {\n+\tif (cmdmode == 'l' && filter.lines != -1) {\n \t\tif (explicitly_enable_column(colopts))\n \t\t\tdie(_(\"--column and -n are incompatible\"));\n \t\tcolopts = 0;\n@@ -651,18 +658,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tcopts.padding = 2;\n \t\t\trun_column_filter(colopts, &copts);\n \t\t}\n-\t\tif (lines != -1 && tag_sort)\n+\t\tif (filter.lines != -1 && tag_sort)\n \t\t\tdie(_(\"--sort and -n are incompatible\"));\n-\t\tret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, tag_sort);\n+\t\tfilter.name_patterns = argv;\n+\t\tret = list_tags(&filter, tag_sort);\n \t\tif (column_active(colopts))\n \t\t\tstop_column_filter();\n \t\treturn ret;\n \t}\n-\tif (lines != -1)\n+\tif (filter.lines != -1)\n \t\tdie(_(\"-n option is only allowed with -l.\"));\n-\tif (with_commit)\n+\tif (filter.with_commit)\n \t\tdie(_(\"--contains option is only allowed with -l.\"));\n-\tif (points_at.nr)\n+\tif (filter.points_at.nr)\n \t\tdie(_(\"--points-at option is only allowed with -l.\"));\n \tif (cmdmode == 'd')\n \t\treturn for_each_tag_name(argv, delete_tag);\n-- \n2.4.6\n"},{"id":"266888","messageId":"1438065211-3777-8-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v6 08/10] tag.c: use 'ref-filter' APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:33:29Z","receivedAt":"2015-07-28T06:33:29Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nMake 'tag.c' use 'ref-filter' APIs for iterating through refs sorting\nand printing of refs. This removes most of the code used in 'tag.c'\nreplacing it with calls to the 'ref-filter' library.\n\nMake 'tag.c' use the 'filter_refs()' function provided by 'ref-filter'\nto filter out tags based on the options set.\n\nFor printing tags we use 'show_ref_array_item()' function provided by\n'ref-filter'.\n\nWe improve the sorting option provided by 'tag.c' by using the sorting\noptions provided by 'ref-filter'. This causes the test 'invalid sort\nparameter on command line' in t7004 to fail, as 'ref-filter' throws an\nerror for all sorting fields which are incorrect. The test is changed\nto reflect the same.\n\nModify 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-tag.txt |  16 ++-\n builtin/tag.c             | 342 ++++++----------------------------------------\n t/t7004-tag.sh            |   8 +-\n 3 files changed, 50 insertions(+), 316 deletions(-)\n\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 84f6496..3ac4a96 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -13,7 +13,7 @@ SYNOPSIS\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]\n-\t[--column[=<options>] | --no-column] [--create-reflog] [<pattern>...]\n+\t[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] [<pattern>...]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -94,14 +94,16 @@ OPTIONS\n \tusing fnmatch(3)).  Multiple patterns may be given; if any of\n \tthem matches, the tag is shown.\n \n---sort=<type>::\n-\tSort in a specific order. Supported type is \"refname\"\n-\t(lexicographic order), \"version:refname\" or \"v:refname\" (tag\n+--sort=<key>::\n+\tSort based on the key given.  Prefix `-` to sort in\n+\tdescending order of the value. You may use the --sort=<key> option\n+\tmultiple times, in which case the last key becomes the primary\n+\tkey. Also supports \"version:refname\" or \"v:refname\" (tag\n \tnames are treated as versions). The \"version:refname\" sort\n \torder can also be affected by the\n-\t\"versionsort.prereleaseSuffix\" configuration variable. Prepend\n-\t\"-\" to reverse sort order. When this option is not given, the\n-\tsort order defaults to the value configured for the 'tag.sort'\n+\t\"versionsort.prereleaseSuffix\" configuration variable.\n+\tThe keys supported are the same as those in `git for-each-ref`.\n+\tSort order defaults to the value configured for the 'tag.sort'\n \tvariable if it exists, or lexicographic order otherwise. See\n \tlinkgit:git-config[1].\n \ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex e96bae2..1e8d1b2 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -28,278 +28,32 @@ static const char * const git_tag_usage[] = {\n \tNULL\n };\n \n-#define STRCMP_SORT     0\t/* must be zero */\n-#define VERCMP_SORT     1\n-#define SORT_MASK       0x7fff\n-#define REVERSE_SORT    0x8000\n-\n-static int tag_sort;\n-\n static unsigned int colopts;\n \n-static int match_pattern(const char **patterns, const char *ref)\n-{\n-\t/* no pattern means match everything */\n-\tif (!*patterns)\n-\t\treturn 1;\n-\tfor (; *patterns; patterns++)\n-\t\tif (!wildmatch(*patterns, ref, 0, NULL))\n-\t\t\treturn 1;\n-\treturn 0;\n-}\n-\n-/*\n- * This is currently duplicated in ref-filter.c, and will eventually be\n- * removed as we port tag.c to use the ref-filter APIs.\n- */\n-static const unsigned char *match_points_at(const char *refname,\n-\t\t\t\t\t    const unsigned char *sha1,\n-\t\t\t\t\t    struct sha1_array *points_at)\n-{\n-\tconst unsigned char *tagged_sha1 = NULL;\n-\tstruct object *obj;\n-\n-\tif (sha1_array_lookup(points_at, sha1) >= 0)\n-\t\treturn sha1;\n-\tobj = parse_object(sha1);\n-\tif (!obj)\n-\t\tdie(_(\"malformed object at '%s'\"), refname);\n-\tif (obj->type == OBJ_TAG)\n-\t\ttagged_sha1 = ((struct tag *)obj)->tagged->sha1;\n-\tif (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)\n-\t\treturn tagged_sha1;\n-\treturn NULL;\n-}\n-\n-static int in_commit_list(const struct commit_list *want, struct commit *c)\n-{\n-\tfor (; want; want = want->next)\n-\t\tif (!hashcmp(want->item->object.sha1, c->object.sha1))\n-\t\t\treturn 1;\n-\treturn 0;\n-}\n-\n-/*\n- * The entire code segment for supporting the --contains option has been\n- * copied over to ref-filter.{c,h}. This will be deleted evetually when\n- * we port tag.c to use ref-filter APIs.\n- */\n-enum contains_result {\n-\tCONTAINS_UNKNOWN = -1,\n-\tCONTAINS_NO = 0,\n-\tCONTAINS_YES = 1\n-};\n-\n-/*\n- * Test whether the candidate or one of its parents is contained in the list.\n- * Do not recurse to find out, though, but return -1 if inconclusive.\n- */\n-static enum contains_result contains_test(struct commit *candidate,\n-\t\t\t    const struct commit_list *want)\n-{\n-\t/* was it previously marked as containing a want commit? */\n-\tif (candidate->object.flags & TMP_MARK)\n-\t\treturn 1;\n-\t/* or marked as not possibly containing a want commit? */\n-\tif (candidate->object.flags & UNINTERESTING)\n-\t\treturn 0;\n-\t/* or are we it? */\n-\tif (in_commit_list(want, candidate)) {\n-\t\tcandidate->object.flags |= TMP_MARK;\n-\t\treturn 1;\n-\t}\n-\n-\tif (parse_commit(candidate) < 0)\n-\t\treturn 0;\n-\n-\treturn -1;\n-}\n-\n-/*\n- * Mimicking the real stack, this stack lives on the heap, avoiding stack\n- * overflows.\n- *\n- * At each recursion step, the stack items points to the commits whose\n- * ancestors are to be inspected.\n- */\n-struct stack {\n-\tint nr, alloc;\n-\tstruct stack_entry {\n-\t\tstruct commit *commit;\n-\t\tstruct commit_list *parents;\n-\t} *stack;\n-};\n-\n-static void push_to_stack(struct commit *candidate, struct stack *stack)\n-{\n-\tint index = stack->nr++;\n-\tALLOC_GROW(stack->stack, stack->nr, stack->alloc);\n-\tstack->stack[index].commit = candidate;\n-\tstack->stack[index].parents = candidate->parents;\n-}\n-\n-static enum contains_result contains(struct commit *candidate,\n-\t\tconst struct commit_list *want)\n-{\n-\tstruct stack stack = { 0, 0, NULL };\n-\tint result = contains_test(candidate, want);\n-\n-\tif (result != CONTAINS_UNKNOWN)\n-\t\treturn result;\n-\n-\tpush_to_stack(candidate, &stack);\n-\twhile (stack.nr) {\n-\t\tstruct stack_entry *entry = &stack.stack[stack.nr - 1];\n-\t\tstruct commit *commit = entry->commit;\n-\t\tstruct commit_list *parents = entry->parents;\n-\n-\t\tif (!parents) {\n-\t\t\tcommit->object.flags |= UNINTERESTING;\n-\t\t\tstack.nr--;\n-\t\t}\n-\t\t/*\n-\t\t * If we just popped the stack, parents->item has been marked,\n-\t\t * therefore contains_test will return a meaningful 0 or 1.\n-\t\t */\n-\t\telse switch (contains_test(parents->item, want)) {\n-\t\tcase CONTAINS_YES:\n-\t\t\tcommit->object.flags |= TMP_MARK;\n-\t\t\tstack.nr--;\n-\t\t\tbreak;\n-\t\tcase CONTAINS_NO:\n-\t\t\tentry->parents = parents->next;\n-\t\t\tbreak;\n-\t\tcase CONTAINS_UNKNOWN:\n-\t\t\tpush_to_stack(parents->item, &stack);\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\tfree(stack.stack);\n-\treturn contains_test(candidate, want);\n-}\n-\n-/*\n- * Currently duplicated in ref-filter, will eventually be removed as\n- * we port tag.c to use ref-filter APIs.\n- */\n-static void show_tag_lines(const struct object_id *oid, int lines)\n-{\n-\tint i;\n-\tunsigned long size;\n-\tenum object_type type;\n-\tchar *buf, *sp, *eol;\n-\tsize_t len;\n-\n-\tbuf = read_sha1_file(oid->hash, &type, &size);\n-\tif (!buf)\n-\t\tdie_errno(\"unable to read object %s\", oid_to_hex(oid));\n-\tif (type != OBJ_COMMIT && type != OBJ_TAG)\n-\t\tgoto free_return;\n-\tif (!size)\n-\t\tdie(\"an empty %s object %s?\",\n-\t\t    typename(type), oid_to_hex(oid));\n-\n-\t/* skip header */\n-\tsp = strstr(buf, \"\\n\\n\");\n-\tif (!sp)\n-\t\tgoto free_return;\n-\n-\t/* only take up to \"lines\" lines, and strip the signature from a tag */\n-\tif (type == OBJ_TAG)\n-\t\tsize = parse_signature(buf, size);\n-\tfor (i = 0, sp += 2; i < lines && sp < buf + size; i++) {\n-\t\tif (i)\n-\t\t\tprintf(\"\\n    \");\n-\t\teol = memchr(sp, '\\n', size - (sp - buf));\n-\t\tlen = eol ? eol - sp : size - (sp - buf);\n-\t\tfwrite(sp, len, 1, stdout);\n-\t\tif (!eol)\n-\t\t\tbreak;\n-\t\tsp = eol + 1;\n-\t}\n-free_return:\n-\tfree(buf);\n-}\n-\n-static void ref_array_append(struct ref_array *array, const char *refname)\n-{\n-\tsize_t len = strlen(refname);\n-\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);\n-\tmemcpy(ref->refname, refname, len);\n-\tref->refname[len] = '\\0';\n-\tREALLOC_ARRAY(array->items, array->nr + 1);\n-\tarray->items[array->nr++] = ref;\n-}\n-\n-static int show_reference(const char *refname, const struct object_id *oid,\n-\t\t\t  int flag, void *cb_data)\n-{\n-\tstruct ref_filter_cbdata *data = cb_data;\n-\tstruct ref_array *array = data->array;\n-\tstruct ref_filter *filter = data->filter;\n-\n-\tif (match_pattern(filter->name_patterns, refname)) {\n-\t\tif (filter->with_commit) {\n-\t\t\tstruct commit *commit;\n-\n-\t\t\tcommit = lookup_commit_reference_gently(oid->hash, 1);\n-\t\t\tif (!commit)\n-\t\t\t\treturn 0;\n-\t\t\tif (!contains(commit, filter->with_commit))\n-\t\t\t\treturn 0;\n-\t\t}\n-\n-\t\tif (filter->points_at.nr && !match_points_at(refname, oid->hash, &filter->points_at))\n-\t\t\treturn 0;\n-\n-\t\tif (!filter->lines) {\n-\t\t\tif (tag_sort)\n-\t\t\t\tref_array_append(array, refname);\n-\t\t\telse\n-\t\t\t\tprintf(\"%s\\n\", refname);\n-\t\t\treturn 0;\n-\t\t}\n-\t\tprintf(\"%-15s \", refname);\n-\t\tshow_tag_lines(oid, filter->lines);\n-\t\tputchar('\\n');\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int sort_by_version(const void *a_, const void *b_)\n-{\n-\tconst struct ref_array_item *a = *((struct ref_array_item **)a_);\n-\tconst struct ref_array_item *b = *((struct ref_array_item **)b_);\n-\treturn versioncmp(a->refname, b->refname);\n-}\n-\n-static int list_tags(struct ref_filter *filter, int sort)\n+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)\n {\n \tstruct ref_array array;\n-\tstruct ref_filter_cbdata data;\n+\tchar *format;\n+\tint i;\n \n \tmemset(&array, 0, sizeof(array));\n-\tdata.array = &array;\n-\tdata.filter = filter;\n \n \tif (filter->lines == -1)\n \t\tfilter->lines = 0;\n \n-\tfor_each_tag_ref(show_reference, &data);\n-\tif (sort) {\n-\t\tint i;\n-\t\tif ((sort & SORT_MASK) == VERCMP_SORT)\n-\t\t\tqsort(array.items, array.nr,\n-\t\t\t      sizeof(struct ref_array_item *), sort_by_version);\n-\t\tif (sort & REVERSE_SORT)\n-\t\t\tfor (i = array.nr - 1; i >= 0; i--)\n-\t\t\t\tprintf(\"%s\\n\", array.items[i]->refname);\n-\t\telse\n-\t\t\tfor (i = 0; i < array.nr; i++)\n-\t\t\t\tprintf(\"%s\\n\", array.items[i]->refname);\n-\t\tref_array_clear(&array);\n-\t}\n+\tif (filter->lines)\n+\t\tformat = \"%(padright:16)%(refname:short)\";\n+\telse\n+\t\tformat = \"%(refname:short)\";\n+\n+\tverify_ref_format(format);\n+\tfilter_refs(&array, filter, FILTER_REFS_TAGS);\n+\tref_array_sort(sorting, &array);\n+\n+\tfor (i = 0; i < array.nr; i++)\n+\t\tshow_ref_array_item(array.items[i], format, QUOTE_NONE, filter->lines);\n+\tref_array_clear(&array);\n+\n \treturn 0;\n }\n \n@@ -366,35 +120,26 @@ static const char tag_template_nocleanup[] =\n \t\"Lines starting with '%c' will be kept; you may remove them\"\n \t\" yourself if you want to.\\n\");\n \n-/*\n- * Parse a sort string, and return 0 if parsed successfully. Will return\n- * non-zero when the sort string does not parse into a known type. If var is\n- * given, the error message becomes a warning and includes information about\n- * the configuration value.\n- */\n-static int parse_sort_string(const char *var, const char *arg, int *sort)\n+/* Parse arg given and add it the ref_sorting array */\n+static int parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)\n {\n-\tint type = 0, flags = 0;\n-\n-\tif (skip_prefix(arg, \"-\", &arg))\n-\t\tflags |= REVERSE_SORT;\n+\tstruct ref_sorting *s;\n+\tint len;\n \n-\tif (skip_prefix(arg, \"version:\", &arg) || skip_prefix(arg, \"v:\", &arg))\n-\t\ttype = VERCMP_SORT;\n-\telse\n-\t\ttype = STRCMP_SORT;\n+\ts = xcalloc(1, sizeof(*s));\n+\ts->next = *sorting_tail;\n+\t*sorting_tail = s;\n \n-\tif (strcmp(arg, \"refname\")) {\n-\t\tif (!var)\n-\t\t\treturn error(_(\"unsupported sort specification '%s'\"), arg);\n-\t\telse {\n-\t\t\twarning(_(\"unsupported sort specification '%s' in variable '%s'\"),\n-\t\t\t\tvar, arg);\n-\t\t\treturn -1;\n-\t\t}\n+\tif (*arg == '-') {\n+\t\ts->reverse = 1;\n+\t\targ++;\n \t}\n+\tif (skip_prefix(arg, \"version:\", &arg) ||\n+\t    skip_prefix(arg, \"v:\", &arg))\n+\t\ts->version = 1;\n \n-\t*sort = (type | flags);\n+\tlen = strlen(arg);\n+\ts->atom = parse_ref_filter_atom(arg, arg+len);\n \n \treturn 0;\n }\n@@ -402,11 +147,12 @@ static int parse_sort_string(const char *var, const char *arg, int *sort)\n static int git_tag_config(const char *var, const char *value, void *cb)\n {\n \tint status;\n+\tstruct ref_sorting **sorting_tail = (struct ref_sorting **)cb;\n \n \tif (!strcmp(var, \"tag.sort\")) {\n \t\tif (!value)\n \t\t\treturn config_error_nonbool(var);\n-\t\tparse_sort_string(var, value, &tag_sort);\n+\t\tparse_sorting_string(value, sorting_tail);\n \t\treturn 0;\n \t}\n \n@@ -564,13 +310,6 @@ static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)\n \treturn check_refname_format(sb->buf, 0);\n }\n \n-static int parse_opt_sort(const struct option *opt, const char *arg, int unset)\n-{\n-\tint *sort = opt->value;\n-\n-\treturn parse_sort_string(NULL, arg, sort);\n-}\n-\n int cmd_tag(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -587,6 +326,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_filter filter;\n+\tstatic struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;\n \tstruct option options[] = {\n \t\tOPT_CMDMODE('l', \"list\", &cmdmode, N_(\"list tag names\"), 'l'),\n \t\t{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_(\"n\"),\n@@ -613,10 +353,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show tag list in columns\")),\n \t\tOPT_CONTAINS(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n \t\tOPT_WITH(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n-\t\t{\n-\t\t\tOPTION_CALLBACK, 0, \"sort\", &tag_sort, N_(\"type\"), N_(\"sort tags\"),\n-\t\t\tPARSE_OPT_NONEG, parse_opt_sort\n-\t\t},\n+\t\tOPT_CALLBACK(0 , \"sort\", sorting_tail, N_(\"key\"),\n+\t\t\t     N_(\"field name to sort on\"), &parse_opt_ref_sorting),\n \t\t{\n \t\t\tOPTION_CALLBACK, 0, \"points-at\", &filter.points_at, N_(\"object\"),\n \t\t\tN_(\"print only tags of the object\"), 0, parse_opt_object_name\n@@ -624,7 +362,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_tag_config, NULL);\n+\tgit_config(git_tag_config, sorting_tail);\n \n \tmemset(&opt, 0, sizeof(opt));\n \tmemset(&filter, 0, sizeof(filter));\n@@ -650,6 +388,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tdie(_(\"--column and -n are incompatible\"));\n \t\tcolopts = 0;\n \t}\n+\tif (!sorting)\n+\t\tsorting = ref_default_sorting();\n \tif (cmdmode == 'l') {\n \t\tint ret;\n \t\tif (column_active(colopts)) {\n@@ -658,10 +398,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tcopts.padding = 2;\n \t\t\trun_column_filter(colopts, &copts);\n \t\t}\n-\t\tif (filter.lines != -1 && tag_sort)\n-\t\t\tdie(_(\"--sort and -n are incompatible\"));\n \t\tfilter.name_patterns = argv;\n-\t\tret = list_tags(&filter, tag_sort);\n+\t\tret = list_tags(&filter, sorting);\n \t\tif (column_active(colopts))\n \t\t\tstop_column_filter();\n \t\treturn ret;\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex d31788c..1f066aa 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -1462,13 +1462,7 @@ test_expect_success 'invalid sort parameter on command line' '\n \n test_expect_success 'invalid sort parameter in configuratoin' '\n \tgit config tag.sort \"v:notvalid\" &&\n-\tgit tag -l \"foo*\" >actual &&\n-\tcat >expect <<-\\EOF &&\n-\tfoo1.10\n-\tfoo1.3\n-\tfoo1.6\n-\tEOF\n-\ttest_cmp expect actual\n+\ttest_must_fail git tag -l \"foo*\" >actual\n '\n \n test_expect_success 'version sort with prerelease reordering' '\n-- \n2.4.6\n"},{"id":"266887","messageId":"1438065211-3777-9-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v6 09/10] tag.c: implement '--format' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:33:30Z","receivedAt":"2015-07-28T06:33:30Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nImplement the '--format' option provided by 'ref-filter'.\nThis lets the user list tags as per desired format similar\nto the implementation in '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-tag.txt | 15 ++++++++++++++-\n builtin/tag.c             | 11 +++++++----\n t/t7004-tag.sh            | 16 ++++++++++++++++\n 3 files changed, 37 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 3ac4a96..75703c5 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -13,7 +13,8 @@ SYNOPSIS\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]\n-\t[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] [<pattern>...]\n+\t[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]\n+\t[--format=<format>] [<pattern>...]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -158,6 +159,18 @@ This option is only applicable when listing tags without annotation lines.\n \tThe object that the new tag will refer to, usually a commit.\n \tDefaults to HEAD.\n \n+<format>::\n+\tA string that interpolates `%(fieldname)` from the object\n+\tpointed at by a ref being shown.  If `fieldname` is prefixed\n+\twith an asterisk (`*`) and the ref points at a tag object, the\n+\tvalue for the field in the object tag refers is used.  When\n+\tunspecified, defaults to `%(refname:short)`.  It also\n+\tinterpolates `%%` to `%`, and `%xx` where `xx` are hex digits\n+\tinterpolates to character with hex code `xx`; for example\n+\t`%00` interpolates to `\\0` (NUL), `%09` to `\\t` (TAB) and\n+\t`%0a` to `\\n` (LF).  The fields are same as those in `git\n+\tfor-each-ref`.\n+\n \n CONFIGURATION\n -------------\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 1e8d1b2..7de49c4 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -30,10 +30,9 @@ static const char * const git_tag_usage[] = {\n \n static unsigned int colopts;\n \n-static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)\n+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)\n {\n \tstruct ref_array array;\n-\tchar *format;\n \tint i;\n \n \tmemset(&array, 0, sizeof(array));\n@@ -43,7 +42,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)\n \n \tif (filter->lines)\n \t\tformat = \"%(padright:16)%(refname:short)\";\n-\telse\n+\telse if (!format)\n \t\tformat = \"%(refname:short)\";\n \n \tverify_ref_format(format);\n@@ -327,6 +326,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_filter filter;\n \tstatic struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;\n+\tconst char *format = NULL;\n \tstruct option options[] = {\n \t\tOPT_CMDMODE('l', \"list\", &cmdmode, N_(\"list tag names\"), 'l'),\n \t\t{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_(\"n\"),\n@@ -359,6 +359,7 @@ int cmd_tag(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 tags 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@@ -398,8 +399,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tcopts.padding = 2;\n \t\t\trun_column_filter(colopts, &copts);\n \t\t}\n+\t\tif (format && (filter.lines != -1))\n+\t\t\tdie(_(\"--format and -n are incompatible\"));\n \t\tfilter.name_patterns = argv;\n-\t\tret = list_tags(&filter, sorting);\n+\t\tret = list_tags(&filter, sorting, format);\n \t\tif (column_active(colopts))\n \t\t\tstop_column_filter();\n \t\treturn ret;\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex 1f066aa..1809011 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -1519,4 +1519,20 @@ EOF\"\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--format cannot be used with -n' '\n+\ttest_must_fail git tag -l -n4 --format=\"%(refname)\"\n+'\n+\n+test_expect_success '--format should list tags as per format given' '\n+\tcat >expect <<-\\EOF &&\n+\trefname : refs/tags/foo1.10\n+\trefname : refs/tags/foo1.3\n+\trefname : refs/tags/foo1.6\n+\trefname : refs/tags/foo1.6-rc1\n+\trefname : refs/tags/foo1.6-rc2\n+\tEOF\n+\tgit tag -l --format=\"refname : %(refname)\" \"foo*\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.6\n"},{"id":"266889","messageId":"1438065211-3777-10-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v6 10/10] tag.c: implement '--merged' and '--no-merged' options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-28T06:33:31Z","receivedAt":"2015-07-28T06:33:31Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nUsing 'ref-filter' APIs implement the '--merged' and '--no-merged'\noptions into 'tag.c'. The '--merged' option lets the user to only\nlist tags merged into the named commit. The '--no-merged' option\nlets the user to only list tags not merged into the named commit.\nIf no object is provided it assumes HEAD as the object.\n\nAdd documentation and tests 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-tag.txt |  7 ++++++-\n builtin/tag.c             |  6 +++++-\n t/t7004-tag.sh            | 27 +++++++++++++++++++++++++++\n 3 files changed, 38 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 75703c5..c2785d9 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -14,7 +14,7 @@ SYNOPSIS\n 'git tag' -d <tagname>...\n 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]\n \t[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]\n-\t[--format=<format>] [<pattern>...]\n+\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -171,6 +171,11 @@ This option is only applicable when listing tags without annotation lines.\n \t`%0a` to `\\n` (LF).  The fields are same as those in `git\n \tfor-each-ref`.\n \n+--[no-]merged [<commit>]::\n+\tOnly list tags whose tips are reachable, or not reachable\n+\tif '--no-merged' is used, from the specified commit ('HEAD'\n+\tif not specified).\n+\n \n CONFIGURATION\n -------------\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 7de49c4..fc01117 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -23,7 +23,7 @@ static const char * const git_tag_usage[] = {\n \tN_(\"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]\"),\n \tN_(\"git tag -d <tagname>...\"),\n \tN_(\"git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]\"\n-\t\t\"\\n\\t\\t[<pattern>...]\"),\n+\t\t\"\\n\\t\\t[--[no-]merged [<commit>]] [<pattern>...]\"),\n \tN_(\"git tag -v <tagname>...\"),\n \tNULL\n };\n@@ -353,6 +353,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show tag list in columns\")),\n \t\tOPT_CONTAINS(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n \t\tOPT_WITH(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n+\t\tOPT_MERGED(&filter, N_(\"print only tags that are merged\")),\n+\t\tOPT_NO_MERGED(&filter, N_(\"print only tags that are not merged\")),\n \t\tOPT_CALLBACK(0 , \"sort\", sorting_tail, N_(\"key\"),\n \t\t\t     N_(\"field name to sort on\"), &parse_opt_ref_sorting),\n \t\t{\n@@ -413,6 +415,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"--contains option is only allowed with -l.\"));\n \tif (filter.points_at.nr)\n \t\tdie(_(\"--points-at option is only allowed with -l.\"));\n+\tif (filter.merge_commit)\n+\t\tdie(_(\"--merged and --no-merged option are only allowed with -l\"));\n \tif (cmdmode == 'd')\n \t\treturn for_each_tag_name(argv, delete_tag);\n \tif (cmdmode == 'v')\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex 1809011..5b73539 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -1535,4 +1535,31 @@ test_expect_success '--format should list tags as per format given' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'setup --merged test tags' '\n+\tgit tag mergetest-1 HEAD~2 &&\n+\tgit tag mergetest-2 HEAD~1 &&\n+\tgit tag mergetest-3 HEAD\n+'\n+\n+test_expect_success '--merged cannot be used in non-list mode' '\n+\ttest_must_fail git tag --merged=mergetest-2 foo\n+'\n+\n+test_expect_success '--merged shows merged tags' '\n+\tcat >expect <<-\\EOF &&\n+\tmergetest-1\n+\tmergetest-2\n+\tEOF\n+\tgit tag -l --merged=mergetest-2 mergetest-* >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--no-merged show unmerged tags' '\n+\tcat >expect <<-\\EOF &&\n+\tmergetest-3\n+\tEOF\n+\tgit tag -l --no-merged=mergetest-2 mergetest-* >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.6\n"},{"id":"266903","messageId":"vpqwpxk215y.fsf@anie.imag.fr","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-07-28T07:26:17Z","receivedAt":"2015-07-28T07:26:17Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> -static void print_value(struct atom_value *v, int quote_style)\n> +static void apply_formatting_state(struct ref_formatting_state *state,\n> +\t\t\t\t   struct atom_value *v, struct strbuf *value)\n>  {\n> -\tstruct strbuf sb = STRBUF_INIT;\n> -\tswitch (quote_style) {\n> +\t/*  Eventually we'll formatt based on the ref_formatting_state */\n\ns/formatt/format/\n\nAlso, we usually use a single space after /*.\n\n(Neither is very important since it disapears in the next commit)\n\n> +\t/*\n> +\t * Some (pesudo) atoms have no immediate side effect, but only\n\ns/pesudo/pseudo/. But if you are to rename these atoms to \"modifier\natoms\", you should get rid of this \"pseudo\" here.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"267062","messageId":"CAOLa=ZS=bMnVDjGL0vb08t2XCTqH7tG9+QuPyz3D9wb7hCbpaA@mail.gmail.com","threadId":"39951","inReplyTo":"vpqwpxk215y.fsf@anie.imag.fr","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-29T15:56:55Z","receivedAt":"2015-07-29T15:56:55Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Tue, Jul 28, 2015 at 12:56 PM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> -static void print_value(struct atom_value *v, int quote_style)\n>> +static void apply_formatting_state(struct ref_formatting_state *state,\n>> +                                struct atom_value *v, struct strbuf *value)\n>>  {\n>> -     struct strbuf sb = STRBUF_INIT;\n>> -     switch (quote_style) {\n>> +     /*  Eventually we'll formatt based on the ref_formatting_state */\n>\n> s/formatt/format/\n>\n> Also, we usually use a single space after /*.\n>\n> (Neither is very important since it disapears in the next commit)\n>\n>> +     /*\n>> +      * Some (pesudo) atoms have no immediate side effect, but only\n>\n> s/pesudo/pseudo/. But if you are to rename these atoms to \"modifier\n> atoms\", you should get rid of this \"pseudo\" here.\n>\n\nAh, I hate making grammatical errors, Even though I check it always gets away.\nAnyways waiting for Junio and others to reply on this version. Could do a resend\nfor this series if needed.\n\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267065","messageId":"vpqio93eyqs.fsf@anie.imag.fr","threadId":"39951","inReplyTo":"CAOLa=ZS=bMnVDjGL0vb08t2XCTqH7tG9+QuPyz3D9wb7hCbpaA@mail.gmail.com","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-07-29T16:04:43Z","receivedAt":"2015-07-29T16:04:43Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> Ah, I hate making grammatical errors, Even though I check it always gets away.\n> Anyways waiting for Junio and others to reply on this version. Could do a resend\n> for this series if needed.\n\nIf you took all my remarks into account, I think it's worth a resend as\nit should make it easier for new reviewers.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"267066","messageId":"CAOLa=ZQfmiYGgnPmLrhHzQGFiPOFZ=76dpR5dPJ-gJZZgOEc8g@mail.gmail.com","threadId":"39951","inReplyTo":"vpqio93eyqs.fsf@anie.imag.fr","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-29T16:10:23Z","receivedAt":"2015-07-29T16:10:23Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Wed, Jul 29, 2015 at 9:34 PM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> Ah, I hate making grammatical errors, Even though I check it always gets away.\n>> Anyways waiting for Junio and others to reply on this version. Could do a resend\n>> for this series if needed.\n>\n> If you took all my remarks into account, I think it's worth a resend as\n> it should make it easier for new reviewers.\n>\n\nCorrect me If I missed something, but the only remark for this series\nwere the ones mentioned\nabove right? I mean branch.c being an RFC patch series and separate from this.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267070","messageId":"vpqsi86exba.fsf@anie.imag.fr","threadId":"39951","inReplyTo":"CAOLa=ZQfmiYGgnPmLrhHzQGFiPOFZ=76dpR5dPJ-gJZZgOEc8g@mail.gmail.com","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-07-29T16:35:37Z","receivedAt":"2015-07-29T16:35:37Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> On Wed, Jul 29, 2015 at 9:34 PM, Matthieu Moy\n> <Matthieu.Moy@grenoble-inp.fr> wrote:\n>> Karthik Nayak <karthik.188@gmail.com> writes:\n>>\n>>> Ah, I hate making grammatical errors, Even though I check it always gets away.\n>>> Anyways waiting for Junio and others to reply on this version. Could do a resend\n>>> for this series if needed.\n>>\n>> If you took all my remarks into account, I think it's worth a resend as\n>> it should make it easier for new reviewers.\n>>\n>\n> Correct me If I missed something, but the only remark for this series\n> were the ones mentioned\n> above right?\n\nOops, indeed, I did not realize we were in the other series.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"267092","messageId":"CAPig+cS+w8ECma--ncJDoN1fEgrFZMvBC8GBgU6+tLYm_oGkaw@mail.gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-29T19:19:36Z","receivedAt":"2015-07-29T19:19:36Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tuesday, July 28, 2015, Karthik Nayak <karthik.188@gmail.com> wrote:\n> Introduce 'ref_formatting' structure to hold values of pseudo atoms\n> which help only in formatting. This will eventually be used by atoms\n> like `color` and the `padright` atom which will be introduced in a\n> later patch.\n\nIsn't this commit message outdated now that you no longer treat color\nspecially and since the terminology is changing from \"pseudo\" to\n\"modifier\"? Also, isn't the structure now called\n'ref_formatting_state' rather than 'ref_formatting'?\n\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n> diff --git a/ref-filter.c b/ref-filter.c\n> index 7561727..a919a14 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -620,7 +622,7 @@ static void populate_value(struct ref_array_item *ref)\n>                 const char *name = used_atom[i];\n>                 struct atom_value *v = &ref->value[i];\n>                 int deref = 0;\n> -               const char *refname;\n> +               const char *refname = NULL;\n\nWhat is this change about? It doesn't seem to be related to anything\nelse in the patch.\n\n>                 const char *formatp;\n>                 struct branch *branch = NULL;\n>\n> @@ -1190,30 +1192,47 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n> +static void print_value(struct atom_value *v, struct ref_formatting_state *state)\n> +{\n> +       struct strbuf value = STRBUF_INIT;\n> +       struct strbuf formatted = STRBUF_INIT;\n> +\n> +       /*\n> +        * Some (pesudo) atoms have no immediate side effect, but only\n> +        * affect the next atom. Store the relevant information from\n> +        * these atoms in the 'state' variable for use when displaying\n> +        * the next atom.\n> +        */\n> +       apply_formatting_state(state, v, &value);\n\nThe comment says that this is \"storing\" formatting state, however, the\ncode is actually \"applying\" the state. You could move this comment\ndown to show_ref_array_item() where formatting state actually gets\nstored. Or you could fix it to talk about \"applying\" the state.\nHowever, now that apply_formatting_state() has a meaningful name, you\ncould also drop the comment altogether since it doesn't say much\nbeyond what is said already by the function name.\n\n> +       switch (state->quote_style) {\n>         case QUOTE_NONE:\n> -               fputs(v->s, stdout);\n> @@ -1254,9 +1273,26 @@ static void emit(const char *cp, const char *ep)\n> +static void reset_formatting_state(struct ref_formatting_state *state)\n> +{\n> +       int quote_style = state->quote_style;\n> +       memset(state, 0, sizeof(*state));\n> +       state->quote_style = quote_style;\n\nI wonder if this sledge-hammer approach of saving one or two values\nbefore clearing the entire 'ref_formatting_state' and then restoring\nthe saved values will scale well. Would it be better for this to just\nindividually reset the fields which need resetting and not touch those\nthat don't?\n\nAlso, the fact that quote_style has to be handled specially may be an\nindication that it doesn't belong in this structure grouped with the\nother modifiers or that you need better classification within the\nstructure. For instance:\n\n    struct ref_formatting_state {\n        struct global {\n            int quote_style;\n        };\n        struct local {\n            int pad_right;\n        };\n\nwhere 'local' state gets reset by reset_formatting_state(), and\n'global' is left alone.\n\nThat's just one idea, not necessarily a proposal, but is something to\nthink about since the current arrangement is kind of yucky.\n\n> +}\n> +\n>  void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n>  {\n>         const char *cp, *sp, *ep;\n> +       struct ref_formatting_state state;\n> +\n> +       memset(&state, 0, sizeof(state));\n> +       state.quote_style = quote_style;\n\nIt's a little bit ugly to use memset() here when you have\nreset_formatting_state() available. You could set quote_style first,\nand then call reset_formatting_state() rather than memset(). Or,\nperhaps, change reset_formatting_state(), as described above, to stop\nusing the sledge-hammer approach.\n\n>         for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n>                 struct atom_value *atomv;\n"},{"id":"267093","messageId":"CAPig+cQv4BWFwgx7mDHh=gNpxbEeR7auw4os-tOpf3cGah=zpw@mail.gmail.com","threadId":"39951","inReplyTo":"CAOLa=ZR6_2NBB4v0Ynq391=8Jk2RZON6R0YG=HKUNwKx249b7Q@mail.gmail.com","subject":"Re: [PATCH v6 0/10] port tag.c to use ref-filter APIs","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-29T19:27:21Z","receivedAt":"2015-07-29T19:27:21Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tuesday, July 28, 2015, Karthik Nayak <karthik.188@gmail.com> wrote:\n> Changes in v6:\n> * Change the flow of commits, introduce rest_formatting_state.\n> * Rename\n> ref_formatting -> apply_formatting_state\n> apply_pseudo_state -> store_formatting_state\n> * Remove the patch for making color a pseudo atom, and rename\n> pseudo_atom to modifier_atom.\n> * Small grammatical changes.\n>\n> Side Note: --format=\"%(padright:X)\" applies to the next available atom\n> and not to the next span. I find this more accurate as I don't see why\n> we'd want to pad something of known length. But its up for discussion\n\nThis isn't supported by the current %(padright:) syntax, but an\nexample would be if someone wants to pad a string composed of atoms\nand literal strings. For instance, the user might want to right-pad\nthe composed string \"%(refattribute1) glorked %(refattribute2)”.\n\nOne possible syntax to support this sort of thing would be\n%(padright:n:content). For instance, using the example above:\n\n    %(padright:20:%(refattribute1) glorked %(refattribute2))\n\nAnother would be %(padright:n)content%(end). The %(end) token could\nwork with other formatting directives, such as left padding,\nalignment, etc. For instance:\n\n    %(padright:20)%(refattribute1) glorked %(refattribute2)%(end)\n\nIn fact, this sort of thing has come up multiple times, with the most\ngeneral being actual embedding of a full-blown scripting language to\nsupport pretty much any desired formatting. Other less ambitious\nproposals have also been suggested.\n\nThis isn't a suggestion that you should implement any of these, but\njust a heads-up that, when it comes to formatting, it's not at all\nuncommon for people to come up with needs not anticipated by the\ndesign.\n"},{"id":"267094","messageId":"CAPig+cS4zVT2=kuV3yc860vp4fKrqzvM_phyNngSUho9zTzStA@mail.gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-2-git-send-email-Karthik.188@gmail.com","subject":"Re: [PATCH v6 02/10] ref-filter: add option to pad atoms to the right","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-29T19:29:25Z","receivedAt":"2015-07-29T19:29:25Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tuesday, July 28, 2015, Karthik Nayak <karthik.188@gmail.com> wrote:\n> Add a new atom \"padright\" and support %(padright:X) where X is a\n> number.  This will align the succeeding atom value to the left\n> followed by spaces for a total length of X characters. If X is less\n> than the item size, the entire atom value is printed.\n>\n> Add tests and documentation for the same.\n>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n> diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\n> index 505a360..19ac480 100755\n> --- a/t/t6302-for-each-ref-filter.sh\n> +++ b/t/t6302-for-each-ref-filter.sh\n> @@ -81,4 +81,20 @@ test_expect_success 'filtering with --contains' '\n>         test_cmp expect actual\n>  '\n>\n> +test_expect_success 'padding to the right using `padright`' '\n> +       cat >expect <<-\\EOF &&\n> +       refs/heads/master|refs/heads/master        |refs/heads/master|\n> +       refs/heads/side|refs/heads/side          |refs/heads/side|\n> +       refs/odd/spot|refs/odd/spot            |refs/odd/spot|\n> +       refs/tags/double-tag|refs/tags/double-tag     |refs/tags/double-tag|\n> +       refs/tags/four|refs/tags/four           |refs/tags/four|\n> +       refs/tags/one|refs/tags/one            |refs/tags/one|\n> +       refs/tags/signed-tag|refs/tags/signed-tag     |refs/tags/signed-tag|\n> +       refs/tags/three|refs/tags/three          |refs/tags/three|\n> +       refs/tags/two|refs/tags/two            |refs/tags/two|\n> +       EOF\n> +       git for-each-ref --format=\"%(refname)%(padright:25)|%(refname)|%(refname)|\" >actual &&\n\nThis fails to test the important case when the atom length is greater\nthan the padright value (in which case no padding should be done, and\nthe atom text should extend beyond the 'padright' column).\n\n> +       test_cmp expect actual\n> +'\n> +\n>  test_done\n> --\n> 2.4.6\n"},{"id":"267095","messageId":"CAPig+cSZzA+3BaP78VhQ3JjYbW_Y+KRhQ6c4-SPuxysKmL8bZw@mail.gmail.com","threadId":"39951","inReplyTo":"1438065211-3777-5-git-send-email-Karthik.188@gmail.com","subject":"Re: [PATCH v6 05/10] ref-filter: add support to sort by version","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-07-29T19:34:10Z","receivedAt":"2015-07-29T19:34:10Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tuesday, July 28, 2015, Karthik Nayak <karthik.188@gmail.com> wrote:\n> Add support to sort by version using the \"v:refname\" and\n> \"version:refname\" option. This is achieved by using the 'versioncmp()'\n> function as the comparing function for qsort.\n>\n> This option is included to support sorting by versions in `git tag -l`\n> which will eventaully be ported to use ref-filter APIs.\n>\n> Add documentation and tests for the same.\n>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index 45dd7f8..2b60aee 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -151,6 +151,9 @@ For sorting purposes, fields with numeric values sort in numeric\n>  order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).\n>  All other fields are used to sort in their byte-value order.\n>\n> +There is also an option to sort by versions, this can be done by using\n> +the fieldname `version:refname` or in short `v:refname`.\n\nNit: \"or in short\" reads a bit oddly. Perhaps:\n\n    ...`version:refname` or its alias `v:refname`.\n\nor\n\n    ...`version:refname` or the short-form `v:refname`.\n\n(I rather prefer the \"alias\" alternative.)\n\n>  In any case, a field name that refers to a field inapplicable to\n>  the object referred by the ref does not cause an error.  It\n>  returns an empty string instead.\n> diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\n> index 19ac480..68688a9 100755\n> --- a/t/t6302-for-each-ref-filter.sh\n> +++ b/t/t6302-for-each-ref-filter.sh\n> @@ -97,4 +97,40 @@ test_expect_success 'padding to the right using `padright`' '\n> +test_expect_success 'version sort' '\n> +       git for-each-ref --sort=version:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n> +       cat >expect <<-\\EOF &&\n> +       foo1.3\n> +       foo1.6\n> +       foo1.10\n> +       EOF\n> +       test_cmp expect actual\n> +'\n> +\n> +test_expect_success 'version sort (shortened)' '\n> +       git for-each-ref --sort=v:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n> +       cat >expect <<-\\EOF &&\n> +       foo1.3\n> +       foo1.6\n> +       foo1.10\n> +       EOF\n> +       test_cmp expect actual\n\nNit: In the earlier review when I suggested using \"v:refname\" for one\nof the tests in order to exercise it (in addition to\n\"version:refname\"), I didn't mean that you had to add another\n(identical) test but rather that you could have one of the existing\ntests use \"v:refname\". (Not a big deal. You can leave this as is if\nyou like. I just wanted to clarify.)\n\n> +'\n> +\n> +test_expect_success 'reverse version sort' '\n> +       git for-each-ref --sort=-version:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n> +       cat >expect <<-\\EOF &&\n> +       foo1.10\n> +       foo1.6\n> +       foo1.3\n> +       EOF\n> +       test_cmp expect actual\n> +'\n> +\n>  test_done\n> --\n> 2.4.6\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"267097","messageId":"xmqqh9om920w.fsf@gitster.dls.corp.google.com","threadId":"39951","inReplyTo":"CAPig+cS+w8ECma--ncJDoN1fEgrFZMvBC8GBgU6+tLYm_oGkaw@mail.gmail.com","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-07-29T19:50:23Z","receivedAt":"2015-07-29T19:50:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> @@ -1254,9 +1273,26 @@ static void emit(const char *cp, const char *ep)\n>> +static void reset_formatting_state(struct ref_formatting_state *state)\n>> +{\n>> +       int quote_style = state->quote_style;\n>> +       memset(state, 0, sizeof(*state));\n>> +       state->quote_style = quote_style;\n>\n> I wonder if this sledge-hammer approach of saving one or two values\n> before clearing the entire 'ref_formatting_state' and then restoring\n> the saved values will scale well. Would it be better for this to just\n> individually reset the fields which need resetting and not touch those\n> that don't?\n>\n> Also, the fact that quote_style has to be handled specially may be an\n> indication that it doesn't belong in this structure grouped with the\n> other modifiers or that you need better classification within the\n> structure.\n\nActually, I think it is wrong to have this function in the first\nplace.  It is a sign that the caller is doing too little before\ncalling this function.\n\nIf the act of printing an atom uses the formatting state that says\n\"next one needs X\", then it is responsible to clear that \"next one\nneeds X\" part of the state, as it is the one who consumed that\nstate.  E.g. if it used to say \"next one needs to be padded to the\nright\" before entering print_value(), then the function did that\n\"padded output\", then the \"next one needs to be padded to the\nright\" should be cleared inside print_value().\n"},{"id":"267098","messageId":"xmqqfv4691ui.fsf@gitster.dls.corp.google.com","threadId":"39951","inReplyTo":"CAPig+cS+w8ECma--ncJDoN1fEgrFZMvBC8GBgU6+tLYm_oGkaw@mail.gmail.com","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-07-29T19:54:13Z","receivedAt":"2015-07-29T19:54:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> @@ -1254,9 +1273,26 @@ static void emit(const char *cp, const char *ep)\n>> +static void reset_formatting_state(struct ref_formatting_state *state)\n>> +{\n>> +       int quote_style = state->quote_style;\n>> +       memset(state, 0, sizeof(*state));\n>> +       state->quote_style = quote_style;\n>\n> I wonder if this sledge-hammer approach of saving one or two values\n> before clearing the entire 'ref_formatting_state' and then restoring\n> the saved values will scale well. Would it be better for this to just\n> individually reset the fields which need resetting and not touch those\n> that don't?\n>\n> Also, the fact that quote_style has to be handled specially may be an\n> indication that it doesn't belong in this structure grouped with the\n> other modifiers or that you need better classification within the\n> structure.\n\nActually, I think it is wrong to have this function in the first\nplace.  It is a sign that the caller is doing too little before\ncalling this function.\n\nIf the act of printing an atom uses the formatting state that says\n\"next one needs X\", then it is responsible to clear that \"next one\nneeds X\" part of the state, as it is the one who consumed that\nstate.  E.g. if it used to say \"next one needs to be padded to the\nright\" before entering print_value(), then the function did that\n\"padded output\", then the \"next one needs to be padded to the\nright\" should be cleared inside print_value().\n\nAnd with that arrangement, together with calling emit() with\nformatting state, %(color:blue) can be handled as a normal part of\nthe formatting state mechanism.  The pseudo/modifier atom should\nupdate the state to \"Start printing in blue\", and either emit() or\nprint_value(), whichever is called first, would notice that state,\ndoes what was requested, and flip that bit down (because we are\nalready printing in \"blue\" so the next output function does not have\nto do the \"blue\" thing again).\n"},{"id":"267101","messageId":"xmqqbneu907l.fsf@gitster.dls.corp.google.com","threadId":"39951","inReplyTo":"CAPig+cQv4BWFwgx7mDHh=gNpxbEeR7auw4os-tOpf3cGah=zpw@mail.gmail.com","subject":"Re: [PATCH v6 0/10] port tag.c to use ref-filter APIs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-07-29T20:29:34Z","receivedAt":"2015-07-29T20:29:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> Side Note: --format=\"%(padright:X)\" applies to the next available atom\n>> and not to the next span. I find this more accurate as I don't see why\n>> we'd want to pad something of known length. But its up for discussion\n>\n> This isn't supported by the current %(padright:) syntax, but an\n> example would be if someone wants to pad a string composed of atoms\n> and literal strings. For instance, the user might want to right-pad\n> the composed string \"%(refattribute1) glorked %(refattribute2)”.\n\nIt is an excellent example that shows why \"something of known\nlength\" argument needs to be rethought.\n\n\"Currently we do not need it to reimplement the canned 'tag -l'\nformat\" is an OK and sensible justification to stick to the current\nimplementation of %(padright:N), but we'd need to think if we would\nwant to keep this limited and strange form that applies to a single\natom that comes next (ignoring any literal spans) as a private\nimplementation detail between ref-filter and \"git tag\".  Opening it\nup to end-users would not mean we cannot add a correctly operating\nvariant of \"pad this string to the right\" later, but it does mean we\nhave to maintain %(padright) in this limited form forever.\n\nMy knee-jerk reaction is that we probably should not want to expose\nthis to the end users, and to discourage its use, perhaps name it\nsomewhat strangely (e.g. \"%(x-padright:N)\" or something).\n"},{"id":"267109","messageId":"vpqmvyed4ws.fsf@anie.imag.fr","threadId":"39951","inReplyTo":"CAPig+cS+w8ECma--ncJDoN1fEgrFZMvBC8GBgU6+tLYm_oGkaw@mail.gmail.com","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-07-29T21:34:27Z","receivedAt":"2015-07-29T21:34:27Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> @@ -1254,9 +1273,26 @@ static void emit(const char *cp, const char *ep)\n>> +static void reset_formatting_state(struct ref_formatting_state *state)\n>> +{\n>> +       int quote_style = state->quote_style;\n>> +       memset(state, 0, sizeof(*state));\n>> +       state->quote_style = quote_style;\n>\n> I wonder if this sledge-hammer approach of saving one or two values\n> before clearing the entire 'ref_formatting_state' and then restoring\n> the saved values will scale well. Would it be better for this to just\n> individually reset the fields which need resetting and not touch those\n> that don't?\n\nI'm the one who suggested these 3 lines. I wrote them this way with the\nassumption that there would only be 1 field to keep, and thet the rest\nof the series was going to add more fields to reset (currently true I\nthink), to avoid the risk of forgetting one value to reset.\n\nI'm fine with the other way around too.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"267124","messageId":"CAOLa=ZTOakNY79h7HUUxbbop9bFhTXDSm+WCLa6xze8G=XF15A@mail.gmail.com","threadId":"39951","inReplyTo":"CAPig+cS+w8ECma--ncJDoN1fEgrFZMvBC8GBgU6+tLYm_oGkaw@mail.gmail.com","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T06:53:47Z","receivedAt":"2015-07-30T06:53:47Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Thu, Jul 30, 2015 at 12:49 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Tuesday, July 28, 2015, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> Introduce 'ref_formatting' structure to hold values of pseudo atoms\n>> which help only in formatting. This will eventually be used by atoms\n>> like `color` and the `padright` atom which will be introduced in a\n>> later patch.\n>\n> Isn't this commit message outdated now that you no longer treat color\n> specially and since the terminology is changing from \"pseudo\" to\n> \"modifier\"? Also, isn't the structure now called\n> 'ref_formatting_state' rather than 'ref_formatting'?\n\nYes, thanks for pointing it out. will change.\n\n>\n>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>> ---\n>> diff --git a/ref-filter.c b/ref-filter.c\n>> index 7561727..a919a14 100644\n>> --- a/ref-filter.c\n>> +++ b/ref-filter.c\n>> @@ -620,7 +622,7 @@ static void populate_value(struct ref_array_item *ref)\n>>                 const char *name = used_atom[i];\n>>                 struct atom_value *v = &ref->value[i];\n>>                 int deref = 0;\n>> -               const char *refname;\n>> +               const char *refname = NULL;\n>\n> What is this change about? It doesn't seem to be related to anything\n> else in the patch.\n>\n\nIn previous versions it was giving a refname not assigned error before usage\nerror, in the current version, its not needed. will remove.\n\n>>                 const char *formatp;\n>>                 struct branch *branch = NULL;\n>>\n>> @@ -1190,30 +1192,47 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n>> +static void print_value(struct atom_value *v, struct ref_formatting_state *state)\n>> +{\n>> +       struct strbuf value = STRBUF_INIT;\n>> +       struct strbuf formatted = STRBUF_INIT;\n>> +\n>> +       /*\n>> +        * Some (pesudo) atoms have no immediate side effect, but only\n>> +        * affect the next atom. Store the relevant information from\n>> +        * these atoms in the 'state' variable for use when displaying\n>> +        * the next atom.\n>> +        */\n>> +       apply_formatting_state(state, v, &value);\n>\n> The comment says that this is \"storing\" formatting state, however, the\n> code is actually \"applying\" the state. You could move this comment\n> down to show_ref_array_item() where formatting state actually gets\n> stored. Or you could fix it to talk about \"applying\" the state.\n> However, now that apply_formatting_state() has a meaningful name, you\n> could also drop the comment altogether since it doesn't say much\n> beyond what is said already by the function name.\n>\n\nI guess I'll drop the comment thanks :)\n\n>> +       switch (state->quote_style) {\n>>         case QUOTE_NONE:\n>> -               fputs(v->s, stdout);\n>> @@ -1254,9 +1273,26 @@ static void emit(const char *cp, const char *ep)\n>> +static void reset_formatting_state(struct ref_formatting_state *state)\n>> +{\n>> +       int quote_style = state->quote_style;\n>> +       memset(state, 0, sizeof(*state));\n>> +       state->quote_style = quote_style;\n>\n> I wonder if this sledge-hammer approach of saving one or two values\n> before clearing the entire 'ref_formatting_state' and then restoring\n> the saved values will scale well. Would it be better for this to just\n> individually reset the fields which need resetting and not touch those\n> that don't?\n>\n> Also, the fact that quote_style has to be handled specially may be an\n> indication that it doesn't belong in this structure grouped with the\n> other modifiers or that you need better classification within the\n> structure. For instance:\n>\n>     struct ref_formatting_state {\n>         struct global {\n>             int quote_style;\n>         };\n>         struct local {\n>             int pad_right;\n>         };\n>\n> where 'local' state gets reset by reset_formatting_state(), and\n> 'global' is left alone.\n>\n> That's just one idea, not necessarily a proposal, but is something to\n> think about since the current arrangement is kind of yucky.\n>\n\nDid you read Junio's suggestion about not having a reset_formatting_state()\nand rather just have each state be responsible of resetting itself.\n\nI think thats seems to be a better approach.\n\n>> +}\n>> +\n>>  void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n>>  {\n>>         const char *cp, *sp, *ep;\n>> +       struct ref_formatting_state state;\n>> +\n>> +       memset(&state, 0, sizeof(state));\n>> +       state.quote_style = quote_style;\n>\n> It's a little bit ugly to use memset() here when you have\n> reset_formatting_state() available. You could set quote_style first,\n> and then call reset_formatting_state() rather than memset(). Or,\n> perhaps, change reset_formatting_state(), as described above, to stop\n> using the sledge-hammer approach.\n>\n\nI guess even this would be taken care of by implementing Junio's suggestion.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267126","messageId":"CAOLa=ZRyunhpXwW=YnmYS=yWdcyr0Fid2ZzO+1qq+PzSCrHVLA@mail.gmail.com","threadId":"39951","inReplyTo":"xmqqfv4691ui.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T09:18:21Z","receivedAt":"2015-07-30T09:18:21Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Thu, Jul 30, 2015 at 1:24 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Actually, I think it is wrong to have this function in the first\n> place.  It is a sign that the caller is doing too little before\n> calling this function.\n>\n> If the act of printing an atom uses the formatting state that says\n> \"next one needs X\", then it is responsible to clear that \"next one\n> needs X\" part of the state, as it is the one who consumed that\n> state.  E.g. if it used to say \"next one needs to be padded to the\n> right\" before entering print_value(), then the function did that\n> \"padded output\", then the \"next one needs to be padded to the\n> right\" should be cleared inside print_value().\n>\n\nHmm, something like what we did in version 5  I guess.\n\n> And with that arrangement, together with calling emit() with\n> formatting state, %(color:blue) can be handled as a normal part of\n> the formatting state mechanism.  The pseudo/modifier atom should\n> update the state to \"Start printing in blue\", and either emit() or\n> print_value(), whichever is called first, would notice that state,\n> does what was requested, and flip that bit down (because we are\n> already printing in \"blue\" so the next output function does not have\n> to do the \"blue\" thing again).\n\nThis makes sense, will work on this thanks :)\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267127","messageId":"vpqsi86atev.fsf@anie.imag.fr","threadId":"39951","inReplyTo":"xmqqfv4691ui.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 01/10] ref-filter: introduce 'ref_formatting_state'","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-07-30T09:25:44Z","receivedAt":"2015-07-30T09:25:44Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> If the act of printing an atom uses the formatting state that says\n> \"next one needs X\", then it is responsible to clear that \"next one\n> needs X\" part of the state, as it is the one who consumed that\n> state.  E.g. if it used to say \"next one needs to be padded to the\n> right\" before entering print_value(), then the function did that\n> \"padded output\", then the \"next one needs to be padded to the\n> right\" should be cleared inside print_value().\n\nGood point. This would also reduce the risk of forgetting to reset, as\n\"use\" and \"reset\" would be next to each other in the code.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"267128","messageId":"vpqzj2e9dyj.fsf@anie.imag.fr","threadId":"39951","inReplyTo":"xmqqbneu907l.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 0/10] port tag.c to use ref-filter APIs","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2015-07-30T09:44:52Z","receivedAt":"2015-07-30T09:44:52Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> \"Currently we do not need it to reimplement the canned 'tag -l'\n> format\" is an OK and sensible justification to stick to the current\n> implementation of %(padright:N), but we'd need to think if we would\n> want to keep this limited and strange form that applies to a single\n> atom that comes next (ignoring any literal spans) as a private\n> implementation detail between ref-filter and \"git tag\".  Opening it\n> up to end-users would not mean we cannot add a correctly operating\n> variant of \"pad this string to the right\" later, but it does mean we\n> have to maintain %(padright) in this limited form forever.\n>\n> My knee-jerk reaction is that we probably should not want to expose\n> this to the end users, and to discourage its use, perhaps name it\n> somewhat strangely (e.g. \"%(x-padright:N)\" or something).\n\nI disagree. The current %(padright) fits 99.9% needs. It's handy for the\nuser if he wants a column-display with --format. It's consistant with\nthe \"git log\" %<() atoms.\n\nSure, if the user wants really advanced formatting, it's not sufficient.\nBut first I believe this is a case of YAGNI, \"right-pad an arbitrary\nstring\" is a funny coding exercice, but not very useful in real-life.\nAnd then, if one really has a use-case for advanced formatting, I think\na much better approach is to dump an easy-to-parse language\n(XML/JSON/CSV/...) and pipe it to a formatter written in a real\nprogramming language. It will always be more powerful than having to\nchose in a limited set of %(atoms).\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"267130","messageId":"CAOLa=ZSBbURBhV5yq2NcVJNaCBA+1fFORN9a65OJgzObAPdCeQ@mail.gmail.com","threadId":"39951","inReplyTo":"vpqzj2e9dyj.fsf@anie.imag.fr","subject":"Re: [PATCH v6 0/10] port tag.c to use ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T10:13:02Z","receivedAt":"2015-07-30T10:13:02Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Thu, Jul 30, 2015 at 3:14 PM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> \"Currently we do not need it to reimplement the canned 'tag -l'\n>> format\" is an OK and sensible justification to stick to the current\n>> implementation of %(padright:N), but we'd need to think if we would\n>> want to keep this limited and strange form that applies to a single\n>> atom that comes next (ignoring any literal spans) as a private\n>> implementation detail between ref-filter and \"git tag\".  Opening it\n>> up to end-users would not mean we cannot add a correctly operating\n>> variant of \"pad this string to the right\" later, but it does mean we\n>> have to maintain %(padright) in this limited form forever.\n>>\n>> My knee-jerk reaction is that we probably should not want to expose\n>> this to the end users, and to discourage its use, perhaps name it\n>> somewhat strangely (e.g. \"%(x-padright:N)\" or something).\n>\n> I disagree. The current %(padright) fits 99.9% needs. It's handy for the\n> user if he wants a column-display with --format. It's consistant with\n> the \"git log\" %<() atoms.\n>\n> Sure, if the user wants really advanced formatting, it's not sufficient.\n> But first I believe this is a case of YAGNI, \"right-pad an arbitrary\n> string\" is a funny coding exercice, but not very useful in real-life.\n> And then, if one really has a use-case for advanced formatting, I think\n> a much better approach is to dump an easy-to-parse language\n> (XML/JSON/CSV/...) and pipe it to a formatter written in a real\n> programming language. It will always be more powerful than having to\n> chose in a limited set of %(atoms).\n>\n\nExactly! what I was thinking, I mean this is quite useful feature in itself,\nAgreed there are numerous end cases which are not satisfied, but those\nare quite minimal and rarely used.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267131","messageId":"CAOLa=ZR2z1JJnNZr1qr-Bk6JL0p9ghnEAkCKvrvrkQaBOBsyHg@mail.gmail.com","threadId":"39951","inReplyTo":"CAPig+cS4zVT2=kuV3yc860vp4fKrqzvM_phyNngSUho9zTzStA@mail.gmail.com","subject":"Re: [PATCH v6 02/10] ref-filter: add option to pad atoms to the right","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T10:18:17Z","receivedAt":"2015-07-30T10:18:17Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Thu, Jul 30, 2015 at 12:59 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Tuesday, July 28, 2015, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> Add a new atom \"padright\" and support %(padright:X) where X is a\n>> number.  This will align the succeeding atom value to the left\n>> followed by spaces for a total length of X characters. If X is less\n>> than the item size, the entire atom value is printed.\n>>\n>> Add tests and documentation for the same.\n>>\n>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>> ---\n>> diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\n>> index 505a360..19ac480 100755\n>> --- a/t/t6302-for-each-ref-filter.sh\n>> +++ b/t/t6302-for-each-ref-filter.sh\n>> @@ -81,4 +81,20 @@ test_expect_success 'filtering with --contains' '\n>>         test_cmp expect actual\n>>  '\n>>\n>> +test_expect_success 'padding to the right using `padright`' '\n>> +       cat >expect <<-\\EOF &&\n>> +       refs/heads/master|refs/heads/master        |refs/heads/master|\n>> +       refs/heads/side|refs/heads/side          |refs/heads/side|\n>> +       refs/odd/spot|refs/odd/spot            |refs/odd/spot|\n>> +       refs/tags/double-tag|refs/tags/double-tag     |refs/tags/double-tag|\n>> +       refs/tags/four|refs/tags/four           |refs/tags/four|\n>> +       refs/tags/one|refs/tags/one            |refs/tags/one|\n>> +       refs/tags/signed-tag|refs/tags/signed-tag     |refs/tags/signed-tag|\n>> +       refs/tags/three|refs/tags/three          |refs/tags/three|\n>> +       refs/tags/two|refs/tags/two            |refs/tags/two|\n>> +       EOF\n>> +       git for-each-ref --format=\"%(refname)%(padright:25)|%(refname)|%(refname)|\" >actual &&\n>\n> This fails to test the important case when the atom length is greater\n> than the padright value (in which case no padding should be done, and\n> the atom text should extend beyond the 'padright' column).\n>\n\nWill add a test for the same, thanks :)\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267132","messageId":"CAOLa=ZRs0-sAC8dfHeMDVAr7Rp6XK+nMN9vGV=HGJH0nBj2fUA@mail.gmail.com","threadId":"39951","inReplyTo":"CAPig+cSZzA+3BaP78VhQ3JjYbW_Y+KRhQ6c4-SPuxysKmL8bZw@mail.gmail.com","subject":"Re: [PATCH v6 05/10] ref-filter: add support to sort by version","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T10:23:25Z","receivedAt":"2015-07-30T10:23:25Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Thu, Jul 30, 2015 at 1:04 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Tuesday, July 28, 2015, Karthik Nayak <karthik.188@gmail.com> wrote:\n>> Add support to sort by version using the \"v:refname\" and\n>> \"version:refname\" option. This is achieved by using the 'versioncmp()'\n>> function as the comparing function for qsort.\n>>\n>> This option is included to support sorting by versions in `git tag -l`\n>> which will eventaully be ported to use ref-filter APIs.\n>>\n>> Add documentation and tests for the same.\n>>\n>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>> ---\n>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n>> index 45dd7f8..2b60aee 100644\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -151,6 +151,9 @@ For sorting purposes, fields with numeric values sort in numeric\n>>  order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).\n>>  All other fields are used to sort in their byte-value order.\n>>\n>> +There is also an option to sort by versions, this can be done by using\n>> +the fieldname `version:refname` or in short `v:refname`.\n>\n> Nit: \"or in short\" reads a bit oddly. Perhaps:\n>\n>     ...`version:refname` or its alias `v:refname`.\n>\n> or\n>\n>     ...`version:refname` or the short-form `v:refname`.\n>\n> (I rather prefer the \"alias\" alternative.)\n>\n\nThanks! will change.\n\n>>  In any case, a field name that refers to a field inapplicable to\n>>  the object referred by the ref does not cause an error.  It\n>>  returns an empty string instead.\n>> diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\n>> index 19ac480..68688a9 100755\n>> --- a/t/t6302-for-each-ref-filter.sh\n>> +++ b/t/t6302-for-each-ref-filter.sh\n>> @@ -97,4 +97,40 @@ test_expect_success 'padding to the right using `padright`' '\n>> +test_expect_success 'version sort' '\n>> +       git for-each-ref --sort=version:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n>> +       cat >expect <<-\\EOF &&\n>> +       foo1.3\n>> +       foo1.6\n>> +       foo1.10\n>> +       EOF\n>> +       test_cmp expect actual\n>> +'\n>> +\n>> +test_expect_success 'version sort (shortened)' '\n>> +       git for-each-ref --sort=v:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n>> +       cat >expect <<-\\EOF &&\n>> +       foo1.3\n>> +       foo1.6\n>> +       foo1.10\n>> +       EOF\n>> +       test_cmp expect actual\n>\n> Nit: In the earlier review when I suggested using \"v:refname\" for one\n> of the tests in order to exercise it (in addition to\n> \"version:refname\"), I didn't mean that you had to add another\n> (identical) test but rather that you could have one of the existing\n> tests use \"v:refname\". (Not a big deal. You can leave this as is if\n> you like. I just wanted to clarify.)\n>\n\nAh! Thats ok, this seems to explain the commit entirely showing that\n\"v:refname\" and \"version:refname\" are the same thing.\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267142","messageId":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"CAOLa=ZR6_2NBB4v0Ynq391=8Jk2RZON6R0YG=HKUNwKx249b7Q@mail.gmail.com","subject":"[PATCH v7 01/11] ref-filter: introduce 'ref_formatting_state'","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:42Z","receivedAt":"2015-07-30T15:35:42Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce 'ref_formatting_state' structure to hold values of modifier\natoms which help only in formatting. This will eventually be used by\natoms like `padright` which will be introduced in a later patch.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\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 | 70 ++++++++++++++++++++++++++++++++++++++++++++----------------\n ref-filter.h |  5 +++++\n 2 files changed, 57 insertions(+), 18 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7561727..d6510a6 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -10,6 +10,8 @@\n #include \"quote.h\"\n #include \"ref-filter.h\"\n #include \"revision.h\"\n+#include \"utf8.h\"\n+#include \"git-compat-util.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n@@ -1190,30 +1192,41 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n \tqsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);\n }\n \n-static void print_value(struct atom_value *v, int quote_style)\n+static void apply_formatting_state(struct ref_formatting_state *state,\n+\t\t\t\t   const char *buf, struct strbuf *value)\n {\n-\tstruct strbuf sb = STRBUF_INIT;\n-\tswitch (quote_style) {\n+\t/* Eventually we'll format based on the ref_formatting_state */\n+\tstrbuf_addstr(value, buf);\n+}\n+\n+static void print_value(struct atom_value *v, struct ref_formatting_state *state)\n+{\n+\tstruct strbuf value = STRBUF_INIT;\n+\tstruct strbuf formatted = STRBUF_INIT;\n+\n+\tapply_formatting_state(state, v->s, &value);\n+\n+\tswitch (state->quote_style) {\n \tcase QUOTE_NONE:\n-\t\tfputs(v->s, stdout);\n+\t\tfputs(value.buf, stdout);\n \t\tbreak;\n \tcase QUOTE_SHELL:\n-\t\tsq_quote_buf(&sb, v->s);\n+\t\tsq_quote_buf(&formatted, value.buf);\n \t\tbreak;\n \tcase QUOTE_PERL:\n-\t\tperl_quote_buf(&sb, v->s);\n+\t\tperl_quote_buf(&formatted, value.buf);\n \t\tbreak;\n \tcase QUOTE_PYTHON:\n-\t\tpython_quote_buf(&sb, v->s);\n+\t\tpython_quote_buf(&formatted, value.buf);\n \t\tbreak;\n \tcase QUOTE_TCL:\n-\t\ttcl_quote_buf(&sb, v->s);\n+\t\ttcl_quote_buf(&formatted, value.buf);\n \t\tbreak;\n \t}\n-\tif (quote_style != QUOTE_NONE) {\n-\t\tfputs(sb.buf, stdout);\n-\t\tstrbuf_release(&sb);\n-\t}\n+\tif (state->quote_style != QUOTE_NONE)\n+\t\tfputs(formatted.buf, stdout);\n+\tstrbuf_release(&value);\n+\tstrbuf_release(&formatted);\n }\n \n static int hex1(char ch)\n@@ -1234,8 +1247,12 @@ static int hex2(const char *cp)\n \t\treturn -1;\n }\n \n-static void emit(const char *cp, const char *ep)\n+static void emit(const char *cp, const char *ep,\n+\t\t struct ref_formatting_state *state)\n {\n+\tstruct strbuf value = STRBUF_INIT;\n+\tstruct strbuf format = STRBUF_INIT;\n+\n \twhile (*cp && (!ep || cp < ep)) {\n \t\tif (*cp == '%') {\n \t\t\tif (cp[1] == '%')\n@@ -1249,27 +1266,44 @@ static void emit(const char *cp, const char *ep)\n \t\t\t\t}\n \t\t\t}\n \t\t}\n-\t\tputchar(*cp);\n+\t\tstrbuf_addch(&value, *cp);\n \t\tcp++;\n \t}\n+\tapply_formatting_state(state, value.buf, &format);\n+\tfputs(format.buf, stdout);\n+\tstrbuf_release(&format);\n+\tstrbuf_release(&value);\n+}\n+\n+static void store_formatting_state(struct ref_formatting_state *state,\n+\t\t\t\t   struct atom_value *atomv)\n+{\n+\t/*  Here the 'ref_formatting_state' variable will be filled */\n }\n \n void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n {\n \tconst char *cp, *sp, *ep;\n+\tstruct ref_formatting_state state;\n+\n+\tmemset(&state, 0, sizeof(state));\n+\tstate.quote_style = quote_style;\n \n \tfor (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n \t\tstruct atom_value *atomv;\n \n \t\tep = strchr(sp, ')');\n \t\tif (cp < sp)\n-\t\t\temit(cp, sp);\n+\t\t\temit(cp, sp, &state);\n \t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n-\t\tprint_value(atomv, quote_style);\n+\t\tif (atomv->modifier_atom)\n+\t\t\tstore_formatting_state(&state, atomv);\n+\t\telse\n+\t\t\tprint_value(atomv, &state);\n \t}\n \tif (*cp) {\n \t\tsp = cp + strlen(cp);\n-\t\temit(cp, sp);\n+\t\temit(cp, sp, &state);\n \t}\n \tif (need_color_reset_at_eol) {\n \t\tstruct atom_value resetv;\n@@ -1278,7 +1312,7 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n \t\tif (color_parse(\"reset\", color) < 0)\n \t\t\tdie(\"BUG: couldn't parse 'reset' as a color\");\n \t\tresetv.s = color;\n-\t\tprint_value(&resetv, quote_style);\n+\t\tprint_value(&resetv, &state);\n \t}\n \tputchar('\\n');\n }\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 6bf27d8..12e6a6b 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -19,6 +19,11 @@\n struct atom_value {\n \tconst char *s;\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n+\tunsigned int modifier_atom : 1; /*  atoms which act as modifiers for the next atom */\n+};\n+\n+struct ref_formatting_state {\n+\tint quote_style;\n };\n \n struct ref_sorting {\n-- \n2.4.6\n"},{"id":"267144","messageId":"1438270552-10333-2-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v7 02/11] ref-filter: make `color` use `ref_formatting_state`","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:43Z","receivedAt":"2015-07-30T15:35:43Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Convert the 'color' atom to behave as a modifier atom and make it use\n'ref_formatting_state' which was introduced in the previous patch.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\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 | 10 ++++++++--\n ref-filter.h |  4 +++-\n 2 files changed, 11 insertions(+), 3 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex d6510a6..9a63d25 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -662,6 +662,8 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\tif (color_parse(name + 6, color) < 0)\n \t\t\t\tdie(_(\"unable to parse format\"));\n \t\t\tv->s = xstrdup(color);\n+\t\t\tv->color = 1;\n+\t\t\tv->modifier_atom = 1;\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"flag\")) {\n \t\t\tchar buf[256], *cp = buf;\n@@ -1195,7 +1197,10 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n static void apply_formatting_state(struct ref_formatting_state *state,\n \t\t\t\t   const char *buf, struct strbuf *value)\n {\n-\t/* Eventually we'll format based on the ref_formatting_state */\n+\tif (state->color) {\n+\t\tstrbuf_addstr(value, state->color);\n+\t\tstate->color = NULL;\n+\t}\n \tstrbuf_addstr(value, buf);\n }\n \n@@ -1278,7 +1283,8 @@ static void emit(const char *cp, const char *ep,\n static void store_formatting_state(struct ref_formatting_state *state,\n \t\t\t\t   struct atom_value *atomv)\n {\n-\t/*  Here the 'ref_formatting_state' variable will be filled */\n+\tif (atomv->color)\n+\t\tstate->color = atomv->s;\n }\n \n void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 12e6a6b..5d33360 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -19,11 +19,13 @@\n struct atom_value {\n \tconst char *s;\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n-\tunsigned int modifier_atom : 1; /*  atoms which act as modifiers for the next atom */\n+\tunsigned int modifier_atom : 1, /*  atoms which act as modifiers for the next atom */\n+\t\tcolor : 1;\n };\n \n struct ref_formatting_state {\n \tint quote_style;\n+\tconst char *color;\n };\n \n struct ref_sorting {\n-- \n2.4.6\n"},{"id":"267146","messageId":"1438270552-10333-3-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v7 03/11] ref-filter: add option to pad atoms to the right","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:44Z","receivedAt":"2015-07-30T15:35:44Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add a new atom \"padright\" and support %(padright:X) where X is a\nnumber.  This will align the succeeding atom or string to the left\nfollowed by spaces for a total length of X characters. If X is less\nthan the atom or string length then no padding is done.\n\nAdd tests and documentation for the same.\n\nHelped-by: Duy Nguyen <pclouds@gmail.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\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 |  6 ++++++\n ref-filter.c                       | 26 ++++++++++++++++++++++++++\n ref-filter.h                       |  4 +++-\n t/t6302-for-each-ref-filter.sh     | 32 ++++++++++++++++++++++++++++++++\n 4 files changed, 67 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex e49d578..9961921 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -127,6 +127,12 @@ color::\n \tChange output color.  Followed by `:<colorname>`, where names\n \tare described in `color.branch.*`.\n \n+padright::\n+\tPad succeeding atom or string to the right. Followed by\n+\t`:<value>`, where `value` states the total length of atom or\n+\tstring including the padding. If the `value` is lesser than\n+\tthe atom or string length, then no padding is performed.\n+\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 9a63d25..e2890e3 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -55,6 +55,7 @@ static struct {\n \t{ \"flag\" },\n \t{ \"HEAD\" },\n \t{ \"color\" },\n+\t{ \"padright\" },\n };\n \n /*\n@@ -691,6 +692,18 @@ static void populate_value(struct ref_array_item *ref)\n \t\t\telse\n \t\t\t\tv->s = \" \";\n \t\t\tcontinue;\n+\t\t} else if (starts_with(name, \"padright:\")) {\n+\t\t\tconst char *valp = NULL;\n+\n+\t\t\tskip_prefix(name, \"padright:\", &valp);\n+\t\t\tif (!valp[0])\n+\t\t\t\tdie(_(\"no value given with 'padright:'\"));\n+\t\t\tif (strtoul_ui(valp, 10, (unsigned int *)&v->ul))\n+\t\t\t\tdie(_(\"positive integer expected after ':' in padright:%u\\n\"),\n+\t\t\t\t    (unsigned int)v->ul);\n+\t\t\tv->modifier_atom = 1;\n+\t\t\tv->pad_to_right = 1;\n+\t\t\tcontinue;\n \t\t} else\n \t\t\tcontinue;\n \n@@ -1201,6 +1214,17 @@ static void apply_formatting_state(struct ref_formatting_state *state,\n \t\tstrbuf_addstr(value, state->color);\n \t\tstate->color = NULL;\n \t}\n+\tif (state->pad_to_right) {\n+\t\tif (!is_utf8(buf))\n+\t\t\tstrbuf_addf(value, \"%-*s\", state->pad_to_right, buf);\n+\t\telse {\n+\t\t\tint utf8_compensation = strlen(buf) - utf8_strwidth(buf);\n+\t\t\tstrbuf_addf(value, \"%-*s\", state->pad_to_right + utf8_compensation, buf);\n+\t\t}\n+\t\tstate->pad_to_right = 0;\n+\t\treturn;\n+\t}\n+\n \tstrbuf_addstr(value, buf);\n }\n \n@@ -1285,6 +1309,8 @@ static void store_formatting_state(struct ref_formatting_state *state,\n {\n \tif (atomv->color)\n \t\tstate->color = atomv->s;\n+\tif (atomv->pad_to_right)\n+\t\tstate->pad_to_right = atomv->ul;\n }\n \n void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 5d33360..e548c93 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -20,11 +20,13 @@ struct atom_value {\n \tconst char *s;\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n \tunsigned int modifier_atom : 1, /*  atoms which act as modifiers for the next atom */\n-\t\tcolor : 1;\n+\t\tcolor : 1,\n+\t\tpad_to_right : 1;\n };\n \n struct ref_formatting_state {\n \tint quote_style;\n+\tunsigned int pad_to_right;\n \tconst char *color;\n };\n \ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex 505a360..48caac9 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -81,4 +81,36 @@ test_expect_success 'filtering with --contains' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'padding to the right using `padright`' '\n+\tcat >expect <<-\\EOF &&\n+\tmaster|    master    |\n+\tside|    side      |\n+\todd/spot|    odd/spot  |\n+\tdouble-tag|    double-tag|\n+\tfour|    four      |\n+\tone|    one       |\n+\tsigned-tag|    signed-tag|\n+\tthree|    three     |\n+\ttwo|    two       |\n+\tEOF\n+\tgit for-each-ref --format=\"%(refname:short)%(padright:5)|%(padright:10)%(refname:short)|\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'no padding when `padright` length is smaller than atom length' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/master|\n+\trefs/heads/side|\n+\trefs/odd/spot|\n+\trefs/tags/double-tag|\n+\trefs/tags/four|\n+\trefs/tags/one|\n+\trefs/tags/signed-tag|\n+\trefs/tags/three|\n+\trefs/tags/two|\n+\tEOF\n+\tgit for-each-ref --format=\"%(padright:5)%(refname)|\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.6\n"},{"id":"267143","messageId":"1438270552-10333-4-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v7 04/11] ref-filter: add option to filter only tags","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:45Z","receivedAt":"2015-07-30T15:35:45Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nAdd a functions called 'for_each_tag_ref_fullpath()' to refs.{c,h}\nwhich iterates through each tag ref without trimming the path.\n\nAdd an option in 'filter_refs()' to use 'for_each_tag_ref_fullpath()'\nand filter refs. This type checking is done by adding a\n'FILTER_REFS_TAGS' in 'ref-filter.h'\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 | 2 ++\n ref-filter.h | 1 +\n refs.c       | 5 +++++\n refs.h       | 1 +\n 4 files changed, 9 insertions(+)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex e2890e3..510a1da 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1152,6 +1152,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int\n \t\tret = for_each_rawref(ref_filter_handler, &ref_cbdata);\n \telse if (type & FILTER_REFS_ALL)\n \t\tret = for_each_ref(ref_filter_handler, &ref_cbdata);\n+\telse if (type & FILTER_REFS_TAGS)\n+\t\tret = for_each_tag_ref_fullpath(ref_filter_handler, &ref_cbdata);\n \telse if (type)\n \t\tdie(\"filter_refs: invalid type\");\n \ndiff --git a/ref-filter.h b/ref-filter.h\nindex e548c93..2b8462a 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -15,6 +15,7 @@\n \n #define FILTER_REFS_INCLUDE_BROKEN 0x1\n #define FILTER_REFS_ALL 0x2\n+#define FILTER_REFS_TAGS 0x4\n \n struct atom_value {\n \tconst char *s;\ndiff --git a/refs.c b/refs.c\nindex 0b96ece..23ce483 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2108,6 +2108,11 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)\n \treturn for_each_ref_in(\"refs/tags/\", fn, cb_data);\n }\n \n+int for_each_tag_ref_fullpath(each_ref_fn fn, void *cb_data)\n+{\n+\treturn do_for_each_ref(&ref_cache, \"refs/tags/\", fn, 0, 0, cb_data);\n+}\n+\n int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)\n {\n \treturn for_each_ref_in_submodule(submodule, \"refs/tags/\", fn, cb_data);\ndiff --git a/refs.h b/refs.h\nindex e4e46c3..9eee2de 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -174,6 +174,7 @@ extern int head_ref(each_ref_fn fn, void *cb_data);\n extern int for_each_ref(each_ref_fn fn, void *cb_data);\n extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);\n extern int for_each_tag_ref(each_ref_fn fn, void *cb_data);\n+extern int for_each_tag_ref_fullpath(each_ref_fn fn, void *cb_data);\n extern int for_each_branch_ref(each_ref_fn fn, void *cb_data);\n extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);\n extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);\n-- \n2.4.6\n"},{"id":"267145","messageId":"1438270552-10333-5-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v7 05/11] ref-filter: support printing N lines from tag annotation","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:46Z","receivedAt":"2015-07-30T15:35:46Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nIn 'tag.c' we can print N lines from the annotation of the tag using\nthe '-n<num>' option. Copy code from 'tag.c' to 'ref-filter' and\nmodify 'ref-filter' to support printing of N lines from the annotation\nof tags.\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/for-each-ref.c |  2 +-\n builtin/tag.c          |  4 ++++\n ref-filter.c           | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-\n ref-filter.h           |  9 +++++++--\n 4 files changed, 62 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 40f343b..e4a4f8a 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -74,7 +74,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \tif (!maxcount || array.nr < maxcount)\n \t\tmaxcount = array.nr;\n \tfor (i = 0; i < maxcount; i++)\n-\t\tshow_ref_array_item(array.items[i], format, quote_style);\n+\t\tshow_ref_array_item(array.items[i], format, quote_style, 0);\n \tref_array_clear(&array);\n \treturn 0;\n }\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 471d6b1..0fc7557 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -185,6 +185,10 @@ static enum contains_result contains(struct commit *candidate,\n \treturn contains_test(candidate, want);\n }\n \n+/*\n+ * Currently duplicated in ref-filter, will eventually be removed as\n+ * we port tag.c to use ref-filter APIs.\n+ */\n static void show_tag_lines(const struct object_id *oid, int lines)\n {\n \tint i;\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 510a1da..edb2c38 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1315,7 +1315,51 @@ static void store_formatting_state(struct ref_formatting_state *state,\n \t\tstate->pad_to_right = atomv->ul;\n }\n \n-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n+/*\n+ * If 'lines' is greater than 0, print that many lines from the given\n+ * object_id 'oid'.\n+ */\n+static void show_tag_lines(const struct object_id *oid, int lines)\n+{\n+\tint i;\n+\tunsigned long size;\n+\tenum object_type type;\n+\tchar *buf, *sp, *eol;\n+\tsize_t len;\n+\n+\tbuf = read_sha1_file(oid->hash, &type, &size);\n+\tif (!buf)\n+\t\tdie_errno(\"unable to read object %s\", oid_to_hex(oid));\n+\tif (type != OBJ_COMMIT && type != OBJ_TAG)\n+\t\tgoto free_return;\n+\tif (!size)\n+\t\tdie(\"an empty %s object %s?\",\n+\t\t    typename(type), oid_to_hex(oid));\n+\n+\t/* skip header */\n+\tsp = strstr(buf, \"\\n\\n\");\n+\tif (!sp)\n+\t\tgoto free_return;\n+\n+\t/* only take up to \"lines\" lines, and strip the signature from a tag */\n+\tif (type == OBJ_TAG)\n+\t\tsize = parse_signature(buf, size);\n+\tfor (i = 0, sp += 2; i < lines && sp < buf + size; i++) {\n+\t\tif (i)\n+\t\t\tprintf(\"\\n    \");\n+\t\teol = memchr(sp, '\\n', size - (sp - buf));\n+\t\tlen = eol ? eol - sp : size - (sp - buf);\n+\t\tfwrite(sp, len, 1, stdout);\n+\t\tif (!eol)\n+\t\t\tbreak;\n+\t\tsp = eol + 1;\n+\t}\n+free_return:\n+\tfree(buf);\n+}\n+\n+void show_ref_array_item(struct ref_array_item *info, const char *format,\n+\t\t\t int quote_style, unsigned int lines)\n {\n \tconst char *cp, *sp, *ep;\n \tstruct ref_formatting_state state;\n@@ -1348,6 +1392,11 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n \t\tresetv.s = color;\n \t\tprint_value(&resetv, &state);\n \t}\n+\tif (lines > 0) {\n+\t\tstruct object_id oid;\n+\t\thashcpy(oid.hash, info->objectname);\n+\t\tshow_tag_lines(&oid, lines);\n+\t}\n \tputchar('\\n');\n }\n \ndiff --git a/ref-filter.h b/ref-filter.h\nindex 2b8462a..4d94ebf 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -64,6 +64,7 @@ struct ref_filter {\n \tstruct commit *merge_commit;\n \n \tunsigned int with_commit_tag_algo : 1;\n+\tunsigned int lines;\n };\n \n struct ref_filter_cbdata {\n@@ -95,8 +96,12 @@ 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-/*  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+/*\n+ * Print the ref using the given format and quote_style. If 'lines' > 0,\n+ * print that many lines of the the given ref.\n+ */\n+void show_ref_array_item(struct ref_array_item *info, const char *format,\n+\t\t\t int quote_style, unsigned int lines);\n /*  Callback function for parsing the sort option */\n int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);\n /*  Default sort option based on refname */\n-- \n2.4.6\n"},{"id":"267149","messageId":"1438270552-10333-6-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v7 06/11] ref-filter: add support to sort by version","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:47Z","receivedAt":"2015-07-30T15:35:47Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nAdd support to sort by version using the \"v:refname\" and\n\"version:refname\" option. This is achieved by using the 'versioncmp()'\nfunction as the comparing function for qsort.\n\nThis option is included to support sorting by versions in `git tag -l`\nwhich will eventaully be ported to use ref-filter APIs.\n\nAdd documentation and tests 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                       | 14 +++++++++-----\n ref-filter.h                       |  3 ++-\n t/t6302-for-each-ref-filter.sh     | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 50 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex 9961921..bcf319a 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -151,6 +151,9 @@ For sorting purposes, fields with numeric values sort in numeric\n order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).\n All other fields are used to sort in their byte-value order.\n \n+There is also an option to sort by versions, this can be done by using\n+the fieldname `version:refname` or its alias `v:refname`.\n+\n In any case, a field name that refers to a field inapplicable to\n the object referred by the ref does not cause an error.  It\n returns an empty string instead.\ndiff --git a/ref-filter.c b/ref-filter.c\nindex edb2c38..2b4a853 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -12,6 +12,7 @@\n #include \"revision.h\"\n #include \"utf8.h\"\n #include \"git-compat-util.h\"\n+#include \"version.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n@@ -1172,19 +1173,19 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru\n \n \tget_ref_atom_value(a, s->atom, &va);\n \tget_ref_atom_value(b, s->atom, &vb);\n-\tswitch (cmp_type) {\n-\tcase FIELD_STR:\n+\tif (s->version)\n+\t\tcmp = versioncmp(va->s, vb->s);\n+\telse if (cmp_type == FIELD_STR)\n \t\tcmp = strcmp(va->s, vb->s);\n-\t\tbreak;\n-\tdefault:\n+\telse {\n \t\tif (va->ul < vb->ul)\n \t\t\tcmp = -1;\n \t\telse if (va->ul == vb->ul)\n \t\t\tcmp = 0;\n \t\telse\n \t\t\tcmp = 1;\n-\t\tbreak;\n \t}\n+\n \treturn (s->reverse) ? -cmp : cmp;\n }\n \n@@ -1429,6 +1430,9 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)\n \t\ts->reverse = 1;\n \t\targ++;\n \t}\n+\tif (skip_prefix(arg, \"version:\", &arg) ||\n+\t    skip_prefix(arg, \"v:\", &arg))\n+\t\ts->version = 1;\n \tlen = strlen(arg);\n \ts->atom = parse_ref_filter_atom(arg, arg+len);\n \treturn 0;\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 4d94ebf..2bdae58 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -34,7 +34,8 @@ struct ref_formatting_state {\n struct ref_sorting {\n \tstruct ref_sorting *next;\n \tint atom; /* index into used_atom array (internal) */\n-\tunsigned reverse : 1;\n+\tunsigned reverse : 1,\n+\t\tversion : 1;\n };\n \n struct ref_array_item {\ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex 48caac9..b9ebb3d 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -113,4 +113,40 @@ test_expect_success 'no padding when `padright` length is smaller than atom leng\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'setup for version sort' '\n+\ttest_commit foo1.3 &&\n+\ttest_commit foo1.6 &&\n+\ttest_commit foo1.10\n+'\n+\n+test_expect_success 'version sort' '\n+\tgit for-each-ref --sort=version:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tfoo1.3\n+\tfoo1.6\n+\tfoo1.10\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'version sort (shortened)' '\n+\tgit for-each-ref --sort=v:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tfoo1.3\n+\tfoo1.6\n+\tfoo1.10\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'reverse version sort' '\n+\tgit for-each-ref --sort=-version:refname --format=\"%(refname:short)\" refs/tags/ | grep \"foo\" >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tfoo1.10\n+\tfoo1.6\n+\tfoo1.3\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.6\n"},{"id":"267164","messageId":"1438270552-10333-7-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v7 07/11] ref-filter: add option to match literal pattern","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:48Z","receivedAt":"2015-07-30T15:35:48Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nSince 'ref-filter' only has an option to match path names add an\noption for plain fnmatch pattern-matching.\n\nThis is to support the pattern matching options which are used in `git\ntag -l` and `git branch -l` where we can match patterns like `git tag\n-l foo*` which would match all tags which has a \"foo*\" pattern.\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/for-each-ref.c |  1 +\n ref-filter.c           | 39 ++++++++++++++++++++++++++++++++++++---\n ref-filter.h           |  3 ++-\n 3 files changed, 39 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex e4a4f8a..3ad6a64 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -68,6 +68,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)\n \tgit_config(git_default_config, NULL);\n \n \tfilter.name_patterns = argv;\n+\tfilter.match_as_path = 1;\n \tfilter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);\n \tref_array_sort(sorting, &array);\n \ndiff --git a/ref-filter.c b/ref-filter.c\nindex 2b4a853..65d168e 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -946,9 +946,32 @@ static int commit_contains(struct ref_filter *filter, struct commit *commit)\n \n /*\n  * Return 1 if the refname matches one of the patterns, otherwise 0.\n+ * A pattern can be a literal prefix (e.g. a refname \"refs/heads/master\"\n+ * matches a pattern \"refs/heads/mas\") or a wildcard (e.g. the same ref\n+ * matches \"refs/heads/mas*\", too).\n+ */\n+static int match_pattern(const char **patterns, const char *refname)\n+{\n+\t/*\n+\t * When no '--format' option is given we need to skip the prefix\n+\t * for matching refs of tags and branches.\n+\t */\n+\t(void)(skip_prefix(refname, \"refs/tags/\", &refname) ||\n+\t       skip_prefix(refname, \"refs/heads/\", &refname) ||\n+\t       skip_prefix(refname, \"refs/remotes/\", &refname));\n+\n+\tfor (; *patterns; patterns++) {\n+\t\tif (!wildmatch(*patterns, refname, 0, NULL))\n+\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Return 1 if the refname matches one of the patterns, otherwise 0.\n  * A pattern can be path prefix (e.g. a refname \"refs/heads/master\"\n- * matches a pattern \"refs/heads/\") or a wildcard (e.g. the same ref\n- * matches \"refs/heads/m*\",too).\n+ * matches a pattern \"refs/heads/\" but not \"refs/heads/m\") or a\n+ * wildcard (e.g. the same ref matches \"refs/heads/m*\", too).\n  */\n static int match_name_as_path(const char **pattern, const char *refname)\n {\n@@ -969,6 +992,16 @@ static int match_name_as_path(const char **pattern, const char *refname)\n \treturn 0;\n }\n \n+/* Return 1 if the refname matches one of the patterns, otherwise 0. */\n+static int filter_pattern_match(struct ref_filter *filter, const char *refname)\n+{\n+\tif (!*filter->name_patterns)\n+\t\treturn 1; /* No pattern always matches */\n+\tif (filter->match_as_path)\n+\t\treturn match_name_as_path(filter->name_patterns, refname);\n+\treturn match_pattern(filter->name_patterns, refname);\n+}\n+\n /*\n  * Given a ref (sha1, refname), check if the ref belongs to the array\n  * of sha1s. If the given ref is a tag, check if the given tag points\n@@ -1037,7 +1070,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,\n \t\treturn 0;\n \t}\n \n-\tif (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))\n+\tif (!filter_pattern_match(filter, refname))\n \t\treturn 0;\n \n \tif (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname))\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 2bdae58..b50a036 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -64,7 +64,8 @@ struct ref_filter {\n \t} merge;\n \tstruct commit *merge_commit;\n \n-\tunsigned int with_commit_tag_algo : 1;\n+\tunsigned int with_commit_tag_algo : 1,\n+\t\tmatch_as_path : 1;\n \tunsigned int lines;\n };\n \n-- \n2.4.6\n"},{"id":"267147","messageId":"1438270552-10333-8-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v7 08/11] tag.c: use 'ref-filter' data structures","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:49Z","receivedAt":"2015-07-30T15:35:49Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nMake 'tag.c' use 'ref-filter' data structures and make changes to\nsupport the new data structures. This is a part of the process\nof porting 'tag.c' to use 'ref-filter' APIs.\n\nThis is a temporary step before porting 'tag.c' to use 'ref-filter'\ncompletely. As this is a temporary step, most of the code\nintroduced here will be removed when 'tag.c' is ported over to use\n'ref-filter' APIs\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/tag.c | 106 +++++++++++++++++++++++++++++++---------------------------\n 1 file changed, 57 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 0fc7557..e96bae2 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -17,6 +17,7 @@\n #include \"gpg-interface.h\"\n #include \"sha1-array.h\"\n #include \"column.h\"\n+#include \"ref-filter.h\"\n \n static const char * const git_tag_usage[] = {\n \tN_(\"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]\"),\n@@ -34,15 +35,6 @@ static const char * const git_tag_usage[] = {\n \n static int tag_sort;\n \n-struct tag_filter {\n-\tconst char **patterns;\n-\tint lines;\n-\tint sort;\n-\tstruct string_list tags;\n-\tstruct commit_list *with_commit;\n-};\n-\n-static struct sha1_array points_at;\n static unsigned int colopts;\n \n static int match_pattern(const char **patterns, const char *ref)\n@@ -61,19 +53,20 @@ static int match_pattern(const char **patterns, const char *ref)\n  * removed as we port tag.c to use the ref-filter APIs.\n  */\n static const unsigned char *match_points_at(const char *refname,\n-\t\t\t\t\t    const unsigned char *sha1)\n+\t\t\t\t\t    const unsigned char *sha1,\n+\t\t\t\t\t    struct sha1_array *points_at)\n {\n \tconst unsigned char *tagged_sha1 = NULL;\n \tstruct object *obj;\n \n-\tif (sha1_array_lookup(&points_at, sha1) >= 0)\n+\tif (sha1_array_lookup(points_at, sha1) >= 0)\n \t\treturn sha1;\n \tobj = parse_object(sha1);\n \tif (!obj)\n \t\tdie(_(\"malformed object at '%s'\"), refname);\n \tif (obj->type == OBJ_TAG)\n \t\ttagged_sha1 = ((struct tag *)obj)->tagged->sha1;\n-\tif (tagged_sha1 && sha1_array_lookup(&points_at, tagged_sha1) >= 0)\n+\tif (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)\n \t\treturn tagged_sha1;\n \treturn NULL;\n }\n@@ -228,12 +221,24 @@ free_return:\n \tfree(buf);\n }\n \n+static void ref_array_append(struct ref_array *array, const char *refname)\n+{\n+\tsize_t len = strlen(refname);\n+\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);\n+\tmemcpy(ref->refname, refname, len);\n+\tref->refname[len] = '\\0';\n+\tREALLOC_ARRAY(array->items, array->nr + 1);\n+\tarray->items[array->nr++] = ref;\n+}\n+\n static int show_reference(const char *refname, const struct object_id *oid,\n \t\t\t  int flag, void *cb_data)\n {\n-\tstruct tag_filter *filter = cb_data;\n+\tstruct ref_filter_cbdata *data = cb_data;\n+\tstruct ref_array *array = data->array;\n+\tstruct ref_filter *filter = data->filter;\n \n-\tif (match_pattern(filter->patterns, refname)) {\n+\tif (match_pattern(filter->name_patterns, refname)) {\n \t\tif (filter->with_commit) {\n \t\t\tstruct commit *commit;\n \n@@ -244,12 +249,12 @@ static int show_reference(const char *refname, const struct object_id *oid,\n \t\t\t\treturn 0;\n \t\t}\n \n-\t\tif (points_at.nr && !match_points_at(refname, oid->hash))\n+\t\tif (filter->points_at.nr && !match_points_at(refname, oid->hash, &filter->points_at))\n \t\t\treturn 0;\n \n \t\tif (!filter->lines) {\n-\t\t\tif (filter->sort)\n-\t\t\t\tstring_list_append(&filter->tags, refname);\n+\t\t\tif (tag_sort)\n+\t\t\t\tref_array_append(array, refname);\n \t\t\telse\n \t\t\t\tprintf(\"%s\\n\", refname);\n \t\t\treturn 0;\n@@ -264,36 +269,36 @@ static int show_reference(const char *refname, const struct object_id *oid,\n \n static int sort_by_version(const void *a_, const void *b_)\n {\n-\tconst struct string_list_item *a = a_;\n-\tconst struct string_list_item *b = b_;\n-\treturn versioncmp(a->string, b->string);\n+\tconst struct ref_array_item *a = *((struct ref_array_item **)a_);\n+\tconst struct ref_array_item *b = *((struct ref_array_item **)b_);\n+\treturn versioncmp(a->refname, b->refname);\n }\n \n-static int list_tags(const char **patterns, int lines,\n-\t\t     struct commit_list *with_commit, int sort)\n+static int list_tags(struct ref_filter *filter, int sort)\n {\n-\tstruct tag_filter filter;\n+\tstruct ref_array array;\n+\tstruct ref_filter_cbdata data;\n+\n+\tmemset(&array, 0, sizeof(array));\n+\tdata.array = &array;\n+\tdata.filter = filter;\n \n-\tfilter.patterns = patterns;\n-\tfilter.lines = lines;\n-\tfilter.sort = sort;\n-\tfilter.with_commit = with_commit;\n-\tmemset(&filter.tags, 0, sizeof(filter.tags));\n-\tfilter.tags.strdup_strings = 1;\n+\tif (filter->lines == -1)\n+\t\tfilter->lines = 0;\n \n-\tfor_each_tag_ref(show_reference, (void *)&filter);\n+\tfor_each_tag_ref(show_reference, &data);\n \tif (sort) {\n \t\tint i;\n \t\tif ((sort & SORT_MASK) == VERCMP_SORT)\n-\t\t\tqsort(filter.tags.items, filter.tags.nr,\n-\t\t\t      sizeof(struct string_list_item), sort_by_version);\n+\t\t\tqsort(array.items, array.nr,\n+\t\t\t      sizeof(struct ref_array_item *), sort_by_version);\n \t\tif (sort & REVERSE_SORT)\n-\t\t\tfor (i = filter.tags.nr - 1; i >= 0; i--)\n-\t\t\t\tprintf(\"%s\\n\", filter.tags.items[i].string);\n+\t\t\tfor (i = array.nr - 1; i >= 0; i--)\n+\t\t\t\tprintf(\"%s\\n\", array.items[i]->refname);\n \t\telse\n-\t\t\tfor (i = 0; i < filter.tags.nr; i++)\n-\t\t\t\tprintf(\"%s\\n\", filter.tags.items[i].string);\n-\t\tstring_list_clear(&filter.tags, 0);\n+\t\t\tfor (i = 0; i < array.nr; i++)\n+\t\t\t\tprintf(\"%s\\n\", array.items[i]->refname);\n+\t\tref_array_clear(&array);\n \t}\n \treturn 0;\n }\n@@ -574,17 +579,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tconst char *object_ref, *tag;\n \tstruct create_tag_options opt;\n \tchar *cleanup_arg = NULL;\n-\tint annotate = 0, force = 0, lines = -1;\n \tint create_reflog = 0;\n+\tint annotate = 0, force = 0;\n \tint cmdmode = 0;\n \tconst char *msgfile = NULL, *keyid = NULL;\n \tstruct msg_arg msg = { 0, STRBUF_INIT };\n-\tstruct commit_list *with_commit = NULL;\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n+\tstruct ref_filter filter;\n \tstruct option options[] = {\n \t\tOPT_CMDMODE('l', \"list\", &cmdmode, N_(\"list tag names\"), 'l'),\n-\t\t{ OPTION_INTEGER, 'n', NULL, &lines, N_(\"n\"),\n+\t\t{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_(\"n\"),\n \t\t\t\tN_(\"print <n> lines of each tag message\"),\n \t\t\t\tPARSE_OPT_OPTARG, NULL, 1 },\n \t\tOPT_CMDMODE('d', \"delete\", &cmdmode, N_(\"delete tags\"), 'd'),\n@@ -606,14 +611,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \n \t\tOPT_GROUP(N_(\"Tag listing options\")),\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show tag list in columns\")),\n-\t\tOPT_CONTAINS(&with_commit, N_(\"print only tags that contain the commit\")),\n-\t\tOPT_WITH(&with_commit, N_(\"print only tags that contain the commit\")),\n+\t\tOPT_CONTAINS(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n+\t\tOPT_WITH(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n \t\t{\n \t\t\tOPTION_CALLBACK, 0, \"sort\", &tag_sort, N_(\"type\"), N_(\"sort tags\"),\n \t\t\tPARSE_OPT_NONEG, parse_opt_sort\n \t\t},\n \t\t{\n-\t\t\tOPTION_CALLBACK, 0, \"points-at\", &points_at, N_(\"object\"),\n+\t\t\tOPTION_CALLBACK, 0, \"points-at\", &filter.points_at, N_(\"object\"),\n \t\t\tN_(\"print only tags of the object\"), 0, parse_opt_object_name\n \t\t},\n \t\tOPT_END()\n@@ -622,6 +627,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tgit_config(git_tag_config, NULL);\n \n \tmemset(&opt, 0, sizeof(opt));\n+\tmemset(&filter, 0, sizeof(filter));\n+\tfilter.lines = -1;\n \n \targc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);\n \n@@ -638,7 +645,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options(git_tag_usage, options);\n \n \tfinalize_colopts(&colopts, -1);\n-\tif (cmdmode == 'l' && lines != -1) {\n+\tif (cmdmode == 'l' && filter.lines != -1) {\n \t\tif (explicitly_enable_column(colopts))\n \t\t\tdie(_(\"--column and -n are incompatible\"));\n \t\tcolopts = 0;\n@@ -651,18 +658,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tcopts.padding = 2;\n \t\t\trun_column_filter(colopts, &copts);\n \t\t}\n-\t\tif (lines != -1 && tag_sort)\n+\t\tif (filter.lines != -1 && tag_sort)\n \t\t\tdie(_(\"--sort and -n are incompatible\"));\n-\t\tret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, tag_sort);\n+\t\tfilter.name_patterns = argv;\n+\t\tret = list_tags(&filter, tag_sort);\n \t\tif (column_active(colopts))\n \t\t\tstop_column_filter();\n \t\treturn ret;\n \t}\n-\tif (lines != -1)\n+\tif (filter.lines != -1)\n \t\tdie(_(\"-n option is only allowed with -l.\"));\n-\tif (with_commit)\n+\tif (filter.with_commit)\n \t\tdie(_(\"--contains option is only allowed with -l.\"));\n-\tif (points_at.nr)\n+\tif (filter.points_at.nr)\n \t\tdie(_(\"--points-at option is only allowed with -l.\"));\n \tif (cmdmode == 'd')\n \t\treturn for_each_tag_name(argv, delete_tag);\n-- \n2.4.6\n"},{"id":"267150","messageId":"1438270552-10333-9-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v7 09/11] tag.c: use 'ref-filter' APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:50Z","receivedAt":"2015-07-30T15:35:50Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nMake 'tag.c' use 'ref-filter' APIs for iterating through refs, sorting\nand printing of refs. This removes most of the code used in 'tag.c'\nreplacing it with calls to the 'ref-filter' library.\n\nMake 'tag.c' use the 'filter_refs()' function provided by 'ref-filter'\nto filter out tags based on the options set.\n\nFor printing tags we use 'show_ref_array_item()' function provided by\n'ref-filter'.\n\nWe improve the sorting option provided by 'tag.c' by using the sorting\noptions provided by 'ref-filter'. This causes the test 'invalid sort\nparameter on command line' in t7004 to fail, as 'ref-filter' throws an\nerror for all sorting fields which are incorrect. The test is changed\nto reflect the same.\n\nModify 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-tag.txt |  16 ++-\n builtin/tag.c             | 342 ++++++----------------------------------------\n t/t7004-tag.sh            |   8 +-\n 3 files changed, 50 insertions(+), 316 deletions(-)\n\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 84f6496..3ac4a96 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -13,7 +13,7 @@ SYNOPSIS\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]\n-\t[--column[=<options>] | --no-column] [--create-reflog] [<pattern>...]\n+\t[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] [<pattern>...]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -94,14 +94,16 @@ OPTIONS\n \tusing fnmatch(3)).  Multiple patterns may be given; if any of\n \tthem matches, the tag is shown.\n \n---sort=<type>::\n-\tSort in a specific order. Supported type is \"refname\"\n-\t(lexicographic order), \"version:refname\" or \"v:refname\" (tag\n+--sort=<key>::\n+\tSort based on the key given.  Prefix `-` to sort in\n+\tdescending order of the value. You may use the --sort=<key> option\n+\tmultiple times, in which case the last key becomes the primary\n+\tkey. Also supports \"version:refname\" or \"v:refname\" (tag\n \tnames are treated as versions). The \"version:refname\" sort\n \torder can also be affected by the\n-\t\"versionsort.prereleaseSuffix\" configuration variable. Prepend\n-\t\"-\" to reverse sort order. When this option is not given, the\n-\tsort order defaults to the value configured for the 'tag.sort'\n+\t\"versionsort.prereleaseSuffix\" configuration variable.\n+\tThe keys supported are the same as those in `git for-each-ref`.\n+\tSort order defaults to the value configured for the 'tag.sort'\n \tvariable if it exists, or lexicographic order otherwise. See\n \tlinkgit:git-config[1].\n \ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex e96bae2..1e8d1b2 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -28,278 +28,32 @@ static const char * const git_tag_usage[] = {\n \tNULL\n };\n \n-#define STRCMP_SORT     0\t/* must be zero */\n-#define VERCMP_SORT     1\n-#define SORT_MASK       0x7fff\n-#define REVERSE_SORT    0x8000\n-\n-static int tag_sort;\n-\n static unsigned int colopts;\n \n-static int match_pattern(const char **patterns, const char *ref)\n-{\n-\t/* no pattern means match everything */\n-\tif (!*patterns)\n-\t\treturn 1;\n-\tfor (; *patterns; patterns++)\n-\t\tif (!wildmatch(*patterns, ref, 0, NULL))\n-\t\t\treturn 1;\n-\treturn 0;\n-}\n-\n-/*\n- * This is currently duplicated in ref-filter.c, and will eventually be\n- * removed as we port tag.c to use the ref-filter APIs.\n- */\n-static const unsigned char *match_points_at(const char *refname,\n-\t\t\t\t\t    const unsigned char *sha1,\n-\t\t\t\t\t    struct sha1_array *points_at)\n-{\n-\tconst unsigned char *tagged_sha1 = NULL;\n-\tstruct object *obj;\n-\n-\tif (sha1_array_lookup(points_at, sha1) >= 0)\n-\t\treturn sha1;\n-\tobj = parse_object(sha1);\n-\tif (!obj)\n-\t\tdie(_(\"malformed object at '%s'\"), refname);\n-\tif (obj->type == OBJ_TAG)\n-\t\ttagged_sha1 = ((struct tag *)obj)->tagged->sha1;\n-\tif (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)\n-\t\treturn tagged_sha1;\n-\treturn NULL;\n-}\n-\n-static int in_commit_list(const struct commit_list *want, struct commit *c)\n-{\n-\tfor (; want; want = want->next)\n-\t\tif (!hashcmp(want->item->object.sha1, c->object.sha1))\n-\t\t\treturn 1;\n-\treturn 0;\n-}\n-\n-/*\n- * The entire code segment for supporting the --contains option has been\n- * copied over to ref-filter.{c,h}. This will be deleted evetually when\n- * we port tag.c to use ref-filter APIs.\n- */\n-enum contains_result {\n-\tCONTAINS_UNKNOWN = -1,\n-\tCONTAINS_NO = 0,\n-\tCONTAINS_YES = 1\n-};\n-\n-/*\n- * Test whether the candidate or one of its parents is contained in the list.\n- * Do not recurse to find out, though, but return -1 if inconclusive.\n- */\n-static enum contains_result contains_test(struct commit *candidate,\n-\t\t\t    const struct commit_list *want)\n-{\n-\t/* was it previously marked as containing a want commit? */\n-\tif (candidate->object.flags & TMP_MARK)\n-\t\treturn 1;\n-\t/* or marked as not possibly containing a want commit? */\n-\tif (candidate->object.flags & UNINTERESTING)\n-\t\treturn 0;\n-\t/* or are we it? */\n-\tif (in_commit_list(want, candidate)) {\n-\t\tcandidate->object.flags |= TMP_MARK;\n-\t\treturn 1;\n-\t}\n-\n-\tif (parse_commit(candidate) < 0)\n-\t\treturn 0;\n-\n-\treturn -1;\n-}\n-\n-/*\n- * Mimicking the real stack, this stack lives on the heap, avoiding stack\n- * overflows.\n- *\n- * At each recursion step, the stack items points to the commits whose\n- * ancestors are to be inspected.\n- */\n-struct stack {\n-\tint nr, alloc;\n-\tstruct stack_entry {\n-\t\tstruct commit *commit;\n-\t\tstruct commit_list *parents;\n-\t} *stack;\n-};\n-\n-static void push_to_stack(struct commit *candidate, struct stack *stack)\n-{\n-\tint index = stack->nr++;\n-\tALLOC_GROW(stack->stack, stack->nr, stack->alloc);\n-\tstack->stack[index].commit = candidate;\n-\tstack->stack[index].parents = candidate->parents;\n-}\n-\n-static enum contains_result contains(struct commit *candidate,\n-\t\tconst struct commit_list *want)\n-{\n-\tstruct stack stack = { 0, 0, NULL };\n-\tint result = contains_test(candidate, want);\n-\n-\tif (result != CONTAINS_UNKNOWN)\n-\t\treturn result;\n-\n-\tpush_to_stack(candidate, &stack);\n-\twhile (stack.nr) {\n-\t\tstruct stack_entry *entry = &stack.stack[stack.nr - 1];\n-\t\tstruct commit *commit = entry->commit;\n-\t\tstruct commit_list *parents = entry->parents;\n-\n-\t\tif (!parents) {\n-\t\t\tcommit->object.flags |= UNINTERESTING;\n-\t\t\tstack.nr--;\n-\t\t}\n-\t\t/*\n-\t\t * If we just popped the stack, parents->item has been marked,\n-\t\t * therefore contains_test will return a meaningful 0 or 1.\n-\t\t */\n-\t\telse switch (contains_test(parents->item, want)) {\n-\t\tcase CONTAINS_YES:\n-\t\t\tcommit->object.flags |= TMP_MARK;\n-\t\t\tstack.nr--;\n-\t\t\tbreak;\n-\t\tcase CONTAINS_NO:\n-\t\t\tentry->parents = parents->next;\n-\t\t\tbreak;\n-\t\tcase CONTAINS_UNKNOWN:\n-\t\t\tpush_to_stack(parents->item, &stack);\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\tfree(stack.stack);\n-\treturn contains_test(candidate, want);\n-}\n-\n-/*\n- * Currently duplicated in ref-filter, will eventually be removed as\n- * we port tag.c to use ref-filter APIs.\n- */\n-static void show_tag_lines(const struct object_id *oid, int lines)\n-{\n-\tint i;\n-\tunsigned long size;\n-\tenum object_type type;\n-\tchar *buf, *sp, *eol;\n-\tsize_t len;\n-\n-\tbuf = read_sha1_file(oid->hash, &type, &size);\n-\tif (!buf)\n-\t\tdie_errno(\"unable to read object %s\", oid_to_hex(oid));\n-\tif (type != OBJ_COMMIT && type != OBJ_TAG)\n-\t\tgoto free_return;\n-\tif (!size)\n-\t\tdie(\"an empty %s object %s?\",\n-\t\t    typename(type), oid_to_hex(oid));\n-\n-\t/* skip header */\n-\tsp = strstr(buf, \"\\n\\n\");\n-\tif (!sp)\n-\t\tgoto free_return;\n-\n-\t/* only take up to \"lines\" lines, and strip the signature from a tag */\n-\tif (type == OBJ_TAG)\n-\t\tsize = parse_signature(buf, size);\n-\tfor (i = 0, sp += 2; i < lines && sp < buf + size; i++) {\n-\t\tif (i)\n-\t\t\tprintf(\"\\n    \");\n-\t\teol = memchr(sp, '\\n', size - (sp - buf));\n-\t\tlen = eol ? eol - sp : size - (sp - buf);\n-\t\tfwrite(sp, len, 1, stdout);\n-\t\tif (!eol)\n-\t\t\tbreak;\n-\t\tsp = eol + 1;\n-\t}\n-free_return:\n-\tfree(buf);\n-}\n-\n-static void ref_array_append(struct ref_array *array, const char *refname)\n-{\n-\tsize_t len = strlen(refname);\n-\tstruct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);\n-\tmemcpy(ref->refname, refname, len);\n-\tref->refname[len] = '\\0';\n-\tREALLOC_ARRAY(array->items, array->nr + 1);\n-\tarray->items[array->nr++] = ref;\n-}\n-\n-static int show_reference(const char *refname, const struct object_id *oid,\n-\t\t\t  int flag, void *cb_data)\n-{\n-\tstruct ref_filter_cbdata *data = cb_data;\n-\tstruct ref_array *array = data->array;\n-\tstruct ref_filter *filter = data->filter;\n-\n-\tif (match_pattern(filter->name_patterns, refname)) {\n-\t\tif (filter->with_commit) {\n-\t\t\tstruct commit *commit;\n-\n-\t\t\tcommit = lookup_commit_reference_gently(oid->hash, 1);\n-\t\t\tif (!commit)\n-\t\t\t\treturn 0;\n-\t\t\tif (!contains(commit, filter->with_commit))\n-\t\t\t\treturn 0;\n-\t\t}\n-\n-\t\tif (filter->points_at.nr && !match_points_at(refname, oid->hash, &filter->points_at))\n-\t\t\treturn 0;\n-\n-\t\tif (!filter->lines) {\n-\t\t\tif (tag_sort)\n-\t\t\t\tref_array_append(array, refname);\n-\t\t\telse\n-\t\t\t\tprintf(\"%s\\n\", refname);\n-\t\t\treturn 0;\n-\t\t}\n-\t\tprintf(\"%-15s \", refname);\n-\t\tshow_tag_lines(oid, filter->lines);\n-\t\tputchar('\\n');\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int sort_by_version(const void *a_, const void *b_)\n-{\n-\tconst struct ref_array_item *a = *((struct ref_array_item **)a_);\n-\tconst struct ref_array_item *b = *((struct ref_array_item **)b_);\n-\treturn versioncmp(a->refname, b->refname);\n-}\n-\n-static int list_tags(struct ref_filter *filter, int sort)\n+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)\n {\n \tstruct ref_array array;\n-\tstruct ref_filter_cbdata data;\n+\tchar *format;\n+\tint i;\n \n \tmemset(&array, 0, sizeof(array));\n-\tdata.array = &array;\n-\tdata.filter = filter;\n \n \tif (filter->lines == -1)\n \t\tfilter->lines = 0;\n \n-\tfor_each_tag_ref(show_reference, &data);\n-\tif (sort) {\n-\t\tint i;\n-\t\tif ((sort & SORT_MASK) == VERCMP_SORT)\n-\t\t\tqsort(array.items, array.nr,\n-\t\t\t      sizeof(struct ref_array_item *), sort_by_version);\n-\t\tif (sort & REVERSE_SORT)\n-\t\t\tfor (i = array.nr - 1; i >= 0; i--)\n-\t\t\t\tprintf(\"%s\\n\", array.items[i]->refname);\n-\t\telse\n-\t\t\tfor (i = 0; i < array.nr; i++)\n-\t\t\t\tprintf(\"%s\\n\", array.items[i]->refname);\n-\t\tref_array_clear(&array);\n-\t}\n+\tif (filter->lines)\n+\t\tformat = \"%(padright:16)%(refname:short)\";\n+\telse\n+\t\tformat = \"%(refname:short)\";\n+\n+\tverify_ref_format(format);\n+\tfilter_refs(&array, filter, FILTER_REFS_TAGS);\n+\tref_array_sort(sorting, &array);\n+\n+\tfor (i = 0; i < array.nr; i++)\n+\t\tshow_ref_array_item(array.items[i], format, QUOTE_NONE, filter->lines);\n+\tref_array_clear(&array);\n+\n \treturn 0;\n }\n \n@@ -366,35 +120,26 @@ static const char tag_template_nocleanup[] =\n \t\"Lines starting with '%c' will be kept; you may remove them\"\n \t\" yourself if you want to.\\n\");\n \n-/*\n- * Parse a sort string, and return 0 if parsed successfully. Will return\n- * non-zero when the sort string does not parse into a known type. If var is\n- * given, the error message becomes a warning and includes information about\n- * the configuration value.\n- */\n-static int parse_sort_string(const char *var, const char *arg, int *sort)\n+/* Parse arg given and add it the ref_sorting array */\n+static int parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)\n {\n-\tint type = 0, flags = 0;\n-\n-\tif (skip_prefix(arg, \"-\", &arg))\n-\t\tflags |= REVERSE_SORT;\n+\tstruct ref_sorting *s;\n+\tint len;\n \n-\tif (skip_prefix(arg, \"version:\", &arg) || skip_prefix(arg, \"v:\", &arg))\n-\t\ttype = VERCMP_SORT;\n-\telse\n-\t\ttype = STRCMP_SORT;\n+\ts = xcalloc(1, sizeof(*s));\n+\ts->next = *sorting_tail;\n+\t*sorting_tail = s;\n \n-\tif (strcmp(arg, \"refname\")) {\n-\t\tif (!var)\n-\t\t\treturn error(_(\"unsupported sort specification '%s'\"), arg);\n-\t\telse {\n-\t\t\twarning(_(\"unsupported sort specification '%s' in variable '%s'\"),\n-\t\t\t\tvar, arg);\n-\t\t\treturn -1;\n-\t\t}\n+\tif (*arg == '-') {\n+\t\ts->reverse = 1;\n+\t\targ++;\n \t}\n+\tif (skip_prefix(arg, \"version:\", &arg) ||\n+\t    skip_prefix(arg, \"v:\", &arg))\n+\t\ts->version = 1;\n \n-\t*sort = (type | flags);\n+\tlen = strlen(arg);\n+\ts->atom = parse_ref_filter_atom(arg, arg+len);\n \n \treturn 0;\n }\n@@ -402,11 +147,12 @@ static int parse_sort_string(const char *var, const char *arg, int *sort)\n static int git_tag_config(const char *var, const char *value, void *cb)\n {\n \tint status;\n+\tstruct ref_sorting **sorting_tail = (struct ref_sorting **)cb;\n \n \tif (!strcmp(var, \"tag.sort\")) {\n \t\tif (!value)\n \t\t\treturn config_error_nonbool(var);\n-\t\tparse_sort_string(var, value, &tag_sort);\n+\t\tparse_sorting_string(value, sorting_tail);\n \t\treturn 0;\n \t}\n \n@@ -564,13 +310,6 @@ static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)\n \treturn check_refname_format(sb->buf, 0);\n }\n \n-static int parse_opt_sort(const struct option *opt, const char *arg, int unset)\n-{\n-\tint *sort = opt->value;\n-\n-\treturn parse_sort_string(NULL, arg, sort);\n-}\n-\n int cmd_tag(int argc, const char **argv, const char *prefix)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -587,6 +326,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tstruct ref_transaction *transaction;\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_filter filter;\n+\tstatic struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;\n \tstruct option options[] = {\n \t\tOPT_CMDMODE('l', \"list\", &cmdmode, N_(\"list tag names\"), 'l'),\n \t\t{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_(\"n\"),\n@@ -613,10 +353,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show tag list in columns\")),\n \t\tOPT_CONTAINS(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n \t\tOPT_WITH(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n-\t\t{\n-\t\t\tOPTION_CALLBACK, 0, \"sort\", &tag_sort, N_(\"type\"), N_(\"sort tags\"),\n-\t\t\tPARSE_OPT_NONEG, parse_opt_sort\n-\t\t},\n+\t\tOPT_CALLBACK(0 , \"sort\", sorting_tail, N_(\"key\"),\n+\t\t\t     N_(\"field name to sort on\"), &parse_opt_ref_sorting),\n \t\t{\n \t\t\tOPTION_CALLBACK, 0, \"points-at\", &filter.points_at, N_(\"object\"),\n \t\t\tN_(\"print only tags of the object\"), 0, parse_opt_object_name\n@@ -624,7 +362,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tgit_config(git_tag_config, NULL);\n+\tgit_config(git_tag_config, sorting_tail);\n \n \tmemset(&opt, 0, sizeof(opt));\n \tmemset(&filter, 0, sizeof(filter));\n@@ -650,6 +388,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tdie(_(\"--column and -n are incompatible\"));\n \t\tcolopts = 0;\n \t}\n+\tif (!sorting)\n+\t\tsorting = ref_default_sorting();\n \tif (cmdmode == 'l') {\n \t\tint ret;\n \t\tif (column_active(colopts)) {\n@@ -658,10 +398,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tcopts.padding = 2;\n \t\t\trun_column_filter(colopts, &copts);\n \t\t}\n-\t\tif (filter.lines != -1 && tag_sort)\n-\t\t\tdie(_(\"--sort and -n are incompatible\"));\n \t\tfilter.name_patterns = argv;\n-\t\tret = list_tags(&filter, tag_sort);\n+\t\tret = list_tags(&filter, sorting);\n \t\tif (column_active(colopts))\n \t\t\tstop_column_filter();\n \t\treturn ret;\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex d31788c..1f066aa 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -1462,13 +1462,7 @@ test_expect_success 'invalid sort parameter on command line' '\n \n test_expect_success 'invalid sort parameter in configuratoin' '\n \tgit config tag.sort \"v:notvalid\" &&\n-\tgit tag -l \"foo*\" >actual &&\n-\tcat >expect <<-\\EOF &&\n-\tfoo1.10\n-\tfoo1.3\n-\tfoo1.6\n-\tEOF\n-\ttest_cmp expect actual\n+\ttest_must_fail git tag -l \"foo*\" >actual\n '\n \n test_expect_success 'version sort with prerelease reordering' '\n-- \n2.4.6\n"},{"id":"267148","messageId":"1438270552-10333-10-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v7 10/11] tag.c: implement '--format' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:51Z","receivedAt":"2015-07-30T15:35:51Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nImplement the '--format' option provided by 'ref-filter'.\nThis lets the user list tags as per desired format similar\nto the implementation in '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-tag.txt | 15 ++++++++++++++-\n builtin/tag.c             | 11 +++++++----\n t/t7004-tag.sh            | 16 ++++++++++++++++\n 3 files changed, 37 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 3ac4a96..75703c5 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -13,7 +13,8 @@ SYNOPSIS\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]\n-\t[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] [<pattern>...]\n+\t[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]\n+\t[--format=<format>] [<pattern>...]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -158,6 +159,18 @@ This option is only applicable when listing tags without annotation lines.\n \tThe object that the new tag will refer to, usually a commit.\n \tDefaults to HEAD.\n \n+<format>::\n+\tA string that interpolates `%(fieldname)` from the object\n+\tpointed at by a ref being shown.  If `fieldname` is prefixed\n+\twith an asterisk (`*`) and the ref points at a tag object, the\n+\tvalue for the field in the object tag refers is used.  When\n+\tunspecified, defaults to `%(refname:short)`.  It also\n+\tinterpolates `%%` to `%`, and `%xx` where `xx` are hex digits\n+\tinterpolates to character with hex code `xx`; for example\n+\t`%00` interpolates to `\\0` (NUL), `%09` to `\\t` (TAB) and\n+\t`%0a` to `\\n` (LF).  The fields are same as those in `git\n+\tfor-each-ref`.\n+\n \n CONFIGURATION\n -------------\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 1e8d1b2..7de49c4 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -30,10 +30,9 @@ static const char * const git_tag_usage[] = {\n \n static unsigned int colopts;\n \n-static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)\n+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)\n {\n \tstruct ref_array array;\n-\tchar *format;\n \tint i;\n \n \tmemset(&array, 0, sizeof(array));\n@@ -43,7 +42,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)\n \n \tif (filter->lines)\n \t\tformat = \"%(padright:16)%(refname:short)\";\n-\telse\n+\telse if (!format)\n \t\tformat = \"%(refname:short)\";\n \n \tverify_ref_format(format);\n@@ -327,6 +326,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \tstruct strbuf err = STRBUF_INIT;\n \tstruct ref_filter filter;\n \tstatic struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;\n+\tconst char *format = NULL;\n \tstruct option options[] = {\n \t\tOPT_CMDMODE('l', \"list\", &cmdmode, N_(\"list tag names\"), 'l'),\n \t\t{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_(\"n\"),\n@@ -359,6 +359,7 @@ int cmd_tag(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 tags 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@@ -398,8 +399,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\t\tcopts.padding = 2;\n \t\t\trun_column_filter(colopts, &copts);\n \t\t}\n+\t\tif (format && (filter.lines != -1))\n+\t\t\tdie(_(\"--format and -n are incompatible\"));\n \t\tfilter.name_patterns = argv;\n-\t\tret = list_tags(&filter, sorting);\n+\t\tret = list_tags(&filter, sorting, format);\n \t\tif (column_active(colopts))\n \t\t\tstop_column_filter();\n \t\treturn ret;\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex 1f066aa..1809011 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -1519,4 +1519,20 @@ EOF\"\n \ttest_cmp expect actual\n '\n \n+test_expect_success '--format cannot be used with -n' '\n+\ttest_must_fail git tag -l -n4 --format=\"%(refname)\"\n+'\n+\n+test_expect_success '--format should list tags as per format given' '\n+\tcat >expect <<-\\EOF &&\n+\trefname : refs/tags/foo1.10\n+\trefname : refs/tags/foo1.3\n+\trefname : refs/tags/foo1.6\n+\trefname : refs/tags/foo1.6-rc1\n+\trefname : refs/tags/foo1.6-rc2\n+\tEOF\n+\tgit tag -l --format=\"refname : %(refname)\" \"foo*\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.6\n"},{"id":"293767","messageId":"1438270552-10333-11-git-send-email-Karthik.188@gmail.com","threadId":"39951","inReplyTo":"1438270552-10333-1-git-send-email-Karthik.188@gmail.com","subject":"[PATCH v7 11/11] tag.c: implement '--merged' and '--no-merged' options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-07-30T15:35:52Z","receivedAt":"2015-07-30T15:35:52Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nUsing 'ref-filter' APIs implement the '--merged' and '--no-merged'\noptions into 'tag.c'. The '--merged' option lets the user to only\nlist tags merged into the named commit. The '--no-merged' option\nlets the user to only list tags not merged into the named commit.\nIf no object is provided it assumes HEAD as the object.\n\nAdd documentation and tests 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-tag.txt |  7 ++++++-\n builtin/tag.c             |  6 +++++-\n t/t7004-tag.sh            | 27 +++++++++++++++++++++++++++\n 3 files changed, 38 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 75703c5..c2785d9 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -14,7 +14,7 @@ SYNOPSIS\n 'git tag' -d <tagname>...\n 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]\n \t[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]\n-\t[--format=<format>] [<pattern>...]\n+\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -171,6 +171,11 @@ This option is only applicable when listing tags without annotation lines.\n \t`%0a` to `\\n` (LF).  The fields are same as those in `git\n \tfor-each-ref`.\n \n+--[no-]merged [<commit>]::\n+\tOnly list tags whose tips are reachable, or not reachable\n+\tif '--no-merged' is used, from the specified commit ('HEAD'\n+\tif not specified).\n+\n \n CONFIGURATION\n -------------\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 7de49c4..fc01117 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -23,7 +23,7 @@ static const char * const git_tag_usage[] = {\n \tN_(\"git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]\"),\n \tN_(\"git tag -d <tagname>...\"),\n \tN_(\"git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]\"\n-\t\t\"\\n\\t\\t[<pattern>...]\"),\n+\t\t\"\\n\\t\\t[--[no-]merged [<commit>]] [<pattern>...]\"),\n \tN_(\"git tag -v <tagname>...\"),\n \tNULL\n };\n@@ -353,6 +353,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_COLUMN(0, \"column\", &colopts, N_(\"show tag list in columns\")),\n \t\tOPT_CONTAINS(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n \t\tOPT_WITH(&filter.with_commit, N_(\"print only tags that contain the commit\")),\n+\t\tOPT_MERGED(&filter, N_(\"print only tags that are merged\")),\n+\t\tOPT_NO_MERGED(&filter, N_(\"print only tags that are not merged\")),\n \t\tOPT_CALLBACK(0 , \"sort\", sorting_tail, N_(\"key\"),\n \t\t\t     N_(\"field name to sort on\"), &parse_opt_ref_sorting),\n \t\t{\n@@ -413,6 +415,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"--contains option is only allowed with -l.\"));\n \tif (filter.points_at.nr)\n \t\tdie(_(\"--points-at option is only allowed with -l.\"));\n+\tif (filter.merge_commit)\n+\t\tdie(_(\"--merged and --no-merged option are only allowed with -l\"));\n \tif (cmdmode == 'd')\n \t\treturn for_each_tag_name(argv, delete_tag);\n \tif (cmdmode == 'v')\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex 1809011..5b73539 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -1535,4 +1535,31 @@ test_expect_success '--format should list tags as per format given' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'setup --merged test tags' '\n+\tgit tag mergetest-1 HEAD~2 &&\n+\tgit tag mergetest-2 HEAD~1 &&\n+\tgit tag mergetest-3 HEAD\n+'\n+\n+test_expect_success '--merged cannot be used in non-list mode' '\n+\ttest_must_fail git tag --merged=mergetest-2 foo\n+'\n+\n+test_expect_success '--merged shows merged tags' '\n+\tcat >expect <<-\\EOF &&\n+\tmergetest-1\n+\tmergetest-2\n+\tEOF\n+\tgit tag -l --merged=mergetest-2 mergetest-* >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--no-merged show unmerged tags' '\n+\tcat >expect <<-\\EOF &&\n+\tmergetest-3\n+\tEOF\n+\tgit tag -l --no-merged=mergetest-2 mergetest-* >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.6\n"}]}