Version 15 can be found here:
http://thread.gmane.org/gmane.comp.version-control.git/277012

Changes in this version:
* Grammatical changes.
* rename cb_data to at_end_data.
* rename align_handler() to end_align_handler() and move it
next to align_atom_handler().
* introduce match_atom_name() for checking atom names.

Karthik Nayak (14):
  ref-filter: move `struct atom_value` to ref-filter.c
  ref-filter: introduce ref_formatting_state and ref_formatting_stack
  utf8: add function to align a string into given strbuf
  ref-filter: introduce handler function for each atom
  ref-filter: introduce match_atom_name()
  ref-filter: implement an `align` atom
  ref-filter: add option to filter out tags, branches and remotes
  ref-filter: add support for %(contents:lines=X)
  ref-filter: add support to sort by version
  ref-filter: add option to match literal pattern
  tag.c: use 'ref-filter' data structures
  tag.c: use 'ref-filter' APIs
  tag.c: implement '--format' option
  tag.c: implement '--merged' and '--no-merged' options

 Documentation/git-for-each-ref.txt |  16 +-
 Documentation/git-tag.txt          |  27 ++-
 builtin/for-each-ref.c             |   1 +
 builtin/tag.c                      | 368 ++++++--------------------------
 ref-filter.c                       | 415 ++++++++++++++++++++++++++++++++-----
 ref-filter.h                       |  25 ++-
 refs.c                             |   9 +
 refs.h                             |   1 +
 t/t6302-for-each-ref-filter.sh     | 174 ++++++++++++++++
 t/t7004-tag.sh                     |  47 ++++-
 utf8.c                             |  21 ++
 utf8.h                             |  15 ++
 12 files changed, 743 insertions(+), 376 deletions(-)

Interdiff:

diff --git a/Documentation/git-for-each-ref.txt 
b/Documentation/git-for-each-ref.txt
index d039f40..c5154bb 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -128,13 +128,14 @@ color::
        are described in `color.branch.*`.
 
 align::
-       Left-, middle-, or right-align the content between %(align:..)
+       Left-, middle-, or right-align the content between %(align:...)
        and %(end). Followed by `:<width>,<position>`, where the
        `<position>` is either left, right or middle and `<width>` is
        the total length of the content with alignment. If the
        contents length is more than the width then no alignment is
        performed. If used with '--quote' everything in between
-       %(align:..)  and %(end) is quoted.
+       %(align:...) and %(end) is quoted, but if nested then only the
+       topmost level performs quoting.
 
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
diff --git a/builtin/tag.c b/builtin/tag.c
index 9fa1400..f55dfda 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -43,8 +43,8 @@ static int list_tags(struct ref_filter *filter, struct 
ref_sorting *sorting, con
 
        if (!format) {
                if (filter->lines)
-                       format = to_free = 
xstrfmt("%%(align:15,left)%%(refname:short)%%(end) %%(contents:lines=%d)",
-                                                  filter->lines);
+                       format = to_free = 
xstrfmt("%%(align:15,left)%%(refname:short)%%(end) "
+                                                  "%%(contents:lines=%d)", 
filter->lines);
                else
                        format = "%(refname:short)";
        }
diff --git a/ref-filter.c b/ref-filter.c
index b37d57a..e3024d3 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -77,7 +77,7 @@ struct ref_formatting_stack {
        struct ref_formatting_stack *prev;
        struct strbuf output;
        void (*at_end)(struct ref_formatting_stack *stack);
-       void *cb_data;
+       void *at_end_data;
 };
 
 struct ref_formatting_state {
@@ -185,16 +185,6 @@ static void quote_formatting(struct strbuf *s, const char 
*str, int quote_style)
        }
 }
 
-static void align_handler(struct ref_formatting_stack *stack)
-{
-       struct align *align = (struct align *)stack->cb_data;
-       struct strbuf s = STRBUF_INIT;
-
-       strbuf_utf8_align(&s, align->position, align->width, stack->output.buf);
-       strbuf_swap(&stack->output, &s);
-       strbuf_release(&s);
-}
-
 static void append_atom(struct atom_value *v, struct ref_formatting_state 
*state)
 {
        /*
@@ -230,14 +220,24 @@ static void pop_stack_element(struct ref_formatting_stack 
**stack)
        *stack = prev;
 }
 
+static void end_align_handler(struct ref_formatting_stack *stack)
+{
+       struct align *align = (struct align *)stack->at_end_data;
+       struct strbuf s = STRBUF_INIT;
+
+       strbuf_utf8_align(&s, align->position, align->width, stack->output.buf);
+       strbuf_swap(&stack->output, &s);
+       strbuf_release(&s);
+}
+
 static void align_atom_handler(struct atom_value *atomv, struct 
ref_formatting_state *state)
 {
        struct ref_formatting_stack *new;
 
        push_stack_element(&state->stack);
        new = state->stack;
-       new->at_end = align_handler;
-       new->cb_data = &atomv->u.align;
+       new->at_end = end_align_handler;
+       new->at_end_data = &atomv->u.align;
 }
 
 static void end_atom_handler(struct atom_value *atomv, struct 
ref_formatting_state *state)
@@ -246,12 +246,13 @@ static void end_atom_handler(struct atom_value *atomv, 
struct ref_formatting_sta
        struct strbuf s = STRBUF_INIT;
 
        if (!current->at_end)
-               die(_("format: `end` atom used without a supporting atom"));
+               die(_("format: %%(end) atom used without corresponding atom"));
        current->at_end(current);
 
        /*
         * Perform quote formatting when the stack element is that of
-        * a modifier atom and right above the first stack element.
+        * a supporting atom. If nested then perform quote formatting
+        * only on the topmost supporting atom.
         */
        if (!state->stack->prev->prev) {
                quote_formatting(&s, current->output.buf, state->quote_style);
@@ -261,6 +262,22 @@ static void end_atom_handler(struct atom_value *atomv, 
struct ref_formatting_sta
        pop_stack_element(&state->stack);
 }
 
+static int match_atom_name(const char *name, const char *atom_name, const char 
**val)
+{
+       const char *body;
+
+       if (!skip_prefix(name, atom_name, &body))
+               return 0; /* doesn't even begin with "atom_name" */
+       if (!body[0] || !body[1]) {
+               *val = NULL; /* %(atom_name) and no customization */
+               return 1;
+       }
+       if (body[0] != ':')
+               return 0; /* "atom_namefoo" is not "atom_name" or 
"atom_name:..." */
+       *val = body + 1; /* "atomname:val" */
+       return 1;
+}
+
 /*
  * In a format string, find the next occurrence of %(atom).
  */
@@ -645,9 +662,6 @@ static void append_lines(struct strbuf *out, const char 
*buf, unsigned long size
        const char *sp, *eol;
        size_t len;
 
-       if ((sp = strstr(buf, "\n\n")) && (sp <= buf + size))
-               size += 2;
-
        sp = buf;
 
        for (i = 0; i < lines && sp < buf + size; i++) {
@@ -705,9 +719,12 @@ static void grab_sub_body_contents(struct atom_value *val, 
int deref, struct obj
                        v->s = xstrdup(subpos);
                else if (skip_prefix(name, "contents:lines=", &valp)) {
                        struct strbuf s = STRBUF_INIT;
+                       const char *contents_end = bodylen + bodypos - siglen;
+
                        if (strtoul_ui(valp, 10, &v->u.contents.lines))
-                               die(_("positive width expected 
contents:lines=%s"), valp);
-                       append_lines(&s, subpos, sublen + bodylen - siglen, 
v->u.contents.lines);
+                               die(_("positive value expected 
contents:lines=%s"), valp);
+                       /*  Size is the length of the message after removing 
the signature */
+                       append_lines(&s, subpos, contents_end - subpos, 
v->u.contents.lines);
                        v->s = strbuf_detach(&s, NULL);
                }
        }
@@ -830,10 +847,12 @@ static void populate_value(struct ref_array_item *ref)
                        refname = branch_get_push(branch, NULL);
                        if (!refname)
                                continue;
-               } else if (starts_with(name, "color:")) {
+               } else if (match_atom_name(name, "color", &valp)) {
                        char color[COLOR_MAXLEN] = "";
 
-                       if (color_parse(name + 6, color) < 0)
+                       if (!valp)
+                               die(_("expected format: %%(color:<color>)"));
+                       if (color_parse(valp, color) < 0)
                                die(_("unable to parse format"));
                        v->s = xstrdup(color);
                        continue;
@@ -863,24 +882,32 @@ static void populate_value(struct ref_array_item *ref)
                        else
                                v->s = " ";
                        continue;
-               } else if (skip_prefix(name, "align", &valp)) {
+               } else if (match_atom_name(name, "align", &valp)) {
                        struct align *align = &v->u.align;
                        struct strbuf **s;
 
-                       if (valp[0] != ':')
-                               die(_("format: usage 
%%(align:<width>,<position>)"));
-                       else
-                               valp++;
+                       if (!valp)
+                               die(_("expected format: %%(align:<width>, 
<position>)"));
 
+                       /*
+                        * TODO: Implement a function similar to 
strbuf_split_str()
+                        * which would strip the terminator at the end.
+                        */
                        s = strbuf_split_str(valp, ',', 0);
 
                        /* If the position is given trim the ',' from the first 
strbuf */
                        if (s[1])
                                strbuf_setlen(s[0], s[0]->len - 1);
+                       if (s[2])
+                               die(_("align:<width>,<position> followed by 
garbage: %s"), s[2]->buf);
 
                        if (strtoul_ui(s[0]->buf, 10, &align->width))
                                die(_("positive width expected align:%s"), 
s[0]->buf);
 
+                       /*
+                        * TODO: Implement a more general check, so that the 
values
+                        * do not always have to be in a specific order.
+                        */
                        if (!s[1])
                                align->position = ALIGN_LEFT;
                        else if (!strcmp(s[1]->buf, "left"))
@@ -1294,11 +1321,7 @@ static int ref_filter_handler(const char *refname, const 
struct object_id *oid,
                return 0;
        }
 
-       /*
-        * Get the current ref kind. If we're filtering tags, remotes or local 
branches
-        * only then the current ref-kind is nothing but filter->kind and 
filter_ref_kind()
-        * will only return that value.
-        */
+       /* Obtain the current ref kind from filter_ref_kind() and ignore 
unwanted refs. */
        kind = filter_ref_kind(filter, refname);
        if (!(kind & filter->kind))
                return 0;
@@ -1424,6 +1447,12 @@ int filter_refs(struct ref_array *array, struct 
ref_filter *filter, unsigned int
        if (!filter->kind)
                die("filter_refs: invalid type");
        else {
+               /*
+                * For common cases where we need only branches or remotes or 
tags,
+                * we only iterate through those refs. If a mix of refs is 
needed,
+                * we iterate over all refs and filter out required refs with 
the help
+                * of filter_ref_kind().
+                */
                if (filter->kind == FILTER_REFS_BRANCHES)
                        ret = for_each_fullref_in("refs/heads/", 
ref_filter_handler, &ref_cbdata, broken);
                else if (filter->kind == FILTER_REFS_REMOTES)
@@ -1561,7 +1590,7 @@ void show_ref_array_item(struct ref_array_item *info, 
const char *format, int qu
                append_atom(&resetv, &state);
        }
        if (state.stack->prev)
-               die(_("format: `end` atom missing"));
+               die(_("format: %%(end) atom missing"));
        final_buf = &state.stack->output;
        fwrite(final_buf->buf, 1, final_buf->len, stdout);
        pop_stack_element(&state.stack);
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index d95b781..4bc1055 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -81,6 +81,10 @@ test_expect_success 'filtering with --contains' '
        test_cmp expect actual
 '
 
+test_expect_success '%(color) must fail' '
+       test_must_fail git for-each-ref --format="%(color)%(refname)"
+'
+
 test_expect_success 'left alignment' '
        cat >expect <<-\EOF &&
        refname is refs/heads/master  |refs/heads/master
@@ -129,42 +133,39 @@ test_expect_success 'right alignment' '
        test_cmp expect actual
 '
 
-# Everything in between the %(align)...%(end) atom must be quoted, hence we 
test this by
-# introducing single quote's in %(align)...%(end), which must not be escaped.
-
-sq="'"
+# Individual atoms inside %(align:...) and %(end) must not be quoted.
 
-test_expect_success 'alignment with format quote' '
-       cat >expect <<-EOF &&
-       refname is ${sq}           ${sq}\\${sq}${sq}master${sq}\\${sq}${sq}     
      ${sq}|
-       refname is ${sq}            ${sq}\\${sq}${sq}side${sq}\\${sq}${sq}      
      ${sq}|
-       refname is ${sq}          ${sq}\\${sq}${sq}odd/spot${sq}\\${sq}${sq}    
      ${sq}|
-       refname is ${sq}         ${sq}\\${sq}${sq}double-tag${sq}\\${sq}${sq}   
      ${sq}|
-       refname is ${sq}            ${sq}\\${sq}${sq}four${sq}\\${sq}${sq}      
      ${sq}|
-       refname is ${sq}            ${sq}\\${sq}${sq}one${sq}\\${sq}${sq}       
      ${sq}|
-       refname is ${sq}         ${sq}\\${sq}${sq}signed-tag${sq}\\${sq}${sq}   
      ${sq}|
-       refname is ${sq}           ${sq}\\${sq}${sq}three${sq}\\${sq}${sq}      
      ${sq}|
-       refname is ${sq}            ${sq}\\${sq}${sq}two${sq}\\${sq}${sq}       
      ${sq}|
+test_expect_success 'alignment with format quote' "
+       cat >expect <<-\EOF &&
+       |'       master| A U Thor       '|
+       |'        side| A U Thor        '|
+       |'      odd/spot| A U Thor      '|
+       |'         double-tag|          '|
+       |'        four| A U Thor        '|
+       |'        one| A U Thor         '|
+       |'         signed-tag|          '|
+       |'       three| A U Thor        '|
+       |'        two| A U Thor         '|
        EOF
-       git for-each-ref --shell --format="refname is 
%(align:30,middle)${sq}%(refname:short)${sq}%(end)|" >actual &&
+       git for-each-ref --shell --format='|%(align:30,middle)%(refname:short)| 
%(authorname)%(end)|' >actual &&
        test_cmp expect actual
-'
+"
 
-test_expect_success 'nested alignment' '
+test_expect_success 'nested alignment with quote formatting' "
        cat >expect <<-\EOF &&
-       |         master               |
-       |           side               |
-       |       odd/spot               |
-       |     double-tag               |
-       |           four               |
-       |            one               |
-       |     signed-tag               |
-       |          three               |
-       |            two               |
+       |'         master               '|
+       |'           side               '|
+       |'       odd/spot               '|
+       |'     double-tag               '|
+       |'           four               '|
+       |'            one               '|
+       |'     signed-tag               '|
+       |'          three               '|
+       |'            two               '|
        EOF
-       git for-each-ref 
--format="|%(align:30,left)%(align:15,right)%(refname:short)%(end)%(end)|" 
>actual &&
+       git for-each-ref --shell 
--format='|%(align:30,left)%(align:15,right)%(refname:short)%(end)%(end)|' 
>actual &&
        test_cmp expect actual
-'
+"
 
 test_expect_success 'check `%(contents:lines=1)`' '
        cat >expect <<-\EOF &&

-- 
2.5.1

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to