This is part of porting tag.c to ref-filter APIs.
Version 13 can be found:
thread.gmane.org/gmane.comp.version-control.git/276363

Changes in this version:
* Introduce format_ref_array_item() and make show_ref_array_item() a
wrapper around the same.
* Introduce %(contents:lines=X) which gets the first X lines from a
given object.
* Change code in 05/13 to make the code neater and consistent.
* %(align) without arguments fails now.
* Add test for nested alignment.
* We perform quoting on each layer of nested alignment. 

Karthik Nayak (13):
  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: implement an `align` atom
  ref-filter: add option to filter out tags, branches and remotes
  ref-filter: introduce format_ref_array_item()
  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 |  13 ++
 Documentation/git-tag.txt          |  27 ++-
 builtin/for-each-ref.c             |   1 +
 builtin/tag.c                      | 368 ++++++--------------------------
 ref-filter.c                       | 418 ++++++++++++++++++++++++++++++++-----
 ref-filter.h                       |  32 ++-
 refs.c                             |   9 +
 refs.h                             |   1 +
 t/t6302-for-each-ref-filter.sh     | 137 ++++++++++++
 t/t7004-tag.sh                     |  47 ++++-
 utf8.c                             |  21 ++
 utf8.h                             |  15 ++
 12 files changed, 712 insertions(+), 377 deletions(-)

Interdiff between version 13 and version 14

diff --git a/Documentation/git-for-each-ref.txt 
b/Documentation/git-for-each-ref.txt
index 06d468e..1b48b95 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -149,6 +149,7 @@ Its first line is `contents:subject`, where subject is the 
concatenation
 of all lines of the commit message up to the first blank line.  The next
 line is 'contents:body', where body is all of the lines after the first
 blank line.  Finally, the optional GPG signature is `contents:signature`.
+The first `N` lines of the object is obtained using `contents:lines=N`.
 
 For sorting purposes, fields with numeric values sort in numeric
 order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 3ad6a64..4e9f6c2 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -75,7 +75,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char 
*prefix)
        if (!maxcount || array.nr < maxcount)
                maxcount = array.nr;
        for (i = 0; i < maxcount; i++)
-               show_ref_array_item(array.items[i], format, quote_style, 0);
+               show_ref_array_item(array.items[i], format, quote_style);
        ref_array_clear(&array);
        return 0;
 }
diff --git a/builtin/tag.c b/builtin/tag.c
index bbbcaed..9fa1400 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -33,6 +33,7 @@ static unsigned int colopts;
 static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, 
const char *format)
 {
        struct ref_array array;
+       char *to_free = NULL;
        int i;
 
        memset(&array, 0, sizeof(array));
@@ -42,7 +43,8 @@ static int list_tags(struct ref_filter *filter, struct 
ref_sorting *sorting, con
 
        if (!format) {
                if (filter->lines)
-                       format = "%(align:16,left)%(refname:short)%(end)";
+                       format = to_free = 
xstrfmt("%%(align:15,left)%%(refname:short)%%(end) %%(contents:lines=%d)",
+                                                  filter->lines);
                else
                        format = "%(refname:short)";
        }
@@ -52,8 +54,9 @@ static int list_tags(struct ref_filter *filter, struct 
ref_sorting *sorting, con
        ref_array_sort(sorting, &array);
 
        for (i = 0; i < array.nr; i++)
-               show_ref_array_item(array.items[i], format, QUOTE_NONE, 
filter->lines);
+               show_ref_array_item(array.items[i], format, 0);
        ref_array_clear(&array);
+       free(to_free);
 
        return 0;
 }
diff --git a/ref-filter.c b/ref-filter.c
index f8b8fb7..f268cd7 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -58,6 +58,7 @@ static struct {
        { "color" },
        { "align" },
        { "end" },
+       { "contents:lines" },
 };
 
 struct align {
@@ -65,6 +66,11 @@ struct align {
        unsigned int width;
 };
 
+struct contents {
+       unsigned int lines;
+       struct object_id oid;
+};
+
 #define REF_FORMATTING_STATE_INIT  { 0, NULL }
 
 struct ref_formatting_stack {
@@ -82,6 +88,7 @@ struct ref_formatting_state {
 struct atom_value {
        const char *s;
        struct align *align;
+       struct contents *contents;
        void (*handler)(struct atom_value *atomv, struct ref_formatting_state 
*state);
        unsigned long ul; /* used for sorting when not FIELD_STR */
 };
@@ -155,7 +162,28 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
        return at;
 }
 
-static void push_new_stack_element(struct ref_formatting_stack **stack)
+static void quote_formatting(struct strbuf *s, const char *str, int 
quote_style)
+{
+       switch (quote_style) {
+       case QUOTE_NONE:
+               strbuf_addstr(s, str);
+               break;
+       case QUOTE_SHELL:
+               sq_quote_buf(s, str);
+               break;
+       case QUOTE_PERL:
+               perl_quote_buf(s, str);
+               break;
+       case QUOTE_PYTHON:
+               python_quote_buf(s, str);
+               break;
+       case QUOTE_TCL:
+               tcl_quote_buf(s, str);
+               break;
+       }
+}
+
+static void push_stack_element(struct ref_formatting_stack **stack)
 {
        struct ref_formatting_stack *s = xcalloc(1, sizeof(struct 
ref_formatting_stack));
 
@@ -550,6 +578,61 @@ static void find_subpos(const char *buf, unsigned long sz,
        *nonsiglen = *sig - buf;
 }
 
+/*
+ * If 'lines' is greater than 0, append that many lines from the given
+ * object_id 'oid' to the given strbuf.
+ */
+static void append_tag_lines(struct strbuf *out, const struct object_id *oid, 
int lines)
+{
+       int i;
+       unsigned long size;
+       enum object_type type;
+       char *buf, *sp, *eol;
+       size_t len;
+
+       buf = read_sha1_file(oid->hash, &type, &size);
+       if (!buf)
+               die_errno("unable to read object %s", oid_to_hex(oid));
+       if (type != OBJ_COMMIT && type != OBJ_TAG)
+               goto free_return;
+       if (!size)
+               die("an empty %s object %s?",
+                   typename(type), oid_to_hex(oid));
+
+       /* skip header */
+       sp = strstr(buf, "\n\n");
+       if (!sp)
+               goto free_return;
+
+       /* only take up to "lines" lines, and strip the signature from a tag */
+       if (type == OBJ_TAG)
+               size = parse_signature(buf, size);
+       for (i = 0, sp += 2; i < lines && sp < buf + size; i++) {
+               if (i)
+                       strbuf_addstr(out, "\n    ");
+               eol = memchr(sp, '\n', size - (sp - buf));
+               len = eol ? eol - sp : size - (sp - buf);
+               strbuf_add(out, sp, len);
+               if (!eol)
+                       break;
+               sp = eol + 1;
+       }
+free_return:
+       free(buf);
+}
+
+static void contents_lines_handler(struct atom_value *atomv, struct 
ref_formatting_state *state)
+{
+       struct contents *contents = (struct contents *)atomv->contents;
+       struct strbuf s = STRBUF_INIT;
+
+       append_tag_lines(&s, &contents->oid, contents->lines);
+       quote_formatting(&state->stack->output, s.buf, state->quote_style);
+       strbuf_release(&s);
+
+       free(contents);
+}
+
 /* See grab_values */
 static void grab_sub_body_contents(struct atom_value *val, int deref, struct 
object *obj, void *buf, unsigned long sz)
 {
@@ -560,6 +643,7 @@ static void grab_sub_body_contents(struct atom_value *val, 
int deref, struct obj
        for (i = 0; i < used_atom_cnt; i++) {
                const char *name = used_atom[i];
                struct atom_value *v = &val[i];
+               const char *valp = NULL;
                if (!!deref != (*name == '*'))
                        continue;
                if (deref)
@@ -569,7 +653,8 @@ static void grab_sub_body_contents(struct atom_value *val, 
int deref, struct obj
                    strcmp(name, "contents") &&
                    strcmp(name, "contents:subject") &&
                    strcmp(name, "contents:body") &&
-                   strcmp(name, "contents:signature"))
+                   strcmp(name, "contents:signature") &&
+                   !starts_with(name, "contents:lines="))
                        continue;
                if (!subpos)
                        find_subpos(buf, sz,
@@ -589,6 +674,15 @@ static void grab_sub_body_contents(struct atom_value *val, 
int deref, struct obj
                        v->s = xmemdupz(sigpos, siglen);
                else if (!strcmp(name, "contents"))
                        v->s = xstrdup(subpos);
+               else if (skip_prefix(name, "contents:lines=", &valp)) {
+                       struct contents *contents = xmalloc(sizeof(struct 
contents));
+
+                       if (strtoul_ui(valp, 10, &contents->lines))
+                               die(_("positive width expected align:%s"), 
valp);
+                       hashcpy(contents->oid.hash, obj->sha1);
+                       v->handler = contents_lines_handler;
+                       v->contents = contents;
+               }
        }
 }
 
@@ -661,13 +755,25 @@ static void align_atom_handler(struct atom_value *atomv, 
struct ref_formatting_s
 {
        struct ref_formatting_stack *new;
 
-       push_new_stack_element(&state->stack);
+       push_stack_element(&state->stack);
        new = state->stack;
        new->at_end = align_handler;
        new->cb_data = atomv->align;
 }
 
-static void perform_quote_formatting(struct strbuf *s, const char *str, int 
quote_style);
+static void append_atom(struct atom_value *v, struct ref_formatting_state 
*state)
+{
+       /*
+        * Quote formatting is only done when the stack has a single
+        * element. Otherwise quote formatting is done on the
+        * element's entire output strbuf when the %(end) atom is
+        * encountered.
+        */
+       if (!state->stack->prev)
+               quote_formatting(&state->stack->output, v->s, 
state->quote_style);
+       else
+               strbuf_addstr(&state->stack->output, v->s);
+}
 
 static void end_atom_handler(struct atom_value *atomv, struct 
ref_formatting_state *state)
 {
@@ -682,8 +788,8 @@ static void end_atom_handler(struct atom_value *atomv, 
struct ref_formatting_sta
         * are using a certain modifier atom. In that case we need to
         * perform quote formatting.
         */
-       if (!state->stack->prev->prev) {
-               perform_quote_formatting(&s, current->output.buf, 
state->quote_style);
+       if (state->stack->prev) {
+               quote_formatting(&s, current->output.buf, state->quote_style);
                strbuf_reset(&current->output);
                strbuf_addbuf(&current->output, &s);
        }
@@ -722,6 +828,8 @@ static void populate_value(struct ref_array_item *ref)
                const char *valp;
                struct branch *branch = NULL;
 
+               v->handler = append_atom;
+
                if (*name == '*') {
                        deref = 1;
                        name++;
@@ -785,7 +893,9 @@ static void populate_value(struct ref_array_item *ref)
                        else
                                v->s = " ";
                        continue;
-               } else if (skip_prefix(name, "align:", &valp)) {
+               } else if (!strcmp(name, "align"))
+                       die(_("format: incomplete use of the `align` atom"));
+               else if (skip_prefix(name, "align:", &valp)) {
                        struct align *align = xmalloc(sizeof(struct align));
                        struct strbuf **s = strbuf_split_str(valp, ',', 0);
 
@@ -1172,12 +1282,10 @@ static int filter_ref_kind(struct ref_filter *filter, 
const char *refname)
                { "refs/tags/", FILTER_REFS_TAGS}
        };
 
-       if (filter->kind == FILTER_REFS_BRANCHES)
-               return FILTER_REFS_BRANCHES;
-       else if (filter->kind == FILTER_REFS_REMOTES)
-               return FILTER_REFS_REMOTES;
-       else if (filter->kind == FILTER_REFS_TAGS)
-               return FILTER_REFS_TAGS;
+       if (filter->kind == FILTER_REFS_BRANCHES ||
+           filter->kind == FILTER_REFS_REMOTES ||
+           filter->kind == FILTER_REFS_TAGS)
+               return filter->kind;
        else if (!strcmp(refname, "HEAD"))
                return FILTER_REFS_DETACHED_HEAD;
 
@@ -1211,6 +1319,11 @@ 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.
+        */
        kind = filter_ref_kind(filter, refname);
        if (!(kind & filter->kind))
                return 0;
@@ -1328,25 +1441,26 @@ int filter_refs(struct ref_array *array, struct 
ref_filter *filter, unsigned int
        ref_cbdata.array = array;
        ref_cbdata.filter = filter;
 
-       /*  Simple per-ref filtering */
-       if (type & FILTER_REFS_INCLUDE_BROKEN) {
-               type &= ~FILTER_REFS_INCLUDE_BROKEN;
+       if (type & FILTER_REFS_INCLUDE_BROKEN)
                broken = 1;
-       }
+       filter->kind = type & FILTER_REFS_KIND_MASK;
 
-       filter->kind = type;
-       if (type == FILTER_REFS_BRANCHES)
-               ret = for_each_reftype_fullpath(ref_filter_handler, 
"refs/heads/", broken, &ref_cbdata);
-       else if (type == FILTER_REFS_REMOTES)
-               ret = for_each_reftype_fullpath(ref_filter_handler, 
"refs/remotes/", broken, &ref_cbdata);
-       else if (type == FILTER_REFS_TAGS)
-               ret = for_each_reftype_fullpath(ref_filter_handler, 
"refs/tags/", broken, &ref_cbdata);
-       else if (type & FILTER_REFS_ALL) {
-               ret = for_each_reftype_fullpath(ref_filter_handler, "", broken, 
&ref_cbdata);
-               if (type & FILTER_REFS_DETACHED_HEAD)
-                       head_ref(ref_filter_handler, &ref_cbdata);
-       } else
+       /*  Simple per-ref filtering */
+       if (!filter->kind)
                die("filter_refs: invalid type");
+       else {
+               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)
+                       ret = for_each_fullref_in("refs/remotes/", 
ref_filter_handler, &ref_cbdata, broken);
+               else if (filter->kind == FILTER_REFS_TAGS)
+                       ret = for_each_fullref_in("refs/tags/", 
ref_filter_handler, &ref_cbdata, broken);
+               else if (filter->kind & FILTER_REFS_ALL)
+                       ret = for_each_fullref_in("", ref_filter_handler, 
&ref_cbdata, broken);
+               if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
+                       head_ref(ref_filter_handler, &ref_cbdata);
+       }
+
 
        /*  Filters that need revision walking */
        if (filter->merge_commit)
@@ -1400,33 +1514,6 @@ void ref_array_sort(struct ref_sorting *sorting, struct 
ref_array *array)
        qsort(array->items, array->nr, sizeof(struct ref_array_item *), 
compare_refs);
 }
 
-static void perform_quote_formatting(struct strbuf *s, const char *str, int 
quote_style)
-{
-       switch (quote_style) {
-       case QUOTE_NONE:
-               strbuf_addstr(s, str);
-               break;
-       case QUOTE_SHELL:
-               sq_quote_buf(s, str);
-               break;
-       case QUOTE_PERL:
-               perl_quote_buf(s, str);
-               break;
-       case QUOTE_PYTHON:
-               python_quote_buf(s, str);
-               break;
-       case QUOTE_TCL:
-               tcl_quote_buf(s, str);
-               break;
-       }
-}
-
-static void append_atom(struct atom_value *v, struct ref_formatting_state 
*state)
-{
-       struct strbuf *s = &state->stack->output;
-       perform_quote_formatting(s, v->s, state->quote_style);
-}
-
 static int hex1(char ch)
 {
        if ('0' <= ch && ch <= '9')
@@ -1467,58 +1554,14 @@ static void append_literal(const char *cp, const char 
*ep, struct ref_formatting
        }
 }
 
-/*
- * If 'lines' is greater than 0, print that many lines from the given
- * object_id 'oid'.
- */
-static void show_tag_lines(const struct object_id *oid, int lines)
-{
-       int i;
-       unsigned long size;
-       enum object_type type;
-       char *buf, *sp, *eol;
-       size_t len;
-
-       buf = read_sha1_file(oid->hash, &type, &size);
-       if (!buf)
-               die_errno("unable to read object %s", oid_to_hex(oid));
-       if (type != OBJ_COMMIT && type != OBJ_TAG)
-               goto free_return;
-       if (!size)
-               die("an empty %s object %s?",
-                   typename(type), oid_to_hex(oid));
-
-       /* skip header */
-       sp = strstr(buf, "\n\n");
-       if (!sp)
-               goto free_return;
-
-       /* only take up to "lines" lines, and strip the signature from a tag */
-       if (type == OBJ_TAG)
-               size = parse_signature(buf, size);
-       for (i = 0, sp += 2; i < lines && sp < buf + size; i++) {
-               if (i)
-                       printf("\n    ");
-               eol = memchr(sp, '\n', size - (sp - buf));
-               len = eol ? eol - sp : size - (sp - buf);
-               fwrite(sp, len, 1, stdout);
-               if (!eol)
-                       break;
-               sp = eol + 1;
-       }
-free_return:
-       free(buf);
-}
-
-void show_ref_array_item(struct ref_array_item *info, const char *format,
-                        int quote_style, unsigned int lines)
+void format_ref_array_item(struct strbuf *out, struct ref_array_item *info,
+                          const char *format, int quote_style)
 {
        const char *cp, *sp, *ep;
-       struct strbuf *final_buf;
        struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
 
        state.quote_style = quote_style;
-       push_new_stack_element(&state.stack);
+       push_stack_element(&state.stack);
 
        for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
                struct atom_value *atomv;
@@ -1527,18 +1570,7 @@ void show_ref_array_item(struct ref_array_item *info, 
const char *format,
                if (cp < sp)
                        append_literal(cp, sp, &state);
                get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), 
&atomv);
-               /*
-                * If the atom is a modifier atom, then call the handler 
function.
-                * Else, if this is the first element on the stack, then we 
need to
-                * format the atom as per the given quote. Else we just add the 
atom value
-                * to the current stack element and handle quote formatting at 
the end.
-                */
-               if (atomv->handler)
-                       atomv->handler(atomv, &state);
-               else if (!state.stack->prev)
-                       append_atom(atomv, &state);
-               else
-                       strbuf_addstr(&state.stack->output, atomv->s);
+               atomv->handler(atomv, &state);
        }
        if (*cp) {
                sp = cp + strlen(cp);
@@ -1555,15 +1587,17 @@ void show_ref_array_item(struct ref_array_item *info, 
const char *format,
        }
        if (state.stack->prev)
                die(_("format: `end` atom missing"));
-       final_buf = &state.stack->output;
-       fwrite(final_buf->buf, 1, final_buf->len, stdout);
+       strbuf_addbuf(out, &state.stack->output);
        pop_stack_element(&state.stack);
-       if (lines > 0) {
-               struct object_id oid;
-               hashcpy(oid.hash, info->objectname);
-               show_tag_lines(&oid, lines);
-       }
-       putchar('\n');
+}
+
+void show_ref_array_item(struct ref_array_item *item, const char *format, 
unsigned int quote_style)
+{
+       struct strbuf out = STRBUF_INIT;
+       format_ref_array_item(&out, item, format, quote_style);
+       fwrite(out.buf, out.len, 1, stdout);
+       printf("\n");
+       strbuf_release(&out);
 }
 
 /*  If no sorting option is given, use refname to sort as default */
diff --git a/ref-filter.h b/ref-filter.h
index 8241066..179944c 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -21,6 +21,7 @@
 #define FILTER_REFS_ALL            (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
                                    FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
 #define FILTER_REFS_DETACHED_HEAD  0x0020
+#define FILTER_REFS_KIND_MASK      (FILTER_REFS_ALL | 
FILTER_REFS_DETACHED_HEAD)
 
 struct atom_value;
 
@@ -93,12 +94,11 @@ int parse_ref_filter_atom(const char *atom, const char *ep);
 int verify_ref_format(const char *format);
 /*  Sort the given ref_array as per the ref_sorting provided */
 void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
-/*
- * Print the ref using the given format and quote_style. If 'lines' > 0,
- * print that many lines of the the given ref.
- */
-void show_ref_array_item(struct ref_array_item *info, const char *format,
-                        int quote_style, unsigned int lines);
+/*  Format the ref as per given format and quote_style and store it into the 
strbuf */
+void format_ref_array_item(struct strbuf *out, struct ref_array_item *info,
+                          const char *format, int quote_style);
+/*  Wrapper around format_ref_array_item() which prints the given 
ref_array_item */
+void show_ref_array_item(struct ref_array_item *item, const char *format, 
unsigned int quote_style);
 /*  Callback function for parsing the sort option */
 int parse_opt_ref_sorting(const struct option *opt, const char *arg, int 
unset);
 /*  Default sort option based on refname */
diff --git a/refs.c b/refs.c
index 3266617..a9469c2 100644
--- a/refs.c
+++ b/refs.c
@@ -2108,6 +2108,15 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, 
void *cb_data)
        return do_for_each_ref(&ref_cache, prefix, fn, strlen(prefix), 0, 
cb_data);
 }
 
+int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, 
unsigned int broken)
+{
+       unsigned int flag = 0;
+
+       if (broken)
+               flag = DO_FOR_EACH_INCLUDE_BROKEN;
+       return do_for_each_ref(&ref_cache, prefix, fn, 0, flag, cb_data);
+}
+
 int for_each_ref_in_submodule(const char *submodule, const char *prefix,
                each_ref_fn fn, void *cb_data)
 {
@@ -2150,15 +2159,6 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
                               strlen(git_replace_ref_base), 0, cb_data);
 }
 
-int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, 
void *cb_data)
-{
-       unsigned int flag = 0;
-
-       if (broken)
-               flag = DO_FOR_EACH_INCLUDE_BROKEN;
-       return do_for_each_ref(&ref_cache, type, fn, 0, flag, cb_data);
-}
-
 int head_ref_namespaced(each_ref_fn fn, void *cb_data)
 {
        struct strbuf buf = STRBUF_INIT;
diff --git a/refs.h b/refs.h
index 6e913ee..6d30c98 100644
--- a/refs.h
+++ b/refs.h
@@ -173,13 +173,13 @@ typedef int each_ref_fn(const char *refname,
 extern int head_ref(each_ref_fn fn, void *cb_data);
 extern int for_each_ref(each_ref_fn fn, void *cb_data);
 extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
+extern int for_each_fullref_in(const char *prefix, each_ref_fn fn, void 
*cb_data, unsigned int broken);
 extern int for_each_tag_ref(each_ref_fn fn, void *cb_data);
 extern int for_each_branch_ref(each_ref_fn fn, void *cb_data);
 extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);
 extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);
 extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void 
*cb_data);
 extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const 
char *prefix, void *cb_data);
-extern int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int 
broken, void *cb_data);
 
 extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void 
*cb_data);
 extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void 
*cb_data);
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 38c99c9..8f18f86 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -150,6 +150,38 @@ test_expect_success 'alignment with format quote' '
        test_cmp expect actual
 '
 
+test_expect_success 'nested alignment' '
+       cat >expect <<-\EOF &&
+       |         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 &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check `%(contents:lines=X)`' '
+       cat >expect <<-\EOF &&
+       master three
+       side four
+       odd/spot three
+       double-tag Annonated doubly
+       four four
+       one one
+       signed-tag A signed tag message
+       three three
+       two two
+       EOF
+       git for-each-ref --format="%(refname:short) %(contents:lines=1)" 
>actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'setup for version sort' '
        test_commit foo1.3 &&
        test_commit foo1.6 &&


-- 
2.5.0

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

Reply via email to