{"thread":{"id":"40001","subject":"[PATCH v8 0/11] Port tag.c over to use ref-filter APIs","startedAt":"2015-08-03T16:52:30Z","lastAt":"2015-08-03T22:13:46Z","messageCount":20,"participants":["Karthik Nayak","Junio C Hamano"],"isPatch":true,"patchVersion":8,"patchTotal":11},"messages":[{"id":"267318","messageId":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","threadId":"40001","inReplyTo":null,"subject":"[PATCH v8 0/11] Port tag.c over to use ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:52:30Z","receivedAt":"2015-08-03T16:52:30Z","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\nThis series consists of porting tag.c over to using the ref-filter APIs\n\nVersion 7 can be found here:\nhttp://thread.gmane.org/gmane.comp.version-control.git/274990\n\nChanges:\n* Make padright a general align atom.\n* Make print_value() and emit() output to a strbuf rather than stdout directly.\n\nInterdiff:\n\ndiff --git a/Documentation/git-for-each-ref.txt\nb/Documentation/git-for-each-ref.txt\nindex bcf319a..e89b9b0 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -127,11 +127,12 @@ color::\n     Change output color.  Followed by `:<colorname>`, where names\n     are described in `color.branch.*`.\n\n-padright::\n-    Pad succeeding atom or string to the right. Followed by\n-    `:<value>`, where `value` states the total length of atom or\n-    string including the padding. If the `value` is lesser than\n-    the atom or string length, then no padding is performed.\n+align::\n+    Align succeeding atoms to the right, left or middle. Followed\n+    by `:<type>,<paddinglength>`, where the `<type>` is either\n+    left, right or middle and `<paddinglength>` is the total\n+    length of the padding to be performed. If the atom length is\n+    more than the padding 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\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex fc01117..529b29f 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -41,7 +41,7 @@ static int list_tags(struct ref_filter *filter,\nstruct ref_sorting *sorting, con\n         filter->lines = 0;\n\n     if (filter->lines)\n-        format = \"%(padright:16)%(refname:short)\";\n+        format = \"%(align:left,16)%(refname:short)\";\n     else if (!format)\n         format = \"%(refname:short)\";\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 65d168e..afeab37 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -56,7 +56,7 @@ static struct {\n     { \"flag\" },\n     { \"HEAD\" },\n     { \"color\" },\n-    { \"padright\" },\n+    { \"align\" },\n };\n\n /*\n@@ -624,7 +624,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         const char *formatp;\n         struct branch *branch = NULL;\n\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->color = 1;\n-            v->modifier_atom = 1;\n             continue;\n         } else if (!strcmp(name, \"flag\")) {\n             char buf[256], *cp = buf;\n@@ -693,17 +691,23 @@ static void populate_value(struct ref_array_item *ref)\n             else\n                 v->s = \" \";\n             continue;\n-        } else if (starts_with(name, \"padright:\")) {\n+        } else if (starts_with(name, \"align:\")) {\n             const char *valp = NULL;\n+            struct align *align = xmalloc(sizeof(struct align));\n\n-            skip_prefix(name, \"padright:\", &valp);\n-            if (!valp[0])\n-                die(_(\"no value given with 'padright:'\"));\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->modifier_atom = 1;\n-            v->pad_to_right = 1;\n+            skip_prefix(name, \"align:\", &valp);\n+\n+            if (skip_prefix(valp, \"left,\", &valp))\n+                align->align_type = ALIGN_LEFT;\n+            else if (skip_prefix(valp, \"right,\", &valp))\n+                align->align_type = ALIGN_RIGHT;\n+            else if (skip_prefix(valp, \"middle,\", &valp))\n+                align->align_type = ALIGN_MIDDLE;\n+            else\n+                die(_(\"align: improper format\"));\n+            if (strtoul_ui(valp, 10, &align->align_value))\n+                die(_(\"align: positive value expected\"));\n+            v->align = align;\n             continue;\n         } else\n             continue;\n@@ -1243,55 +1247,26 @@ 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 apply_formatting_state(struct ref_formatting_state *state,\n-                   const char *buf, struct strbuf *value)\n-{\n-    if (state->color) {\n-        strbuf_addstr(value, state->color);\n-        state->color = NULL;\n-    }\n-    if (state->pad_to_right) {\n-        if (!is_utf8(buf))\n-            strbuf_addf(value, \"%-*s\", state->pad_to_right, buf);\n-        else {\n-            int utf8_compensation = strlen(buf) - utf8_strwidth(buf);\n-            strbuf_addf(value, \"%-*s\", state->pad_to_right +\nutf8_compensation, buf);\n-        }\n-        state->pad_to_right = 0;\n-        return;\n-    }\n-\n-    strbuf_addstr(value, buf);\n-}\n-\n-static void print_value(struct atom_value *v, struct\nref_formatting_state *state)\n+static void print_value(struct atom_value *v, struct\nref_formatting_state *state,\n+            struct strbuf *output)\n {\n-    struct strbuf value = STRBUF_INIT;\n-    struct strbuf formatted = STRBUF_INIT;\n-\n-    apply_formatting_state(state, v->s, &value);\n-\n     switch (state->quote_style) {\n     case QUOTE_NONE:\n-        fputs(value.buf, stdout);\n+        strbuf_addstr(output, v->s);\n         break;\n     case QUOTE_SHELL:\n-        sq_quote_buf(&formatted, value.buf);\n+        sq_quote_buf(output, v->s);\n         break;\n     case QUOTE_PERL:\n-        perl_quote_buf(&formatted, value.buf);\n+        perl_quote_buf(output, v->s);\n         break;\n     case QUOTE_PYTHON:\n-        python_quote_buf(&formatted, value.buf);\n+        python_quote_buf(output, v->s);\n         break;\n     case QUOTE_TCL:\n-        tcl_quote_buf(&formatted, value.buf);\n+        tcl_quote_buf(output, v->s);\n         break;\n     }\n-    if (state->quote_style != QUOTE_NONE)\n-        fputs(formatted.buf, stdout);\n-    strbuf_release(&value);\n-    strbuf_release(&formatted);\n }\n\n static int hex1(char ch)\n@@ -1312,12 +1287,8 @@ static int hex2(const char *cp)\n         return -1;\n }\n\n-static void emit(const char *cp, const char *ep,\n-         struct ref_formatting_state *state)\n+static void emit(const char *cp, const char *ep, struct strbuf *output)\n {\n-    struct strbuf value = STRBUF_INIT;\n-    struct strbuf format = STRBUF_INIT;\n-\n     while (*cp && (!ep || cp < ep)) {\n         if (*cp == '%') {\n             if (cp[1] == '%')\n@@ -1325,28 +1296,61 @@ static void emit(const char *cp, const char *ep,\n             else {\n                 int ch = hex2(cp + 1);\n                 if (0 <= ch) {\n-                    putchar(ch);\n+                    strbuf_addch(output, ch);\n                     cp += 3;\n                     continue;\n                 }\n             }\n         }\n-        strbuf_addch(&value, *cp);\n+        strbuf_addch(output, *cp);\n         cp++;\n     }\n-    apply_formatting_state(state, value.buf, &format);\n-    fputs(format.buf, stdout);\n-    strbuf_release(&format);\n-    strbuf_release(&value);\n }\n\n-static void store_formatting_state(struct ref_formatting_state *state,\n-                   struct atom_value *atomv)\n+static void process_formatting_state(struct atom_value *atomv, struct\nref_formatting_state *state)\n {\n-    if (atomv->color)\n-        state->color = atomv->s;\n-    if (atomv->pad_to_right)\n-        state->pad_to_right = atomv->ul;\n+    if (atomv->align) {\n+        state->align = atomv->align;\n+        atomv->align = NULL;\n+    }\n+}\n+\n+static void apply_formatting_state(struct ref_formatting_state\n*state, struct strbuf *value,\n+                   struct strbuf *format)\n+{\n+    if (state->align) {\n+        int len = 0, buf_len = value->len;\n+        struct align *align = state->align;\n+\n+        if (!value->buf)\n+            return;\n+        if (!is_utf8(value->buf)) {\n+            len = value->len - utf8_strwidth(value->buf);\n+            buf_len -= len;\n+        }\n+\n+        if (align->align_value < buf_len) {\n+            state->align = NULL;\n+            strbuf_addbuf(format, value);\n+            strbuf_release(value);\n+            return;\n+        }\n+\n+        if (align->align_type == ALIGN_LEFT)\n+            strbuf_addf(format, \"%-*s\", len + align->align_value, value->buf);\n+        else if (align->align_type == ALIGN_MIDDLE) {\n+            int right = (align->align_value - buf_len)/2;\n+            strbuf_addf(format, \"%*s%-*s\", align->align_value - right + len,\n+                    value->buf, right, \"\");\n+        } else if (align->align_type == ALIGN_RIGHT)\n+            strbuf_addf(format, \"%*s\", align->align_value, value->buf);\n+        strbuf_release(value);\n+        state->align = NULL;\n+        return;\n+    }\n+    strbuf_addbuf(format, value);\n+    strbuf_release(value);\n+\n }\n\n /*\n@@ -1396,26 +1400,34 @@ void show_ref_array_item(struct ref_array_item\n*info, const char *format,\n              int quote_style, unsigned int lines)\n {\n     const char *cp, *sp, *ep;\n+    struct strbuf value = STRBUF_INIT;\n+    struct strbuf final_buf = STRBUF_INIT;\n     struct ref_formatting_state state;\n+    int i;\n\n     memset(&state, 0, sizeof(state));\n     state.quote_style = quote_style;\n\n     for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n-        struct atom_value *atomv;\n+        struct atom_value *atomv = NULL;\n\n         ep = strchr(sp, ')');\n-        if (cp < sp)\n-            emit(cp, sp, &state);\n+        if (cp < sp) {\n+            emit(cp, sp, &value);\n+            apply_formatting_state(&state, &value, &final_buf);\n+        }\n         get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n-        if (atomv->modifier_atom)\n-            store_formatting_state(&state, atomv);\n-        else\n-            print_value(atomv, &state);\n+        if (atomv->align)\n+            process_formatting_state(atomv, &state);\n+        else {\n+            print_value(atomv, &state, &value);\n+            apply_formatting_state(&state, &value, &final_buf);\n+        }\n     }\n     if (*cp) {\n         sp = cp + strlen(cp);\n-        emit(cp, sp, &state);\n+        emit(cp, sp, &value);\n+        apply_formatting_state(&state, &value, &final_buf);\n     }\n     if (need_color_reset_at_eol) {\n         struct atom_value resetv;\n@@ -1424,8 +1436,14 @@ 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(&resetv, &state);\n+        print_value(&resetv, &state, &value);\n+        apply_formatting_state(&state, &value, &final_buf);\n     }\n+\n+    for (i = 0; i < final_buf.len; i++)\n+        printf(\"%c\", final_buf.buf[i]);\n+    strbuf_release(&final_buf);\n+\n     if (lines > 0) {\n         struct object_id oid;\n         hashcpy(oid.hash, info->objectname);\ndiff --git a/ref-filter.h b/ref-filter.h\nindex b50a036..b3b9cd8 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -17,18 +17,25 @@\n #define FILTER_REFS_ALL 0x2\n #define FILTER_REFS_TAGS 0x4\n\n-struct atom_value {\n-    const char *s;\n-    unsigned long ul; /* used for sorting when not FIELD_STR */\n-    unsigned int modifier_atom : 1, /*  atoms which act as modifiers\nfor the next atom */\n-        color : 1,\n-        pad_to_right : 1;\n-};\n+#define ALIGN_LEFT 0x01\n+#define ALIGN_RIGHT 0x02\n+#define ALIGN_MIDDLE 0x04\n\n struct ref_formatting_state {\n     int quote_style;\n-    unsigned int pad_to_right;\n-    const char *color;\n+    struct align *align;\n+    struct strbuf *sb;\n+};\n+\n+struct align {\n+    unsigned int align_type,\n+        align_value;\n+};\n+\n+struct atom_value {\n+    const char *s;\n+    struct align *align;\n+    unsigned long ul; /* used for sorting when not FIELD_STR */\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 842efde..7332bea 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -81,35 +81,83 @@ test_expect_success 'filtering with --contains' '\n     test_cmp expect actual\n '\n\n-test_expect_success 'padding to the right using `padright`' '\n+test_expect_success 'left alignment' '\n     cat >expect <<-\\EOF &&\n-    master    ||    master\n-    side      ||    side\n-    odd/spot  ||    odd/spot\n-    double-tag||    double-tag\n-    four      ||    four\n-    one       ||    one\n-    signed-tag||    signed-tag\n-    three     ||    three\n-    two       ||    two\n+    refs/heads/master   |refs/heads/master\n+    refs/heads/side     |refs/heads/side\n+    refs/odd/spot       |refs/odd/spot\n+    refs/tags/double-tag|refs/tags/double-tag\n+    refs/tags/four      |refs/tags/four\n+    refs/tags/one       |refs/tags/one\n+    refs/tags/signed-tag|refs/tags/signed-tag\n+    refs/tags/three     |refs/tags/three\n+    refs/tags/two       |refs/tags/two\n     EOF\n-    git for-each-ref\n--format=\"%(padright:10)%(refname:short)|%(padright:5)|%(refname:short)\"\n>actual &&\n+    git for-each-ref --format=\"%(align:left,20)%(refname)|%(refname)\"\n>actual &&\n     test_cmp expect actual\n '\n\n-test_expect_success 'no padding when `padright` length is smaller\nthan atom length' '\n+test_expect_success 'middle alignment' '\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\n+    |   refs/heads/side  |refs/heads/side\n+    |    refs/odd/spot   |refs/odd/spot\n+    |refs/tags/double-tag|refs/tags/double-tag\n+    |   refs/tags/four   |refs/tags/four\n+    |    refs/tags/one   |refs/tags/one\n+    |refs/tags/signed-tag|refs/tags/signed-tag\n+    |   refs/tags/three  |refs/tags/three\n+    |    refs/tags/two   |refs/tags/two\n     EOF\n-    git for-each-ref --format=\"%(padright:5)%(refname)|\" >actual &&\n+    git for-each-ref\n--format=\"|%(align:middle,20)%(refname)|%(refname)\" >actual &&\n+    test_cmp expect actual\n+'\n+\n+test_expect_success 'right alignment' '\n+    cat >expect <<-\\EOF &&\n+    |   refs/heads/master|refs/heads/master\n+    |     refs/heads/side|refs/heads/side\n+    |       refs/odd/spot|refs/odd/spot\n+    |refs/tags/double-tag|refs/tags/double-tag\n+    |      refs/tags/four|refs/tags/four\n+    |       refs/tags/one|refs/tags/one\n+    |refs/tags/signed-tag|refs/tags/signed-tag\n+    |     refs/tags/three|refs/tags/three\n+    |       refs/tags/two|refs/tags/two\n+    EOF\n+    git for-each-ref\n--format=\"|%(align:right,20)%(refname)|%(refname)\" >actual &&\n+    test_cmp expect actual\n+'\n+\n+test_expect_success 'alignment value lesser than atom value' '\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+    EOF\n+    git for-each-ref --format=\"|%(align:right,15)%(refname)|\" >actual &&\n+    test_cmp expect actual\n+'\n+\n+test_expect_success 'non atom alignment' '\n+    cat >expect <<-\\EOF &&\n+    |    |master  |  refs/heads/master|  refs/heads/master\n+    |    |side  |  refs/heads/side|  refs/heads/side\n+    |    |odd/spot  |  refs/odd/spot|  refs/odd/spot\n+    |    |double-tag  |  refs/tags/double-tag|  refs/tags/double-tag\n+    |    |four  |  refs/tags/four|  refs/tags/four\n+    |    |one  |  refs/tags/one|  refs/tags/one\n+    |    |signed-tag  |  refs/tags/signed-tag|  refs/tags/signed-tag\n+    |    |three  |  refs/tags/three|  refs/tags/three\n+    |    |two  |  refs/tags/two|  refs/tags/two\n+    EOF\n+    git for-each-ref\n--format=\"|%(align:right,5)|%(refname:short)%(align:middle,5)|%(refname)%(align:left,3)|%(refname)\"\n>actual &&\n     test_cmp expect actual\n '\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267319","messageId":"1438620861-25219-1-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 01/11] ref-filter: print output to strbuf for formatting","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:11Z","receivedAt":"2015-08-03T16:54:11Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce a strbuf `output` which will act as a substitute rather than\nprinting directly to stdout. This will be used for formatting\neventually.\n---\n ref-filter.c | 36 ++++++++++++++++++------------------\n 1 file changed, 18 insertions(+), 18 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7561727..febdc45 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1190,30 +1190,25 @@ 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 print_value(struct atom_value *v, int quote_style, struct strbuf *output)\n {\n-\tstruct strbuf sb = STRBUF_INIT;\n \tswitch (quote_style) {\n \tcase QUOTE_NONE:\n-\t\tfputs(v->s, stdout);\n+\t\tstrbuf_addstr(output, v->s);\n \t\tbreak;\n \tcase QUOTE_SHELL:\n-\t\tsq_quote_buf(&sb, v->s);\n+\t\tsq_quote_buf(output, v->s);\n \t\tbreak;\n \tcase QUOTE_PERL:\n-\t\tperl_quote_buf(&sb, v->s);\n+\t\tperl_quote_buf(output, v->s);\n \t\tbreak;\n \tcase QUOTE_PYTHON:\n-\t\tpython_quote_buf(&sb, v->s);\n+\t\tpython_quote_buf(output, v->s);\n \t\tbreak;\n \tcase QUOTE_TCL:\n-\t\ttcl_quote_buf(&sb, v->s);\n+\t\ttcl_quote_buf(output, v->s);\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 }\n \n static int hex1(char ch)\n@@ -1234,7 +1229,7 @@ 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, struct strbuf *output)\n {\n \twhile (*cp && (!ep || cp < ep)) {\n \t\tif (*cp == '%') {\n@@ -1243,13 +1238,13 @@ static void emit(const char *cp, const char *ep)\n \t\t\telse {\n \t\t\t\tint ch = hex2(cp + 1);\n \t\t\t\tif (0 <= ch) {\n-\t\t\t\t\tputchar(ch);\n+\t\t\t\t\tstrbuf_addch(output, ch);\n \t\t\t\t\tcp += 3;\n \t\t\t\t\tcontinue;\n \t\t\t\t}\n \t\t\t}\n \t\t}\n-\t\tputchar(*cp);\n+\t\tstrbuf_addch(output, *cp);\n \t\tcp++;\n \t}\n }\n@@ -1257,19 +1252,21 @@ static void emit(const char *cp, const char *ep)\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 strbuf output = STRBUF_INIT;\n+\tint i;\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, &output);\n \t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n-\t\tprint_value(atomv, quote_style);\n+\t\tprint_value(atomv, quote_style, &output);\n \t}\n \tif (*cp) {\n \t\tsp = cp + strlen(cp);\n-\t\temit(cp, sp);\n+\t\temit(cp, sp, &output);\n \t}\n \tif (need_color_reset_at_eol) {\n \t\tstruct atom_value resetv;\n@@ -1278,9 +1275,12 @@ 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, quote_style, &output);\n \t}\n+\tfor (i = 0; i < output.len; i++)\n+\t\tprintf(\"%c\", output.buf[i]);\n \tputchar('\\n');\n+\tstrbuf_release(&output);\n }\n \n /*  If no sorting option is given, use refname to sort as default */\n-- \n2.4.6\n"},{"id":"267320","messageId":"1438620861-25219-2-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 02/11] ref-filter: introduce ref_formatting_state","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:12Z","receivedAt":"2015-08-03T16:54:12Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Introduce a ref_formatting_state which will eventually hold the values\nof modifier atoms. Implement this within ref-filter.\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 | 49 +++++++++++++++++++++++++++++++++++++------------\n ref-filter.h |  4 ++++\n 2 files changed, 41 insertions(+), 12 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex febdc45..c4c7064 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1190,9 +1190,10 @@ 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, struct strbuf *output)\n+static void print_value(struct atom_value *v, struct ref_formatting_state *state,\n+\t\t\tstruct strbuf *output)\n {\n-\tswitch (quote_style) {\n+\tswitch (state->quote_style) {\n \tcase QUOTE_NONE:\n \t\tstrbuf_addstr(output, v->s);\n \t\tbreak;\n@@ -1249,24 +1250,47 @@ static void emit(const char *cp, const char *ep, struct strbuf *output)\n \t}\n }\n \n+static void process_formatting_state(struct atom_value *atomv, struct ref_formatting_state *state)\n+{\n+\t/* Based on the atomv values, the formatting state is set */\n+}\n+\n+static void apply_formatting_state(struct ref_formatting_state *state, struct strbuf *value,\n+\t\t\t\t   struct strbuf *format)\n+{\n+\t/* More formatting options to be evetually added */\n+\tstrbuf_addbuf(format, value);\n+\tstrbuf_release(value);\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 strbuf output = STRBUF_INIT;\n+\tstruct strbuf value = STRBUF_INIT;\n+\tstruct strbuf final_buf = STRBUF_INIT;\n+\tstruct ref_formatting_state state;\n \tint i;\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+\t\tstruct atom_value *atomv = NULL;\n \n \t\tep = strchr(sp, ')');\n-\t\tif (cp < sp)\n-\t\t\temit(cp, sp, &output);\n+\t\tif (cp < sp) {\n+\t\t\temit(cp, sp, &value);\n+\t\t\tapply_formatting_state(&state, &value, &final_buf);\n+\t\t}\n \t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n-\t\tprint_value(atomv, quote_style, &output);\n+\t\tprocess_formatting_state(atomv, &state);\n+\t\tprint_value(atomv, &state, &value);\n+\t\tapply_formatting_state(&state, &value, &final_buf);\n \t}\n \tif (*cp) {\n \t\tsp = cp + strlen(cp);\n-\t\temit(cp, sp, &output);\n+\t\temit(cp, sp, &value);\n+\t\tapply_formatting_state(&state, &value, &final_buf);\n \t}\n \tif (need_color_reset_at_eol) {\n \t\tstruct atom_value resetv;\n@@ -1275,12 +1299,13 @@ 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, &output);\n+\t\tprint_value(&resetv, &state, &value);\n+\t\tapply_formatting_state(&state, &value, &final_buf);\n \t}\n-\tfor (i = 0; i < output.len; i++)\n-\t\tprintf(\"%c\", output.buf[i]);\n+\tfor (i = 0; i < final_buf.len; i++)\n+\t\tprintf(\"%c\", final_buf.buf[i]);\n \tputchar('\\n');\n-\tstrbuf_release(&output);\n+\tstrbuf_release(&final_buf);\n }\n \n /*  If no sorting option is given, use refname to sort as default */\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 6bf27d8..b64677f 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -16,6 +16,10 @@\n #define FILTER_REFS_INCLUDE_BROKEN 0x1\n #define FILTER_REFS_ALL 0x2\n \n+struct ref_formatting_state {\n+\tint quote_style;\n+};\n+\n struct atom_value {\n \tconst char *s;\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n-- \n2.4.6\n"},{"id":"267322","messageId":"1438620861-25219-3-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 03/11] ref-filter: implement an `align` atom","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:13Z","receivedAt":"2015-08-03T16:54:13Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Implement an `align` atom which will act as a modifier atom and align\nsucceeding atoms to the right, left or middle. It is followed by\n`:<type>,<paddinglength>`, where the `<type>` is either left, right or\nmiddle and `<paddinglength>` is the total length of the padding to be\nperformed. If the atom length is more than the padding length then no\npadding is performed. e.g. to pad a succeeding atom to the middle with\na total padding size of 40 we can do a --format=\"%(align:middle,40)..\"\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 |  7 ++++\n ref-filter.c                       | 69 +++++++++++++++++++++++++++++---\n ref-filter.h                       | 12 ++++++\n t/t6302-for-each-ref-filter.sh     | 80 ++++++++++++++++++++++++++++++++++++++\n 4 files changed, 162 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex e49d578..bba6d83 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -127,6 +127,13 @@ color::\n \tChange output color.  Followed by `:<colorname>`, where names\n \tare described in `color.branch.*`.\n \n+align::\n+\tAlign succeeding atoms to the right, left or middle. Followed\n+\tby `:<type>,<paddinglength>`, where the `<type>` is either\n+\tleft, right or middle and `<paddinglength>` is the total\n+\tlength of the padding to be performed. If the atom length is\n+\tmore than the padding 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 c4c7064..46d8834 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -10,6 +10,7 @@\n #include \"quote.h\"\n #include \"ref-filter.h\"\n #include \"revision.h\"\n+#include \"utf8.h\"\n \n typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n \n@@ -53,6 +54,7 @@ static struct {\n \t{ \"flag\" },\n \t{ \"HEAD\" },\n \t{ \"color\" },\n+\t{ \"align\" },\n };\n \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@@ -687,6 +689,24 @@ 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, \"align:\")) {\n+\t\t\tconst char *valp = NULL;\n+\t\t\tstruct align *align = xmalloc(sizeof(struct align));\n+\n+\t\t\tskip_prefix(name, \"align:\", &valp);\n+\n+\t\t\tif (skip_prefix(valp, \"left,\", &valp))\n+\t\t\t\talign->align_type = ALIGN_LEFT;\n+\t\t\telse if (skip_prefix(valp, \"right,\", &valp))\n+\t\t\t\talign->align_type = ALIGN_RIGHT;\n+\t\t\telse if (skip_prefix(valp, \"middle,\", &valp))\n+\t\t\t\talign->align_type = ALIGN_MIDDLE;\n+\t\t\telse\n+\t\t\t\tdie(_(\"align: improper format\"));\n+\t\t\tif (strtoul_ui(valp, 10, &align->align_value))\n+\t\t\t\tdie(_(\"align: positive value expected\"));\n+\t\t\tv->align = align;\n+\t\t\tcontinue;\n \t\t} else\n \t\t\tcontinue;\n \n@@ -1252,15 +1272,48 @@ static void emit(const char *cp, const char *ep, struct strbuf *output)\n \n static void process_formatting_state(struct atom_value *atomv, struct ref_formatting_state *state)\n {\n-\t/* Based on the atomv values, the formatting state is set */\n+\tif (atomv->align) {\n+\t\tstate->align = atomv->align;\n+\t\tatomv->align = NULL;\n+\t}\n }\n \n static void apply_formatting_state(struct ref_formatting_state *state, struct strbuf *value,\n \t\t\t\t   struct strbuf *format)\n {\n-\t/* More formatting options to be evetually added */\n+\tif (state->align) {\n+\t\tint len = 0, buf_len = value->len;\n+\t\tstruct align *align = state->align;\n+\n+\t\tif (!value->buf)\n+\t\t\treturn;\n+\t\tif (!is_utf8(value->buf)) {\n+\t\t\tlen = value->len - utf8_strwidth(value->buf);\n+\t\t\tbuf_len -= len;\n+\t\t}\n+\n+\t\tif (align->align_value < buf_len) {\n+\t\t\tstate->align = NULL;\n+\t\t\tstrbuf_addbuf(format, value);\n+\t\t\tstrbuf_release(value);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tif (align->align_type == ALIGN_LEFT)\n+\t\t\tstrbuf_addf(format, \"%-*s\", len + align->align_value, value->buf);\n+\t\telse if (align->align_type == ALIGN_MIDDLE) {\n+\t\t\tint right = (align->align_value - buf_len)/2;\n+\t\t\tstrbuf_addf(format, \"%*s%-*s\", align->align_value - right + len,\n+\t\t\t\t    value->buf, right, \"\");\n+\t\t} else if (align->align_type == ALIGN_RIGHT)\n+\t\t\tstrbuf_addf(format, \"%*s\", align->align_value, value->buf);\n+\t\tstrbuf_release(value);\n+\t\tstate->align = NULL;\n+\t\treturn;\n+\t}\n \tstrbuf_addbuf(format, value);\n \tstrbuf_release(value);\n+\n }\n \n void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)\n@@ -1283,9 +1336,12 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n \t\t\tapply_formatting_state(&state, &value, &final_buf);\n \t\t}\n \t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n-\t\tprocess_formatting_state(atomv, &state);\n-\t\tprint_value(atomv, &state, &value);\n-\t\tapply_formatting_state(&state, &value, &final_buf);\n+\t\tif (atomv->align)\n+\t\t\tprocess_formatting_state(atomv, &state);\n+\t\telse {\n+\t\t\tprint_value(atomv, &state, &value);\n+\t\t\tapply_formatting_state(&state, &value, &final_buf);\n+\t\t}\n \t}\n \tif (*cp) {\n \t\tsp = cp + strlen(cp);\n@@ -1302,6 +1358,7 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n \t\tprint_value(&resetv, &state, &value);\n \t\tapply_formatting_state(&state, &value, &final_buf);\n \t}\n+\n \tfor (i = 0; i < final_buf.len; i++)\n \t\tprintf(\"%c\", final_buf.buf[i]);\n \tputchar('\\n');\ndiff --git a/ref-filter.h b/ref-filter.h\nindex b64677f..01f8cb3 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -16,12 +16,24 @@\n #define FILTER_REFS_INCLUDE_BROKEN 0x1\n #define FILTER_REFS_ALL 0x2\n \n+#define ALIGN_LEFT 0x01\n+#define ALIGN_RIGHT 0x02\n+#define ALIGN_MIDDLE 0x04\n+\n struct ref_formatting_state {\n \tint quote_style;\n+\tstruct align *align;\n+\tstruct strbuf *sb;\n+};\n+\n+struct align {\n+\tunsigned int align_type,\n+\t\talign_value;\n };\n \n struct atom_value {\n \tconst char *s;\n+\tstruct align *align;\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n };\n \ndiff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh\nindex 505a360..87225dd 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -81,4 +81,84 @@ test_expect_success 'filtering with --contains' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'left alignment' '\n+\tcat >expect <<-\\EOF &&\n+\trefs/heads/master   |refs/heads/master\n+\trefs/heads/side     |refs/heads/side\n+\trefs/odd/spot       |refs/odd/spot\n+\trefs/tags/double-tag|refs/tags/double-tag\n+\trefs/tags/four      |refs/tags/four\n+\trefs/tags/one       |refs/tags/one\n+\trefs/tags/signed-tag|refs/tags/signed-tag\n+\trefs/tags/three     |refs/tags/three\n+\trefs/tags/two       |refs/tags/two\n+\tEOF\n+\tgit for-each-ref --format=\"%(align:left,20)%(refname)|%(refname)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'middle alignment' '\n+\tcat >expect <<-\\EOF &&\n+\t|  refs/heads/master |refs/heads/master\n+\t|   refs/heads/side  |refs/heads/side\n+\t|    refs/odd/spot   |refs/odd/spot\n+\t|refs/tags/double-tag|refs/tags/double-tag\n+\t|   refs/tags/four   |refs/tags/four\n+\t|    refs/tags/one   |refs/tags/one\n+\t|refs/tags/signed-tag|refs/tags/signed-tag\n+\t|   refs/tags/three  |refs/tags/three\n+\t|    refs/tags/two   |refs/tags/two\n+\tEOF\n+\tgit for-each-ref --format=\"|%(align:middle,20)%(refname)|%(refname)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'right alignment' '\n+\tcat >expect <<-\\EOF &&\n+\t|   refs/heads/master|refs/heads/master\n+\t|     refs/heads/side|refs/heads/side\n+\t|       refs/odd/spot|refs/odd/spot\n+\t|refs/tags/double-tag|refs/tags/double-tag\n+\t|      refs/tags/four|refs/tags/four\n+\t|       refs/tags/one|refs/tags/one\n+\t|refs/tags/signed-tag|refs/tags/signed-tag\n+\t|     refs/tags/three|refs/tags/three\n+\t|       refs/tags/two|refs/tags/two\n+\tEOF\n+\tgit for-each-ref --format=\"|%(align:right,20)%(refname)|%(refname)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'alignment value lesser than atom value' '\n+\tcat >expect <<-\\EOF &&\n+\t|refs/heads/master|\n+\t|refs/heads/side|\n+\t|  refs/odd/spot|\n+\t|refs/tags/double-tag|\n+\t| refs/tags/four|\n+\t|  refs/tags/one|\n+\t|refs/tags/signed-tag|\n+\t|refs/tags/three|\n+\t|  refs/tags/two|\n+\tEOF\n+\tgit for-each-ref --format=\"|%(align:right,15)%(refname)|\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'non atom alignment' '\n+\tcat >expect <<-\\EOF &&\n+\t|    |master  |  refs/heads/master|  refs/heads/master\n+\t|    |side  |  refs/heads/side|  refs/heads/side\n+\t|    |odd/spot  |  refs/odd/spot|  refs/odd/spot\n+\t|    |double-tag  |  refs/tags/double-tag|  refs/tags/double-tag\n+\t|    |four  |  refs/tags/four|  refs/tags/four\n+\t|    |one  |  refs/tags/one|  refs/tags/one\n+\t|    |signed-tag  |  refs/tags/signed-tag|  refs/tags/signed-tag\n+\t|    |three  |  refs/tags/three|  refs/tags/three\n+\t|    |two  |  refs/tags/two|  refs/tags/two\n+\tEOF\n+\tgit for-each-ref --format=\"|%(align:right,5)|%(refname:short)%(align:middle,5)|%(refname)%(align:left,3)|%(refname)\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.4.6\n"},{"id":"267321","messageId":"1438620861-25219-4-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 04/11] ref-filter: add option to filter only tags","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:14Z","receivedAt":"2015-08-03T16:54:14Z","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 46d8834..01c9097 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1155,6 +1155,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 01f8cb3..8d4e348 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 #define ALIGN_LEFT 0x01\n #define ALIGN_RIGHT 0x02\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":"267324","messageId":"1438620861-25219-5-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 05/11] ref-filter: support printing N lines from tag annotation","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:15Z","receivedAt":"2015-08-03T16:54:15Z","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           | 54 ++++++++++++++++++++++++++++++++++++++++++++++++--\n ref-filter.h           |  9 +++++++--\n 4 files changed, 64 insertions(+), 5 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 01c9097..9f3806a 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1318,7 +1318,51 @@ static void apply_formatting_state(struct ref_formatting_state *state, struct st\n \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 strbuf value = STRBUF_INIT;\n@@ -1363,8 +1407,14 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n \n \tfor (i = 0; i < final_buf.len; i++)\n \t\tprintf(\"%c\", final_buf.buf[i]);\n-\tputchar('\\n');\n \tstrbuf_release(&final_buf);\n+\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 \n /*  If no sorting option is given, use refname to sort as default */\ndiff --git a/ref-filter.h b/ref-filter.h\nindex 8d4e348..16cffab 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -71,6 +71,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@@ -102,8 +103,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":"267323","messageId":"1438620861-25219-6-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 06/11] ref-filter: add support to sort by version","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:16Z","receivedAt":"2015-08-03T16:54:16Z","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                       | 15 ++++++++++-----\n ref-filter.h                       |  3 ++-\n t/t6302-for-each-ref-filter.sh     | 36 ++++++++++++++++++++++++++++++++++++\n 4 files changed, 51 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex bba6d83..e89b9b0 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -152,6 +152,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 9f3806a..1bc6d4b 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -11,6 +11,8 @@\n #include \"ref-filter.h\"\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@@ -1175,19 +1177,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@@ -1446,6 +1448,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 16cffab..c41432f 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -41,7 +41,8 @@ struct atom_value {\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 87225dd..7332bea 100755\n--- a/t/t6302-for-each-ref-filter.sh\n+++ b/t/t6302-for-each-ref-filter.sh\n@@ -161,4 +161,40 @@ test_expect_success 'non atom alignment' '\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":"267325","messageId":"1438620861-25219-7-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 07/11] ref-filter: add option to match literal pattern","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:17Z","receivedAt":"2015-08-03T16:54:17Z","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 1bc6d4b..afeab37 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -950,9 +950,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@@ -973,6 +996,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@@ -1041,7 +1074,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 c41432f..b3b9cd8 100644\n--- a/ref-filter.h\n+++ b/ref-filter.h\n@@ -71,7 +71,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":"267328","messageId":"1438620861-25219-8-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 08/11] tag.c: use 'ref-filter' data structures","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:18Z","receivedAt":"2015-08-03T16:54:18Z","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":"267326","messageId":"1438620861-25219-9-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 09/11] tag.c: use 'ref-filter' APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:19Z","receivedAt":"2015-08-03T16:54:19Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\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..829af6f 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 = \"%(align:left,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":"267327","messageId":"1438620861-25219-10-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 10/11] tag.c: implement '--format' option","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:20Z","receivedAt":"2015-08-03T16:54:20Z","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 829af6f..13c9579 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 = \"%(align:left,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":"267329","messageId":"1438620861-25219-11-git-send-email-Karthik.188@gmail.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"[PATCH v8 11/11] tag.c: implement '--merged' and '--no-merged' options","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T16:54:21Z","receivedAt":"2015-08-03T16:54:21Z","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 13c9579..529b29f 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":"267339","messageId":"xmqq8u9s86n3.fsf@gitster.dls.corp.google.com","threadId":"40001","inReplyTo":"CAOLa=ZTYWTjc-OC7N7FGWETP1svpCkqhQ2wwPmbf5nVRyPRAqg@mail.gmail.com","subject":"Re: [PATCH v8 0/11] Port tag.c over to use ref-filter APIs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-08-03T20:21:52Z","receivedAt":"2015-08-03T20:21:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> This is part of my GSoC project to unify git tag -l, git branch -l,\n> git for-each-ref.  This patch series is continued from: Git (next)\n> https://github.com/git/git/commit/bf5418f49ff0cebc6e5ce04ad1417e1a47c81b61\n>\n> This series consists of porting tag.c over to using the ref-filter APIs\n>\n> Version 7 can be found here:\n> http://thread.gmane.org/gmane.comp.version-control.git/274990\n>\n> Changes:\n> * Make padright a general align atom.\n> * Make print_value() and emit() output to a strbuf rather than stdout directly.\n>\n> Interdiff:\n>\n> diff --git a/Documentation/git-for-each-ref.txt\n> b/Documentation/git-for-each-ref.txt\n> index bcf319a..e89b9b0 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -127,11 +127,12 @@ color::\n>      Change output color.  Followed by `:<colorname>`, where names\n>      are described in `color.branch.*`.\n>\n> -padright::\n> -    Pad succeeding atom or string to the right. Followed by\n> -    `:<value>`, where `value` states the total length of atom or\n> -    string including the padding. If the `value` is lesser than\n> -    the atom or string length, then no padding is performed.\n> +align::\n> +    Align succeeding atoms to the right, left or middle. Followed\n> +    by `:<type>,<paddinglength>`, where the `<type>` is either\n> +    left, right or middle and `<paddinglength>` is the total\n> +    length of the padding to be performed. If the atom length is\n> +    more than the padding length then no padding is performed.\n\nIt is very very dissapointing to allow the \"next atom only\"\nimplementation to squat on a good name \"align:<type>,<width>\",\nespecially when I thought that the list agreed\n\n  %(align:<type>,<width>) any string with or without %(atom) %(end)\n\nwould be the way to go.\n"},{"id":"267340","messageId":"xmqq4mkg85ym.fsf@gitster.dls.corp.google.com","threadId":"40001","inReplyTo":"1438620861-25219-1-git-send-email-Karthik.188@gmail.com","subject":"Re: [PATCH v8 01/11] ref-filter: print output to strbuf for formatting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-08-03T20:36:33Z","receivedAt":"2015-08-03T20:36:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> Introduce a strbuf `output` which will act as a substitute rather than\n> printing directly to stdout. This will be used for formatting\n> eventually.\n> ---\n\nMissing sign-off; the patch looks like a good first step in a nice\ndirection.\n\n>  ref-filter.c | 36 ++++++++++++++++++------------------\n>  1 file changed, 18 insertions(+), 18 deletions(-)\n>\n> diff --git a/ref-filter.c b/ref-filter.c\n> index 7561727..febdc45 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -1190,30 +1190,25 @@ 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 print_value(struct atom_value *v, int quote_style, struct strbuf *output)\n>  {\n> -\tstruct strbuf sb = STRBUF_INIT;\n>  \tswitch (quote_style) {\n>  \tcase QUOTE_NONE:\n> -\t\tfputs(v->s, stdout);\n> +\t\tstrbuf_addstr(output, v->s);\n>  \t\tbreak;\n>  \tcase QUOTE_SHELL:\n> -\t\tsq_quote_buf(&sb, v->s);\n> +\t\tsq_quote_buf(output, v->s);\n>  \t\tbreak;\n>  \tcase QUOTE_PERL:\n> -\t\tperl_quote_buf(&sb, v->s);\n> +\t\tperl_quote_buf(output, v->s);\n>  \t\tbreak;\n>  \tcase QUOTE_PYTHON:\n> -\t\tpython_quote_buf(&sb, v->s);\n> +\t\tpython_quote_buf(output, v->s);\n>  \t\tbreak;\n>  \tcase QUOTE_TCL:\n> -\t\ttcl_quote_buf(&sb, v->s);\n> +\t\ttcl_quote_buf(output, v->s);\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>  }\n>  \n>  static int hex1(char ch)\n> @@ -1234,7 +1229,7 @@ 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, struct strbuf *output)\n>  {\n>  \twhile (*cp && (!ep || cp < ep)) {\n>  \t\tif (*cp == '%') {\n> @@ -1243,13 +1238,13 @@ static void emit(const char *cp, const char *ep)\n>  \t\t\telse {\n>  \t\t\t\tint ch = hex2(cp + 1);\n>  \t\t\t\tif (0 <= ch) {\n> -\t\t\t\t\tputchar(ch);\n> +\t\t\t\t\tstrbuf_addch(output, ch);\n>  \t\t\t\t\tcp += 3;\n>  \t\t\t\t\tcontinue;\n>  \t\t\t\t}\n>  \t\t\t}\n>  \t\t}\n> -\t\tputchar(*cp);\n> +\t\tstrbuf_addch(output, *cp);\n>  \t\tcp++;\n>  \t}\n>  }\n> @@ -1257,19 +1252,21 @@ static void emit(const char *cp, const char *ep)\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 strbuf output = STRBUF_INIT;\n> +\tint i;\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, &output);\n>  \t\tget_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n> -\t\tprint_value(atomv, quote_style);\n> +\t\tprint_value(atomv, quote_style, &output);\n>  \t}\n>  \tif (*cp) {\n>  \t\tsp = cp + strlen(cp);\n> -\t\temit(cp, sp);\n> +\t\temit(cp, sp, &output);\n>  \t}\n>  \tif (need_color_reset_at_eol) {\n>  \t\tstruct atom_value resetv;\n> @@ -1278,9 +1275,12 @@ 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, quote_style, &output);\n>  \t}\n> +\tfor (i = 0; i < output.len; i++)\n> +\t\tprintf(\"%c\", output.buf[i]);\n>  \tputchar('\\n');\n> +\tstrbuf_release(&output);\n>  }\n>  \n>  /*  If no sorting option is given, use refname to sort as default */\n"},{"id":"267341","messageId":"xmqqzj286r46.fsf@gitster.dls.corp.google.com","threadId":"40001","inReplyTo":"1438620861-25219-2-git-send-email-Karthik.188@gmail.com","subject":"Re: [PATCH v8 02/11] ref-filter: introduce ref_formatting_state","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-08-03T20:42:33Z","receivedAt":"2015-08-03T20:42:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> Introduce a ref_formatting_state which will eventually hold the values\n> of modifier atoms. Implement this within ref-filter.\n>\n> Mentored-by: Christian Couder <christian.couder@gmail.com>\n> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  ref-filter.c | 49 +++++++++++++++++++++++++++++++++++++------------\n>  ref-filter.h |  4 ++++\n>  2 files changed, 41 insertions(+), 12 deletions(-)\n>\n> diff --git a/ref-filter.c b/ref-filter.c\n> index febdc45..c4c7064 100644\n> --- a/ref-filter.c\n> +++ b/ref-filter.c\n> @@ -1190,9 +1190,10 @@ 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, struct strbuf *output)\n> +static void print_value(struct atom_value *v, struct ref_formatting_state *state,\n> +\t\t\tstruct strbuf *output)\n>  {\n\nI expect that the state would eventually become a stack of states\n(i.e. the topmost one passed around, each having a pointer to the\nprevious level) in order to implement that \"divert\" mechanism for\n(possibly nested) if ... end construct.\n\nWith that in mind, I suspect that state->output should be \"where the\ncurrent level would output to\", i.e. no need to pass state and\noutput around separately.\n\n> +static void apply_formatting_state(struct ref_formatting_state *state, struct strbuf *value,\n> +\t\t\t\t   struct strbuf *format)\n> +{\n\nThe name \"format\" feels quite misleading; the readers would expect\nthat you would use it in \"strbuf_addf(format, value)\", but that is\nnot what is going on here.\n\n> @@ -1275,12 +1299,13 @@ 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, &output);\n> +\t\tprint_value(&resetv, &state, &value);\n> +\t\tapply_formatting_state(&state, &value, &final_buf);\n>  \t}\n> -\tfor (i = 0; i < output.len; i++)\n> -\t\tprintf(\"%c\", output.buf[i]);\n> +\tfor (i = 0; i < final_buf.len; i++)\n> +\t\tprintf(\"%c\", final_buf.buf[i]);\n>  \tputchar('\\n');\n> -\tstrbuf_release(&output);\n> +\tstrbuf_release(&final_buf);\n>  }\n>  \n>  /*  If no sorting option is given, use refname to sort as default */\n> diff --git a/ref-filter.h b/ref-filter.h\n> index 6bf27d8..b64677f 100644\n> --- a/ref-filter.h\n> +++ b/ref-filter.h\n> @@ -16,6 +16,10 @@\n>  #define FILTER_REFS_INCLUDE_BROKEN 0x1\n>  #define FILTER_REFS_ALL 0x2\n>  \n> +struct ref_formatting_state {\n> +\tint quote_style;\n> +};\n> +\n>  struct atom_value {\n>  \tconst char *s;\n>  \tunsigned long ul; /* used for sorting when not FIELD_STR */\n"},{"id":"267353","messageId":"CAOLa=ZSRLaRjyif_7auUWfX6DmtA_g2MonumDf-3cMZvvrRXGg@mail.gmail.com","threadId":"40001","inReplyTo":"xmqq8u9s86n3.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v8 0/11] Port tag.c over to use ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T21:22:31Z","receivedAt":"2015-08-03T21:22:31Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Tue, Aug 4, 2015 at 1:51 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> This is part of my GSoC project to unify git tag -l, git branch -l,\n>> git for-each-ref.  This patch series is continued from: Git (next)\n>> https://github.com/git/git/commit/bf5418f49ff0cebc6e5ce04ad1417e1a47c81b61\n>>\n>> This series consists of porting tag.c over to using the ref-filter APIs\n>>\n>> Version 7 can be found here:\n>> http://thread.gmane.org/gmane.comp.version-control.git/274990\n>>\n>> Changes:\n>> * Make padright a general align atom.\n>> * Make print_value() and emit() output to a strbuf rather than stdout directly.\n>>\n>> Interdiff:\n>>\n>> diff --git a/Documentation/git-for-each-ref.txt\n>> b/Documentation/git-for-each-ref.txt\n>> index bcf319a..e89b9b0 100644\n>> --- a/Documentation/git-for-each-ref.txt\n>> +++ b/Documentation/git-for-each-ref.txt\n>> @@ -127,11 +127,12 @@ color::\n>>      Change output color.  Followed by `:<colorname>`, where names\n>>      are described in `color.branch.*`.\n>>\n>> -padright::\n>> -    Pad succeeding atom or string to the right. Followed by\n>> -    `:<value>`, where `value` states the total length of atom or\n>> -    string including the padding. If the `value` is lesser than\n>> -    the atom or string length, then no padding is performed.\n>> +align::\n>> +    Align succeeding atoms to the right, left or middle. Followed\n>> +    by `:<type>,<paddinglength>`, where the `<type>` is either\n>> +    left, right or middle and `<paddinglength>` is the total\n>> +    length of the padding to be performed. If the atom length is\n>> +    more than the padding length then no padding is performed.\n>\n> It is very very dissapointing to allow the \"next atom only\"\n> implementation to squat on a good name \"align:<type>,<width>\",\n> especially when I thought that the list agreed\n>\n>   %(align:<type>,<width>) any string with or without %(atom) %(end)\n>\n> would be the way to go.\n\n>From what I read, I thought we wanted the next atom or string to be\naligned, if we need to align everything within the %(end) atom. I could\ndo that :)\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267352","messageId":"CAOLa=ZRx+eY_byR=wSinZV9Ay2-iDfs49d9kDOAcM7iFZvm1-g@mail.gmail.com","threadId":"40001","inReplyTo":"xmqq4mkg85ym.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v8 01/11] ref-filter: print output to strbuf for formatting","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T21:23:08Z","receivedAt":"2015-08-03T21:23:08Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Tue, Aug 4, 2015 at 2:06 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> Introduce a strbuf `output` which will act as a substitute rather than\n>> printing directly to stdout. This will be used for formatting\n>> eventually.\n>> ---\n>\n> Missing sign-off; the patch looks like a good first step in a nice\n> direction.\n>\n\nWill add, Thanks :)\n\n>>  ref-filter.c | 36 ++++++++++++++++++------------------\n>>  1 file changed, 18 insertions(+), 18 deletions(-)\n>>\n>> diff --git a/ref-filter.c b/ref-filter.c\n>> index 7561727..febdc45 100644\n>> --- a/ref-filter.c\n>> +++ b/ref-filter.c\n>> @@ -1190,30 +1190,25 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n>>       qsort(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 print_value(struct atom_value *v, int quote_style, struct strbuf *output)\n>>  {\n>> -     struct strbuf sb = STRBUF_INIT;\n>>       switch (quote_style) {\n>>       case QUOTE_NONE:\n>> -             fputs(v->s, stdout);\n>> +             strbuf_addstr(output, v->s);\n>>               break;\n>>       case QUOTE_SHELL:\n>> -             sq_quote_buf(&sb, v->s);\n>> +             sq_quote_buf(output, v->s);\n>>               break;\n>>       case QUOTE_PERL:\n>> -             perl_quote_buf(&sb, v->s);\n>> +             perl_quote_buf(output, v->s);\n>>               break;\n>>       case QUOTE_PYTHON:\n>> -             python_quote_buf(&sb, v->s);\n>> +             python_quote_buf(output, v->s);\n>>               break;\n>>       case QUOTE_TCL:\n>> -             tcl_quote_buf(&sb, v->s);\n>> +             tcl_quote_buf(output, v->s);\n>>               break;\n>>       }\n>> -     if (quote_style != QUOTE_NONE) {\n>> -             fputs(sb.buf, stdout);\n>> -             strbuf_release(&sb);\n>> -     }\n>>  }\n>>\n>>  static int hex1(char ch)\n>> @@ -1234,7 +1229,7 @@ static int hex2(const char *cp)\n>>               return -1;\n>>  }\n>>\n>> -static void emit(const char *cp, const char *ep)\n>> +static void emit(const char *cp, const char *ep, struct strbuf *output)\n>>  {\n>>       while (*cp && (!ep || cp < ep)) {\n>>               if (*cp == '%') {\n>> @@ -1243,13 +1238,13 @@ static void emit(const char *cp, const char *ep)\n>>                       else {\n>>                               int ch = hex2(cp + 1);\n>>                               if (0 <= ch) {\n>> -                                     putchar(ch);\n>> +                                     strbuf_addch(output, ch);\n>>                                       cp += 3;\n>>                                       continue;\n>>                               }\n>>                       }\n>>               }\n>> -             putchar(*cp);\n>> +             strbuf_addch(output, *cp);\n>>               cp++;\n>>       }\n>>  }\n>> @@ -1257,19 +1252,21 @@ static void emit(const char *cp, const char *ep)\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 strbuf output = STRBUF_INIT;\n>> +     int i;\n>>\n>>       for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n>>               struct atom_value *atomv;\n>>\n>>               ep = strchr(sp, ')');\n>>               if (cp < sp)\n>> -                     emit(cp, sp);\n>> +                     emit(cp, sp, &output);\n>>               get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);\n>> -             print_value(atomv, quote_style);\n>> +             print_value(atomv, quote_style, &output);\n>>       }\n>>       if (*cp) {\n>>               sp = cp + strlen(cp);\n>> -             emit(cp, sp);\n>> +             emit(cp, sp, &output);\n>>       }\n>>       if (need_color_reset_at_eol) {\n>>               struct atom_value resetv;\n>> @@ -1278,9 +1275,12 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n>>               if (color_parse(\"reset\", color) < 0)\n>>                       die(\"BUG: couldn't parse 'reset' as a color\");\n>>               resetv.s = color;\n>> -             print_value(&resetv, quote_style);\n>> +             print_value(&resetv, quote_style, &output);\n>>       }\n>> +     for (i = 0; i < output.len; i++)\n>> +             printf(\"%c\", output.buf[i]);\n>>       putchar('\\n');\n>> +     strbuf_release(&output);\n>>  }\n>>\n>>  /*  If no sorting option is given, use refname to sort as default */\n\n\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267359","messageId":"CAOLa=ZStMApJoOfu5Hs_rE+-dKN+FSjcAqKkbAUomt0pnqEdTQ@mail.gmail.com","threadId":"40001","inReplyTo":"xmqqzj286r46.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v8 02/11] ref-filter: introduce ref_formatting_state","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T22:03:26Z","receivedAt":"2015-08-03T22:03:26Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Tue, Aug 4, 2015 at 2:12 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> Introduce a ref_formatting_state which will eventually hold the values\n>> of modifier atoms. Implement this within ref-filter.\n>>\n>> Mentored-by: Christian Couder <christian.couder@gmail.com>\n>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>\n>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>> ---\n>>  ref-filter.c | 49 +++++++++++++++++++++++++++++++++++++------------\n>>  ref-filter.h |  4 ++++\n>>  2 files changed, 41 insertions(+), 12 deletions(-)\n>>\n>> diff --git a/ref-filter.c b/ref-filter.c\n>> index febdc45..c4c7064 100644\n>> --- a/ref-filter.c\n>> +++ b/ref-filter.c\n>> @@ -1190,9 +1190,10 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)\n>>       qsort(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, struct strbuf *output)\n>> +static void print_value(struct atom_value *v, struct ref_formatting_state *state,\n>> +                     struct strbuf *output)\n>>  {\n>\n> I expect that the state would eventually become a stack of states\n> (i.e. the topmost one passed around, each having a pointer to the\n> previous level) in order to implement that \"divert\" mechanism for\n> (possibly nested) if ... end construct.\n>\n\nMakes sense :)\n\n> With that in mind, I suspect that state->output should be \"where the\n> current level would output to\", i.e. no need to pass state and\n> output around separately.\n>\n\nWill do!\n\n>> +static void apply_formatting_state(struct ref_formatting_state *state, struct strbuf *value,\n>> +                                struct strbuf *format)\n>> +{\n>\n> The name \"format\" feels quite misleading; the readers would expect\n> that you would use it in \"strbuf_addf(format, value)\", but that is\n> not what is going on here.\n>\n\nwill change it to final i guess.\n\n>> @@ -1275,12 +1299,13 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu\n>>               if (color_parse(\"reset\", color) < 0)\n>>                       die(\"BUG: couldn't parse 'reset' as a color\");\n>>               resetv.s = color;\n>> -             print_value(&resetv, quote_style, &output);\n>> +             print_value(&resetv, &state, &value);\n>> +             apply_formatting_state(&state, &value, &final_buf);\n>>       }\n>> -     for (i = 0; i < output.len; i++)\n>> -             printf(\"%c\", output.buf[i]);\n>> +     for (i = 0; i < final_buf.len; i++)\n>> +             printf(\"%c\", final_buf.buf[i]);\n>>       putchar('\\n');\n>> -     strbuf_release(&output);\n>> +     strbuf_release(&final_buf);\n>>  }\n>>\n>>  /*  If no sorting option is given, use refname to sort as default */\n>> diff --git a/ref-filter.h b/ref-filter.h\n>> index 6bf27d8..b64677f 100644\n>> --- a/ref-filter.h\n>> +++ b/ref-filter.h\n>> @@ -16,6 +16,10 @@\n>>  #define FILTER_REFS_INCLUDE_BROKEN 0x1\n>>  #define FILTER_REFS_ALL 0x2\n>>\n>> +struct ref_formatting_state {\n>> +     int quote_style;\n>> +};\n>> +\n>>  struct atom_value {\n>>       const char *s;\n>>       unsigned long ul; /* used for sorting when not FIELD_STR */\n\n\n\n-- \nRegards,\nKarthik Nayak\n"},{"id":"267361","messageId":"xmqq7fpc6n5o.fsf@gitster.dls.corp.google.com","threadId":"40001","inReplyTo":"CAOLa=ZSRLaRjyif_7auUWfX6DmtA_g2MonumDf-3cMZvvrRXGg@mail.gmail.com","subject":"Re: [PATCH v8 0/11] Port tag.c over to use ref-filter APIs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-08-03T22:08:03Z","receivedAt":"2015-08-03T22:08:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> On Tue, Aug 4, 2015 at 1:51 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> ...\n>> It is very very dissapointing to allow the \"next atom only\"\n>> implementation to squat on a good name \"align:<type>,<width>\",\n>> especially when I thought that the list agreed\n>>\n>>   %(align:<type>,<width>) any string with or without %(atom) %(end)\n>>\n>> would be the way to go.\n>\n> From what I read, I thought we wanted the next atom or string to be\n> aligned, if we need to align everything within the %(end) atom.\n\nIs that a serious comment?\n\nDid I read too much into your $gmane/275119, expecting that you\nunderstood everything you are saying \"That's a good way to go\" to?\n\n> I could do that :)\n\nSure ;-)\n"},{"id":"267363","messageId":"CAOLa=ZTf-rQuc_Hv=8NnYx_j8YGJuYNmOa+nz=MeoxAK6JFU3Q@mail.gmail.com","threadId":"40001","inReplyTo":"xmqq7fpc6n5o.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v8 0/11] Port tag.c over to use ref-filter APIs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2015-08-03T22:13:46Z","receivedAt":"2015-08-03T22:13:46Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"On Tue, Aug 4, 2015 at 3:38 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> On Tue, Aug 4, 2015 at 1:51 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> ...\n>>> It is very very dissapointing to allow the \"next atom only\"\n>>> implementation to squat on a good name \"align:<type>,<width>\",\n>>> especially when I thought that the list agreed\n>>>\n>>>   %(align:<type>,<width>) any string with or without %(atom) %(end)\n>>>\n>>> would be the way to go.\n>>\n>> From what I read, I thought we wanted the next atom or string to be\n>> aligned, if we need to align everything within the %(end) atom.\n>\n> Is that a serious comment?\n>\n> Did I read too much into your $gmane/275119, expecting that you\n> understood everything you are saying \"That's a good way to go\" to?\n\nSorry, I kinda was thinking only WRT to the %(if) and %(end) part of it.\nEven though you clearly mentioned about %(align) also.\n\n>\n>> I could do that :)\n>\n> Sure ;-)\n\nI have it ready, will wait to see if there are more comments and send with\nnext iteration of the series.\n\n-- \nRegards,\nKarthik Nayak\n"}]}