pretty: allow %(trailers) options with explicit value
[gitweb.git] / ref-filter.c
index f9e25aea7a97e18b5723c8fa379da859d6dc9fcf..0c45ed9d94a4bd4bfab6c9d441ca9bc7de7ebf31 100644 (file)
@@ -3,6 +3,8 @@
 #include "parse-options.h"
 #include "refs.h"
 #include "wildmatch.h"
+#include "object-store.h"
+#include "repository.h"
 #include "commit.h"
 #include "remote.h"
 #include "color.h"
@@ -16,6 +18,8 @@
 #include "trailer.h"
 #include "wt-status.h"
 #include "commit-slab.h"
+#include "commit-graph.h"
+#include "commit-reach.h"
 
 static struct ref_msg {
        const char *gone;
@@ -40,6 +44,7 @@ void setup_ref_filter_porcelain_msg(void)
 
 typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
 typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;
+typedef enum { SOURCE_NONE = 0, SOURCE_OBJ, SOURCE_OTHER } info_source;
 
 struct align {
        align_type position;
@@ -59,6 +64,17 @@ struct refname_atom {
        int lstrip, rstrip;
 };
 
+static struct expand_data {
+       struct object_id oid;
+       enum object_type type;
+       unsigned long size;
+       off_t disk_size;
+       struct object_id delta_base_oid;
+       void *content;
+
+       struct object_info info;
+} oi, oi_deref;
+
 /*
  * An atom is a valid field atom listed below, possibly prefixed with
  * a "*" to denote deref_tag().
@@ -72,6 +88,7 @@ struct refname_atom {
 static struct used_atom {
        const char *name;
        cmp_type type;
+       info_source source;
        union {
                char color[COLOR_MAXLEN];
                struct align align;
@@ -101,22 +118,38 @@ static struct used_atom {
 } *used_atom;
 static int used_atom_cnt, need_tagged, need_symref;
 
-static void color_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *color_value)
+/*
+ * Expand string, append it to strbuf *sb, then return error code ret.
+ * Allow to save few lines of code.
+ */
+static int strbuf_addf_ret(struct strbuf *sb, int ret, const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       strbuf_vaddf(sb, fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
+static int color_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                            const char *color_value, struct strbuf *err)
 {
        if (!color_value)
-               die(_("expected format: %%(color:<color>)"));
+               return strbuf_addf_ret(err, -1, _("expected format: %%(color:<color>)"));
        if (color_parse(color_value, atom->u.color) < 0)
-               die(_("unrecognized color: %%(color:%s)"), color_value);
+               return strbuf_addf_ret(err, -1, _("unrecognized color: %%(color:%s)"),
+                                      color_value);
        /*
         * We check this after we've parsed the color, which lets us complain
         * about syntactically bogus color names even if they won't be used.
         */
        if (!want_color(format->use_color))
                color_parse("", atom->u.color);
+       return 0;
 }
 
-static void refname_atom_parser_internal(struct refname_atom *atom,
-                                        const char *arg, const char *name)
+static int refname_atom_parser_internal(struct refname_atom *atom, const char *arg,
+                                        const char *name, struct strbuf *err)
 {
        if (!arg)
                atom->option = R_NORMAL;
@@ -126,16 +159,18 @@ static void refname_atom_parser_internal(struct refname_atom *atom,
                 skip_prefix(arg, "strip=", &arg)) {
                atom->option = R_LSTRIP;
                if (strtol_i(arg, 10, &atom->lstrip))
-                       die(_("Integer value expected refname:lstrip=%s"), arg);
+                       return strbuf_addf_ret(err, -1, _("Integer value expected refname:lstrip=%s"), arg);
        } else if (skip_prefix(arg, "rstrip=", &arg)) {
                atom->option = R_RSTRIP;
                if (strtol_i(arg, 10, &atom->rstrip))
-                       die(_("Integer value expected refname:rstrip=%s"), arg);
+                       return strbuf_addf_ret(err, -1, _("Integer value expected refname:rstrip=%s"), arg);
        } else
-               die(_("unrecognized %%(%s) argument: %s"), name, arg);
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), name, arg);
+       return 0;
 }
 
-static void remote_ref_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
+static int remote_ref_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                                 const char *arg, struct strbuf *err)
 {
        struct string_list params = STRING_LIST_INIT_DUP;
        int i;
@@ -145,9 +180,8 @@ static void remote_ref_atom_parser(const struct ref_format *format, struct used_
 
        if (!arg) {
                atom->u.remote_ref.option = RR_REF;
-               refname_atom_parser_internal(&atom->u.remote_ref.refname,
-                                            arg, atom->name);
-               return;
+               return refname_atom_parser_internal(&atom->u.remote_ref.refname,
+                                                   arg, atom->name, err);
        }
 
        atom->u.remote_ref.nobracket = 0;
@@ -170,33 +204,68 @@ static void remote_ref_atom_parser(const struct ref_format *format, struct used_
                        atom->u.remote_ref.push_remote = 1;
                } else {
                        atom->u.remote_ref.option = RR_REF;
-                       refname_atom_parser_internal(&atom->u.remote_ref.refname,
-                                                    arg, atom->name);
+                       if (refname_atom_parser_internal(&atom->u.remote_ref.refname,
+                                                        arg, atom->name, err)) {
+                               string_list_clear(&params, 0);
+                               return -1;
+                       }
                }
        }
 
        string_list_clear(&params, 0);
+       return 0;
+}
+
+static int objecttype_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                                 const char *arg, struct strbuf *err)
+{
+       if (arg)
+               return strbuf_addf_ret(err, -1, _("%%(objecttype) does not take arguments"));
+       if (*atom->name == '*')
+               oi_deref.info.typep = &oi_deref.type;
+       else
+               oi.info.typep = &oi.type;
+       return 0;
 }
 
-static void body_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
+static int objectsize_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                                 const char *arg, struct strbuf *err)
 {
        if (arg)
-               die(_("%%(body) does not take arguments"));
+               return strbuf_addf_ret(err, -1, _("%%(objectsize) does not take arguments"));
+       if (*atom->name == '*')
+               oi_deref.info.sizep = &oi_deref.size;
+       else
+               oi.info.sizep = &oi.size;
+       return 0;
+}
+
+static int body_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                           const char *arg, struct strbuf *err)
+{
+       if (arg)
+               return strbuf_addf_ret(err, -1, _("%%(body) does not take arguments"));
        atom->u.contents.option = C_BODY_DEP;
+       return 0;
 }
 
-static void subject_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
+static int subject_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                              const char *arg, struct strbuf *err)
 {
        if (arg)
-               die(_("%%(subject) does not take arguments"));
+               return strbuf_addf_ret(err, -1, _("%%(subject) does not take arguments"));
        atom->u.contents.option = C_SUB;
+       return 0;
 }
 
-static void trailers_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
+static int trailers_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                               const char *arg, struct strbuf *err)
 {
        struct string_list params = STRING_LIST_INIT_DUP;
        int i;
 
+       atom->u.contents.trailer_opts.no_divider = 1;
+
        if (arg) {
                string_list_split(&params, arg, ',', -1);
                for (i = 0; i < params.nr; i++) {
@@ -205,15 +274,20 @@ static void trailers_atom_parser(const struct ref_format *format, struct used_at
                                atom->u.contents.trailer_opts.unfold = 1;
                        else if (!strcmp(s, "only"))
                                atom->u.contents.trailer_opts.only_trailers = 1;
-                       else
-                               die(_("unknown %%(trailers) argument: %s"), s);
+                       else {
+                               strbuf_addf(err, _("unknown %%(trailers) argument: %s"), s);
+                               string_list_clear(&params, 0);
+                               return -1;
+                       }
                }
        }
        atom->u.contents.option = C_TRAILERS;
        string_list_clear(&params, 0);
+       return 0;
 }
 
-static void contents_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
+static int contents_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                               const char *arg, struct strbuf *err)
 {
        if (!arg)
                atom->u.contents.option = C_BARE;
@@ -225,16 +299,19 @@ static void contents_atom_parser(const struct ref_format *format, struct used_at
                atom->u.contents.option = C_SUB;
        else if (skip_prefix(arg, "trailers", &arg)) {
                skip_prefix(arg, ":", &arg);
-               trailers_atom_parser(format, atom, *arg ? arg : NULL);
+               if (trailers_atom_parser(format, atom, *arg ? arg : NULL, err))
+                       return -1;
        } else if (skip_prefix(arg, "lines=", &arg)) {
                atom->u.contents.option = C_LINES;
                if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
-                       die(_("positive value expected contents:lines=%s"), arg);
+                       return strbuf_addf_ret(err, -1, _("positive value expected contents:lines=%s"), arg);
        } else
-               die(_("unrecognized %%(contents) argument: %s"), arg);
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(contents) argument: %s"), arg);
+       return 0;
 }
 
-static void objectname_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
+static int objectname_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                                 const char *arg, struct strbuf *err)
 {
        if (!arg)
                atom->u.objectname.option = O_FULL;
@@ -244,16 +321,18 @@ static void objectname_atom_parser(const struct ref_format *format, struct used_
                atom->u.objectname.option = O_LENGTH;
                if (strtoul_ui(arg, 10, &atom->u.objectname.length) ||
                    atom->u.objectname.length == 0)
-                       die(_("positive value expected objectname:short=%s"), arg);
+                       return strbuf_addf_ret(err, -1, _("positive value expected objectname:short=%s"), arg);
                if (atom->u.objectname.length < MINIMUM_ABBREV)
                        atom->u.objectname.length = MINIMUM_ABBREV;
        } else
-               die(_("unrecognized %%(objectname) argument: %s"), arg);
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(objectname) argument: %s"), arg);
+       return 0;
 }
 
-static void refname_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
+static int refname_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                              const char *arg, struct strbuf *err)
 {
-       refname_atom_parser_internal(&atom->u.refname, arg, atom->name);
+       return refname_atom_parser_internal(&atom->u.refname, arg, atom->name, err);
 }
 
 static align_type parse_align_position(const char *s)
@@ -267,7 +346,8 @@ static align_type parse_align_position(const char *s)
        return -1;
 }
 
-static void align_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
+static int align_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                            const char *arg, struct strbuf *err)
 {
        struct align *align = &atom->u.align;
        struct string_list params = STRING_LIST_INIT_DUP;
@@ -275,7 +355,7 @@ static void align_atom_parser(const struct ref_format *format, struct used_atom
        unsigned int width = ~0U;
 
        if (!arg)
-               die(_("expected format: %%(align:<width>,<position>)"));
+               return strbuf_addf_ret(err, -1, _("expected format: %%(align:<width>,<position>)"));
 
        align->position = ALIGN_LEFT;
 
@@ -286,89 +366,106 @@ static void align_atom_parser(const struct ref_format *format, struct used_atom
 
                if (skip_prefix(s, "position=", &s)) {
                        position = parse_align_position(s);
-                       if (position < 0)
-                               die(_("unrecognized position:%s"), s);
+                       if (position < 0) {
+                               strbuf_addf(err, _("unrecognized position:%s"), s);
+                               string_list_clear(&params, 0);
+                               return -1;
+                       }
                        align->position = position;
                } else if (skip_prefix(s, "width=", &s)) {
-                       if (strtoul_ui(s, 10, &width))
-                               die(_("unrecognized width:%s"), s);
+                       if (strtoul_ui(s, 10, &width)) {
+                               strbuf_addf(err, _("unrecognized width:%s"), s);
+                               string_list_clear(&params, 0);
+                               return -1;
+                       }
                } else if (!strtoul_ui(s, 10, &width))
                        ;
                else if ((position = parse_align_position(s)) >= 0)
                        align->position = position;
-               else
-                       die(_("unrecognized %%(align) argument: %s"), s);
+               else {
+                       strbuf_addf(err, _("unrecognized %%(align) argument: %s"), s);
+                       string_list_clear(&params, 0);
+                       return -1;
+               }
        }
 
-       if (width == ~0U)
-               die(_("positive width expected with the %%(align) atom"));
+       if (width == ~0U) {
+               string_list_clear(&params, 0);
+               return strbuf_addf_ret(err, -1, _("positive width expected with the %%(align) atom"));
+       }
        align->width = width;
        string_list_clear(&params, 0);
+       return 0;
 }
 
-static void if_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
+static int if_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                         const char *arg, struct strbuf *err)
 {
        if (!arg) {
                atom->u.if_then_else.cmp_status = COMPARE_NONE;
-               return;
+               return 0;
        } else if (skip_prefix(arg, "equals=", &atom->u.if_then_else.str)) {
                atom->u.if_then_else.cmp_status = COMPARE_EQUAL;
        } else if (skip_prefix(arg, "notequals=", &atom->u.if_then_else.str)) {
                atom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;
-       } else {
-               die(_("unrecognized %%(if) argument: %s"), arg);
-       }
+       } else
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(if) argument: %s"), arg);
+       return 0;
 }
 
-static void head_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
+static int head_atom_parser(const struct ref_format *format, struct used_atom *atom,
+                           const char *arg, struct strbuf *unused_err)
 {
        atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL);
+       return 0;
 }
 
 static struct {
        const char *name;
+       info_source source;
        cmp_type cmp_type;
-       void (*parser)(const struct ref_format *format, struct used_atom *atom, const char *arg);
+       int (*parser)(const struct ref_format *format, struct used_atom *atom,
+                     const char *arg, struct strbuf *err);
 } valid_atom[] = {
-       { "refname" , FIELD_STR, refname_atom_parser },
-       { "objecttype" },
-       { "objectsize", FIELD_ULONG },
-       { "objectname", FIELD_STR, objectname_atom_parser },
-       { "tree" },
-       { "parent" },
-       { "numparent", FIELD_ULONG },
-       { "object" },
-       { "type" },
-       { "tag" },
-       { "author" },
-       { "authorname" },
-       { "authoremail" },
-       { "authordate", FIELD_TIME },
-       { "committer" },
-       { "committername" },
-       { "committeremail" },
-       { "committerdate", FIELD_TIME },
-       { "tagger" },
-       { "taggername" },
-       { "taggeremail" },
-       { "taggerdate", FIELD_TIME },
-       { "creator" },
-       { "creatordate", FIELD_TIME },
-       { "subject", FIELD_STR, subject_atom_parser },
-       { "body", FIELD_STR, body_atom_parser },
-       { "trailers", FIELD_STR, trailers_atom_parser },
-       { "contents", FIELD_STR, contents_atom_parser },
-       { "upstream", FIELD_STR, remote_ref_atom_parser },
-       { "push", FIELD_STR, remote_ref_atom_parser },
-       { "symref", FIELD_STR, refname_atom_parser },
-       { "flag" },
-       { "HEAD", FIELD_STR, head_atom_parser },
-       { "color", FIELD_STR, color_atom_parser },
-       { "align", FIELD_STR, align_atom_parser },
-       { "end" },
-       { "if", FIELD_STR, if_atom_parser },
-       { "then" },
-       { "else" },
+       { "refname", SOURCE_NONE, FIELD_STR, refname_atom_parser },
+       { "objecttype", SOURCE_OTHER, FIELD_STR, objecttype_atom_parser },
+       { "objectsize", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser },
+       { "objectname", SOURCE_OTHER, FIELD_STR, objectname_atom_parser },
+       { "tree", SOURCE_OBJ },
+       { "parent", SOURCE_OBJ },
+       { "numparent", SOURCE_OBJ, FIELD_ULONG },
+       { "object", SOURCE_OBJ },
+       { "type", SOURCE_OBJ },
+       { "tag", SOURCE_OBJ },
+       { "author", SOURCE_OBJ },
+       { "authorname", SOURCE_OBJ },
+       { "authoremail", SOURCE_OBJ },
+       { "authordate", SOURCE_OBJ, FIELD_TIME },
+       { "committer", SOURCE_OBJ },
+       { "committername", SOURCE_OBJ },
+       { "committeremail", SOURCE_OBJ },
+       { "committerdate", SOURCE_OBJ, FIELD_TIME },
+       { "tagger", SOURCE_OBJ },
+       { "taggername", SOURCE_OBJ },
+       { "taggeremail", SOURCE_OBJ },
+       { "taggerdate", SOURCE_OBJ, FIELD_TIME },
+       { "creator", SOURCE_OBJ },
+       { "creatordate", SOURCE_OBJ, FIELD_TIME },
+       { "subject", SOURCE_OBJ, FIELD_STR, subject_atom_parser },
+       { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser },
+       { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser },
+       { "contents", SOURCE_OBJ, FIELD_STR, contents_atom_parser },
+       { "upstream", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
+       { "push", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser },
+       { "symref", SOURCE_NONE, FIELD_STR, refname_atom_parser },
+       { "flag", SOURCE_NONE },
+       { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser },
+       { "color", SOURCE_NONE, FIELD_STR, color_atom_parser },
+       { "align", SOURCE_NONE, FIELD_STR, align_atom_parser },
+       { "end", SOURCE_NONE },
+       { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
+       { "then", SOURCE_NONE },
+       { "else", SOURCE_NONE },
 };
 
 #define REF_FORMATTING_STATE_INIT  { 0, NULL }
@@ -387,7 +484,8 @@ struct ref_formatting_state {
 
 struct atom_value {
        const char *s;
-       void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
+       int (*handler)(struct atom_value *atomv, struct ref_formatting_state *state,
+                      struct strbuf *err);
        uintmax_t value; /* used for sorting when not FIELD_STR */
        struct used_atom *atom;
 };
@@ -396,7 +494,8 @@ struct atom_value {
  * Used to parse format string and sort specifiers
  */
 static int parse_ref_filter_atom(const struct ref_format *format,
-                                const char *atom, const char *ep)
+                                const char *atom, const char *ep,
+                                struct strbuf *err)
 {
        const char *sp;
        const char *arg;
@@ -406,7 +505,8 @@ static int parse_ref_filter_atom(const struct ref_format *format,
        if (*sp == '*' && sp < ep)
                sp++; /* deref */
        if (ep <= sp)
-               die(_("malformed field name: %.*s"), (int)(ep-atom), atom);
+               return strbuf_addf_ret(err, -1, _("malformed field name: %.*s"),
+                                      (int)(ep-atom), atom);
 
        /* Do we have the atom already used elsewhere? */
        for (i = 0; i < used_atom_cnt; i++) {
@@ -432,7 +532,8 @@ static int parse_ref_filter_atom(const struct ref_format *format,
        }
 
        if (ARRAY_SIZE(valid_atom) <= i)
-               die(_("unknown field name: %.*s"), (int)(ep-atom), atom);
+               return strbuf_addf_ret(err, -1, _("unknown field name: %.*s"),
+                                      (int)(ep-atom), atom);
 
        /* Add it in, including the deref prefix */
        at = used_atom_cnt;
@@ -440,6 +541,13 @@ static int parse_ref_filter_atom(const struct ref_format *format,
        REALLOC_ARRAY(used_atom, used_atom_cnt);
        used_atom[at].name = xmemdupz(atom, ep - atom);
        used_atom[at].type = valid_atom[i].cmp_type;
+       used_atom[at].source = valid_atom[i].source;
+       if (used_atom[at].source == SOURCE_OBJ) {
+               if (*atom == '*')
+                       oi_deref.info.contentp = &oi_deref.content;
+               else
+                       oi.info.contentp = &oi.content;
+       }
        if (arg) {
                arg = used_atom[at].name + (arg - atom) + 1;
                if (!*arg) {
@@ -451,8 +559,8 @@ static int parse_ref_filter_atom(const struct ref_format *format,
                }
        }
        memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
-       if (valid_atom[i].parser)
-               valid_atom[i].parser(format, &used_atom[at], arg);
+       if (valid_atom[i].parser && valid_atom[i].parser(format, &used_atom[at], arg, err))
+               return -1;
        if (*atom == '*')
                need_tagged = 1;
        if (!strcmp(valid_atom[i].name, "symref"))
@@ -481,7 +589,8 @@ static void quote_formatting(struct strbuf *s, const char *str, int quote_style)
        }
 }
 
-static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
+static int append_atom(struct atom_value *v, struct ref_formatting_state *state,
+                      struct strbuf *unused_err)
 {
        /*
         * Quote formatting is only done when the stack has a single
@@ -493,6 +602,7 @@ static void append_atom(struct atom_value *v, struct ref_formatting_state *state
                quote_formatting(&state->stack->output, v->s, state->quote_style);
        else
                strbuf_addstr(&state->stack->output, v->s);
+       return 0;
 }
 
 static void push_stack_element(struct ref_formatting_stack **stack)
@@ -527,14 +637,16 @@ static void end_align_handler(struct ref_formatting_stack **stack)
        strbuf_release(&s);
 }
 
-static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+static int align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+                             struct strbuf *unused_err)
 {
-       struct ref_formatting_stack *new;
+       struct ref_formatting_stack *new_stack;
 
        push_stack_element(&state->stack);
-       new = state->stack;
-       new->at_end = end_align_handler;
-       new->at_end_data = &atomv->atom->u.align;
+       new_stack = state->stack;
+       new_stack->at_end = end_align_handler;
+       new_stack->at_end_data = &atomv->atom->u.align;
+       return 0;
 }
 
 static void if_then_else_handler(struct ref_formatting_stack **stack)
@@ -572,18 +684,20 @@ static void if_then_else_handler(struct ref_formatting_stack **stack)
        free(if_then_else);
 }
 
-static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+                          struct strbuf *unused_err)
 {
-       struct ref_formatting_stack *new;
+       struct ref_formatting_stack *new_stack;
        struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);
 
        if_then_else->str = atomv->atom->u.if_then_else.str;
        if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;
 
        push_stack_element(&state->stack);
-       new = state->stack;
-       new->at_end = if_then_else_handler;
-       new->at_end_data = if_then_else;
+       new_stack = state->stack;
+       new_stack->at_end = if_then_else_handler;
+       new_stack->at_end_data = if_then_else;
+       return 0;
 }
 
 static int is_empty(const char *s)
@@ -596,7 +710,8 @@ static int is_empty(const char *s)
        return 1;
 }
 
-static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+static int then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+                            struct strbuf *err)
 {
        struct ref_formatting_stack *cur = state->stack;
        struct if_then_else *if_then_else = NULL;
@@ -604,11 +719,11 @@ static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_st
        if (cur->at_end == if_then_else_handler)
                if_then_else = (struct if_then_else *)cur->at_end_data;
        if (!if_then_else)
-               die(_("format: %%(then) atom used without an %%(if) atom"));
+               return strbuf_addf_ret(err, -1, _("format: %%(then) atom used without an %%(if) atom"));
        if (if_then_else->then_atom_seen)
-               die(_("format: %%(then) atom used more than once"));
+               return strbuf_addf_ret(err, -1, _("format: %%(then) atom used more than once"));
        if (if_then_else->else_atom_seen)
-               die(_("format: %%(then) atom used after %%(else)"));
+               return strbuf_addf_ret(err, -1, _("format: %%(then) atom used after %%(else)"));
        if_then_else->then_atom_seen = 1;
        /*
         * If the 'equals' or 'notequals' attribute is used then
@@ -624,9 +739,11 @@ static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_st
        } else if (cur->output.len && !is_empty(cur->output.buf))
                if_then_else->condition_satisfied = 1;
        strbuf_reset(&cur->output);
+       return 0;
 }
 
-static void else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+static int else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+                            struct strbuf *err)
 {
        struct ref_formatting_stack *prev = state->stack;
        struct if_then_else *if_then_else = NULL;
@@ -634,24 +751,26 @@ static void else_atom_handler(struct atom_value *atomv, struct ref_formatting_st
        if (prev->at_end == if_then_else_handler)
                if_then_else = (struct if_then_else *)prev->at_end_data;
        if (!if_then_else)
-               die(_("format: %%(else) atom used without an %%(if) atom"));
+               return strbuf_addf_ret(err, -1, _("format: %%(else) atom used without an %%(if) atom"));
        if (!if_then_else->then_atom_seen)
-               die(_("format: %%(else) atom used without a %%(then) atom"));
+               return strbuf_addf_ret(err, -1, _("format: %%(else) atom used without a %%(then) atom"));
        if (if_then_else->else_atom_seen)
-               die(_("format: %%(else) atom used more than once"));
+               return strbuf_addf_ret(err, -1, _("format: %%(else) atom used more than once"));
        if_then_else->else_atom_seen = 1;
        push_stack_element(&state->stack);
        state->stack->at_end_data = prev->at_end_data;
        state->stack->at_end = prev->at_end;
+       return 0;
 }
 
-static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+static int end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state,
+                           struct strbuf *err)
 {
        struct ref_formatting_stack *current = state->stack;
        struct strbuf s = STRBUF_INIT;
 
        if (!current->at_end)
-               die(_("format: %%(end) atom used without corresponding atom"));
+               return strbuf_addf_ret(err, -1, _("format: %%(end) atom used without corresponding atom"));
        current->at_end(&state->stack);
 
        /*  Stack may have been popped within at_end(), hence reset the current pointer */
@@ -668,6 +787,7 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
        }
        strbuf_release(&s);
        pop_stack_element(&state->stack);
+       return 0;
 }
 
 /*
@@ -702,62 +822,48 @@ int verify_ref_format(struct ref_format *format)
 
        format->need_color_reset_at_eol = 0;
        for (cp = format->format; *cp && (sp = find_next(cp)); ) {
+               struct strbuf err = STRBUF_INIT;
                const char *color, *ep = strchr(sp, ')');
                int at;
 
                if (!ep)
                        return error(_("malformed format string %s"), sp);
                /* sp points at "%(" and ep points at the closing ")" */
-               at = parse_ref_filter_atom(format, sp + 2, ep);
+               at = parse_ref_filter_atom(format, sp + 2, ep, &err);
+               if (at < 0)
+                       die("%s", err.buf);
                cp = ep + 1;
 
                if (skip_prefix(used_atom[at].name, "color:", &color))
                        format->need_color_reset_at_eol = !!strcmp(color, "reset");
+               strbuf_release(&err);
        }
        if (format->need_color_reset_at_eol && !want_color(format->use_color))
                format->need_color_reset_at_eol = 0;
        return 0;
 }
 
-/*
- * Given an object name, read the object data and size, and return a
- * "struct object".  If the object data we are returning is also borrowed
- * by the "struct object" representation, set *eaten as well---it is a
- * signal from parse_object_buffer to us not to free the buffer.
- */
-static void *get_obj(const struct object_id *oid, struct object **obj, unsigned long *sz, int *eaten)
-{
-       enum object_type type;
-       void *buf = read_sha1_file(oid->hash, &type, sz);
-
-       if (buf)
-               *obj = parse_object_buffer(oid, type, *sz, buf, eaten);
-       else
-               *obj = NULL;
-       return buf;
-}
-
-static int grab_objectname(const char *name, const unsigned char *sha1,
+static int grab_objectname(const char *name, const struct object_id *oid,
                           struct atom_value *v, struct used_atom *atom)
 {
        if (starts_with(name, "objectname")) {
                if (atom->u.objectname.option == O_SHORT) {
-                       v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
+                       v->s = xstrdup(find_unique_abbrev(oid, DEFAULT_ABBREV));
                        return 1;
                } else if (atom->u.objectname.option == O_FULL) {
-                       v->s = xstrdup(sha1_to_hex(sha1));
+                       v->s = xstrdup(oid_to_hex(oid));
                        return 1;
                } else if (atom->u.objectname.option == O_LENGTH) {
-                       v->s = xstrdup(find_unique_abbrev(sha1, atom->u.objectname.length));
+                       v->s = xstrdup(find_unique_abbrev(oid, atom->u.objectname.length));
                        return 1;
                } else
-                       die("BUG: unknown %%(objectname) option");
+                       BUG("unknown %%(objectname) option");
        }
        return 0;
 }
 
 /* See grab_values */
-static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+static void grab_common_values(struct atom_value *val, int deref, struct expand_data *oi)
 {
        int i;
 
@@ -769,13 +875,13 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
                if (deref)
                        name++;
                if (!strcmp(name, "objecttype"))
-                       v->s = typename(obj->type);
+                       v->s = xstrdup(type_name(oi->type));
                else if (!strcmp(name, "objectsize")) {
-                       v->value = sz;
-                       v->s = xstrfmt("%lu", sz);
+                       v->value = oi->size;
+                       v->s = xstrfmt("%lu", oi->size);
                }
                else if (deref)
-                       grab_objectname(name, obj->oid.hash, v, &used_atom[i]);
+                       grab_objectname(name, &oi->oid, v, &used_atom[i]);
        }
 }
 
@@ -793,9 +899,9 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
                if (deref)
                        name++;
                if (!strcmp(name, "tag"))
-                       v->s = tag->tag;
+                       v->s = xstrdup(tag->tag);
                else if (!strcmp(name, "type") && tag->tagged)
-                       v->s = typename(tag->tagged->type);
+                       v->s = xstrdup(type_name(tag->tagged->type));
                else if (!strcmp(name, "object") && tag->tagged)
                        v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
        }
@@ -815,7 +921,7 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
                if (deref)
                        name++;
                if (!strcmp(name, "tree")) {
-                       v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
+                       v->s = xstrdup(oid_to_hex(get_commit_tree_oid(commit)));
                }
                else if (!strcmp(name, "numparent")) {
                        v->value = commit_list_count(commit->parents);
@@ -926,7 +1032,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
        v->value = timestamp;
        return;
  bad:
-       v->s = "";
+       v->s = xstrdup("");
        v->value = 0;
 }
 
@@ -1121,7 +1227,7 @@ static void fill_missing_values(struct atom_value *val)
        for (i = 0; i < used_atom_cnt; i++) {
                struct atom_value *v = &val[i];
                if (v->s == NULL)
-                       v->s = "";
+                       v->s = xstrdup("");
        }
 }
 
@@ -1134,7 +1240,6 @@ static void fill_missing_values(struct atom_value *val)
  */
 static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
 {
-       grab_common_values(val, deref, obj, buf, sz);
        switch (obj->type) {
        case OBJ_TAG:
                grab_tag_values(val, deref, obj, buf, sz);
@@ -1168,7 +1273,8 @@ static inline char *copy_advance(char *dst, const char *src)
 static const char *lstrip_ref_components(const char *refname, int len)
 {
        long remaining = len;
-       const char *start = refname;
+       const char *start = xstrdup(refname);
+       const char *to_free = start;
 
        if (len < 0) {
                int i;
@@ -1189,20 +1295,24 @@ static const char *lstrip_ref_components(const char *refname, int len)
        while (remaining > 0) {
                switch (*start++) {
                case '\0':
-                       return "";
+                       free((char *)to_free);
+                       return xstrdup("");
                case '/':
                        remaining--;
                        break;
                }
        }
 
+       start = xstrdup(start);
+       free((char *)to_free);
        return start;
 }
 
 static const char *rstrip_ref_components(const char *refname, int len)
 {
        long remaining = len;
-       char *start = xstrdup(refname);
+       const char *start = xstrdup(refname);
+       const char *to_free = start;
 
        if (len < 0) {
                int i;
@@ -1222,9 +1332,10 @@ static const char *rstrip_ref_components(const char *refname, int len)
 
        while (remaining-- > 0) {
                char *p = strrchr(start, '/');
-               if (p == NULL)
-                       return "";
-               else
+               if (p == NULL) {
+                       free((char *)to_free);
+                       return xstrdup("");
+               } else
                        p[0] = '\0';
        }
        return start;
@@ -1239,7 +1350,7 @@ static const char *show_ref(struct refname_atom *atom, const char *refname)
        else if (atom->option == R_RSTRIP)
                return rstrip_ref_components(refname, atom->rstrip);
        else
-               return refname;
+               return xstrdup(refname);
 }
 
 static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
@@ -1249,11 +1360,11 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
        if (atom->u.remote_ref.option == RR_REF)
                *s = show_ref(&atom->u.remote_ref.refname, refname);
        else if (atom->u.remote_ref.option == RR_TRACK) {
-               if (stat_tracking_info(branch, &num_ours,
-                                      &num_theirs, NULL)) {
+               if (stat_tracking_info(branch, &num_ours, &num_theirs,
+                                      NULL, AHEAD_BEHIND_FULL) < 0) {
                        *s = xstrdup(msgs.gone);
                } else if (!num_ours && !num_theirs)
-                       *s = "";
+                       *s = xstrdup("");
                else if (!num_ours)
                        *s = xstrfmt(msgs.behind, num_theirs);
                else if (!num_theirs)
@@ -1267,39 +1378,34 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                        free((void *)to_free);
                }
        } else if (atom->u.remote_ref.option == RR_TRACKSHORT) {
-               if (stat_tracking_info(branch, &num_ours,
-                                      &num_theirs, NULL))
+               if (stat_tracking_info(branch, &num_ours, &num_theirs,
+                                      NULL, AHEAD_BEHIND_FULL) < 0) {
+                       *s = xstrdup("");
                        return;
-
+               }
                if (!num_ours && !num_theirs)
-                       *s = "=";
+                       *s = xstrdup("=");
                else if (!num_ours)
-                       *s = "<";
+                       *s = xstrdup("<");
                else if (!num_theirs)
-                       *s = ">";
+                       *s = xstrdup(">");
                else
-                       *s = "<>";
+                       *s = xstrdup("<>");
        } else if (atom->u.remote_ref.option == RR_REMOTE_NAME) {
                int explicit;
                const char *remote = atom->u.remote_ref.push ?
                        pushremote_for_branch(branch, &explicit) :
                        remote_for_branch(branch, &explicit);
-               if (explicit)
-                       *s = xstrdup(remote);
-               else
-                       *s = "";
+               *s = xstrdup(explicit ? remote : "");
        } else if (atom->u.remote_ref.option == RR_REMOTE_REF) {
                int explicit;
                const char *merge;
 
                merge = remote_ref_for_branch(branch, atom->u.remote_ref.push,
                                              &explicit);
-               if (explicit)
-                       *s = xstrdup(merge);
-               else
-                       *s = "";
+               *s = xstrdup(explicit ? merge : "");
        } else
-               die("BUG: unhandled RR_* enum");
+               BUG("unhandled RR_* enum");
 }
 
 char *get_head_description(void)
@@ -1309,10 +1415,14 @@ char *get_head_description(void)
        memset(&state, 0, sizeof(state));
        wt_status_get_state(&state, 1);
        if (state.rebase_in_progress ||
-           state.rebase_interactive_in_progress)
-               strbuf_addf(&desc, _("(no branch, rebasing %s)"),
-                           state.branch);
-       else if (state.bisect_in_progress)
+           state.rebase_interactive_in_progress) {
+               if (state.branch)
+                       strbuf_addf(&desc, _("(no branch, rebasing %s)"),
+                                   state.branch);
+               else
+                       strbuf_addf(&desc, _("(no branch, rebasing detached HEAD %s)"),
+                                   state.detached_from);
+       } else if (state.bisect_in_progress)
                strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
                            state.branch);
        else if (state.detached_from) {
@@ -1342,7 +1452,7 @@ char *get_head_description(void)
 static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref)
 {
        if (!ref->symref)
-               return "";
+               return xstrdup("");
        else
                return show_ref(&atom->u.refname, ref->symref);
 }
@@ -1354,16 +1464,46 @@ static const char *get_refname(struct used_atom *atom, struct ref_array_item *re
        return show_ref(&atom->u.refname, ref->refname);
 }
 
+static int get_object(struct ref_array_item *ref, int deref, struct object **obj,
+                     struct expand_data *oi, struct strbuf *err)
+{
+       /* parse_object_buffer() will set eaten to 0 if free() will be needed */
+       int eaten = 1;
+       if (oi->info.contentp) {
+               /* We need to know that to use parse_object_buffer properly */
+               oi->info.sizep = &oi->size;
+               oi->info.typep = &oi->type;
+       }
+       if (oid_object_info_extended(the_repository, &oi->oid, &oi->info,
+                                    OBJECT_INFO_LOOKUP_REPLACE))
+               return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
+                                      oid_to_hex(&oi->oid), ref->refname);
+
+       if (oi->info.contentp) {
+               *obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten);
+               if (!obj) {
+                       if (!eaten)
+                               free(oi->content);
+                       return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
+                                              oid_to_hex(&oi->oid), ref->refname);
+               }
+               grab_values(ref->value, deref, *obj, oi->content, oi->size);
+       }
+
+       grab_common_values(ref->value, deref, oi);
+       if (!eaten)
+               free(oi->content);
+       return 0;
+}
+
 /*
  * Parse the object referred by ref, and grab needed value.
  */
-static void populate_value(struct ref_array_item *ref)
+static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 {
-       void *buf;
        struct object *obj;
-       int eaten, i;
-       unsigned long size;
-       const struct object_id *tagged;
+       int i;
+       struct object_info empty = OBJECT_INFO_INIT;
 
        ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));
 
@@ -1371,7 +1511,7 @@ static void populate_value(struct ref_array_item *ref)
                ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
                                             NULL, NULL);
                if (!ref->symref)
-                       ref->symref = "";
+                       ref->symref = xstrdup("");
        }
 
        /* Fill in specials first */
@@ -1399,16 +1539,21 @@ static void populate_value(struct ref_array_item *ref)
                        const char *branch_name;
                        /* only local branches may have an upstream */
                        if (!skip_prefix(ref->refname, "refs/heads/",
-                                        &branch_name))
+                                        &branch_name)) {
+                               v->s = xstrdup("");
                                continue;
+                       }
                        branch = branch_get(branch_name);
 
                        refname = branch_get_upstream(branch, NULL);
                        if (refname)
                                fill_remote_ref_details(atom, refname, branch, &v->s);
+                       else
+                               v->s = xstrdup("");
                        continue;
                } else if (atom->u.remote_ref.push) {
                        const char *branch_name;
+                       v->s = xstrdup("");
                        if (!skip_prefix(ref->refname, "refs/heads/",
                                         &branch_name))
                                continue;
@@ -1421,10 +1566,12 @@ static void populate_value(struct ref_array_item *ref)
                                if (!refname)
                                        continue;
                        }
+                       /* We will definitely re-init v->s on the next line. */
+                       free((char *)v->s);
                        fill_remote_ref_details(atom, refname, branch, &v->s);
                        continue;
                } else if (starts_with(name, "color:")) {
-                       v->s = atom->u.color;
+                       v->s = xstrdup(atom->u.color);
                        continue;
                } else if (!strcmp(name, "flag")) {
                        char buf[256], *cp = buf;
@@ -1433,80 +1580,84 @@ static void populate_value(struct ref_array_item *ref)
                        if (ref->flag & REF_ISPACKED)
                                cp = copy_advance(cp, ",packed");
                        if (cp == buf)
-                               v->s = "";
+                               v->s = xstrdup("");
                        else {
                                *cp = '\0';
                                v->s = xstrdup(buf + 1);
                        }
                        continue;
-               } else if (!deref && grab_objectname(name, ref->objectname.hash, v, atom)) {
+               } else if (!deref && grab_objectname(name, &ref->objectname, v, atom)) {
                        continue;
                } else if (!strcmp(name, "HEAD")) {
                        if (atom->u.head && !strcmp(ref->refname, atom->u.head))
-                               v->s = "*";
+                               v->s = xstrdup("*");
                        else
-                               v->s = " ";
+                               v->s = xstrdup(" ");
                        continue;
                } else if (starts_with(name, "align")) {
                        v->handler = align_atom_handler;
+                       v->s = xstrdup("");
                        continue;
                } else if (!strcmp(name, "end")) {
                        v->handler = end_atom_handler;
+                       v->s = xstrdup("");
                        continue;
                } else if (starts_with(name, "if")) {
                        const char *s;
-
                        if (skip_prefix(name, "if:", &s))
                                v->s = xstrdup(s);
+                       else
+                               v->s = xstrdup("");
                        v->handler = if_atom_handler;
                        continue;
                } else if (!strcmp(name, "then")) {
                        v->handler = then_atom_handler;
+                       v->s = xstrdup("");
                        continue;
                } else if (!strcmp(name, "else")) {
                        v->handler = else_atom_handler;
+                       v->s = xstrdup("");
                        continue;
                } else
                        continue;
 
                if (!deref)
-                       v->s = refname;
+                       v->s = xstrdup(refname);
                else
                        v->s = xstrfmt("%s^{}", refname);
+               free((char *)refname);
        }
 
        for (i = 0; i < used_atom_cnt; i++) {
                struct atom_value *v = &ref->value[i];
-               if (v->s == NULL)
-                       goto need_obj;
+               if (v->s == NULL && used_atom[i].source == SOURCE_NONE)
+                       return strbuf_addf_ret(err, -1, _("missing object %s for %s"),
+                                              oid_to_hex(&ref->objectname), ref->refname);
        }
-       return;
 
- need_obj:
-       buf = get_obj(&ref->objectname, &obj, &size, &eaten);
-       if (!buf)
-               die(_("missing object %s for %s"),
-                   oid_to_hex(&ref->objectname), ref->refname);
-       if (!obj)
-               die(_("parse_object_buffer failed on %s for %s"),
-                   oid_to_hex(&ref->objectname), ref->refname);
+       if (need_tagged)
+               oi.info.contentp = &oi.content;
+       if (!memcmp(&oi.info, &empty, sizeof(empty)) &&
+           !memcmp(&oi_deref.info, &empty, sizeof(empty)))
+               return 0;
 
-       grab_values(ref->value, 0, obj, buf, size);
-       if (!eaten)
-               free(buf);
+
+       oi.oid = ref->objectname;
+       if (get_object(ref, 0, &obj, &oi, err))
+               return -1;
 
        /*
         * If there is no atom that wants to know about tagged
         * object, we are done.
         */
        if (!need_tagged || (obj->type != OBJ_TAG))
-               return;
+               return 0;
 
        /*
         * If it is a tag object, see if we use a value that derefs
         * the object, and if we do grab the object it refers to.
         */
-       tagged = &((struct tag *)obj)->tagged->oid;
+       oi_deref.oid = ((struct tag *)obj)->tagged->oid;
 
        /*
         * NEEDSWORK: This derefs tag only once, which
@@ -1514,154 +1665,25 @@ static void populate_value(struct ref_array_item *ref)
         * is not consistent with what deref_tag() does
         * which peels the onion to the core.
         */
-       buf = get_obj(tagged, &obj, &size, &eaten);
-       if (!buf)
-               die(_("missing object %s for %s"),
-                   oid_to_hex(tagged), ref->refname);
-       if (!obj)
-               die(_("parse_object_buffer failed on %s for %s"),
-                   oid_to_hex(tagged), ref->refname);
-       grab_values(ref->value, 1, obj, buf, size);
-       if (!eaten)
-               free(buf);
+       return get_object(ref, 1, &obj, &oi_deref, err);
 }
 
 /*
  * Given a ref, return the value for the atom.  This lazily gets value
  * out of the object by calling populate value.
  */
-static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v)
+static int get_ref_atom_value(struct ref_array_item *ref, int atom,
+                             struct atom_value **v, struct strbuf *err)
 {
        if (!ref->value) {
-               populate_value(ref);
+               if (populate_value(ref, err))
+                       return -1;
                fill_missing_values(ref->value);
        }
        *v = &ref->value[atom];
-}
-
-/*
- * Unknown has to be "0" here, because that's the default value for
- * contains_cache slab entries that have not yet been assigned.
- */
-enum contains_result {
-       CONTAINS_UNKNOWN = 0,
-       CONTAINS_NO,
-       CONTAINS_YES
-};
-
-define_commit_slab(contains_cache, enum contains_result);
-
-struct ref_filter_cbdata {
-       struct ref_array *array;
-       struct ref_filter *filter;
-       struct contains_cache contains_cache;
-       struct contains_cache no_contains_cache;
-};
-
-/*
- * Mimicking the real stack, this stack lives on the heap, avoiding stack
- * overflows.
- *
- * At each recursion step, the stack items points to the commits whose
- * ancestors are to be inspected.
- */
-struct contains_stack {
-       int nr, alloc;
-       struct contains_stack_entry {
-               struct commit *commit;
-               struct commit_list *parents;
-       } *contains_stack;
-};
-
-static int in_commit_list(const struct commit_list *want, struct commit *c)
-{
-       for (; want; want = want->next)
-               if (!oidcmp(&want->item->object.oid, &c->object.oid))
-                       return 1;
        return 0;
 }
 
-/*
- * Test whether the candidate or one of its parents is contained in the list.
- * Do not recurse to find out, though, but return -1 if inconclusive.
- */
-static enum contains_result contains_test(struct commit *candidate,
-                                         const struct commit_list *want,
-                                         struct contains_cache *cache)
-{
-       enum contains_result *cached = contains_cache_at(cache, candidate);
-
-       /* If we already have the answer cached, return that. */
-       if (*cached)
-               return *cached;
-
-       /* or are we it? */
-       if (in_commit_list(want, candidate)) {
-               *cached = CONTAINS_YES;
-               return CONTAINS_YES;
-       }
-
-       /* Otherwise, we don't know; prepare to recurse */
-       parse_commit_or_die(candidate);
-       return CONTAINS_UNKNOWN;
-}
-
-static void push_to_contains_stack(struct commit *candidate, struct contains_stack *contains_stack)
-{
-       ALLOC_GROW(contains_stack->contains_stack, contains_stack->nr + 1, contains_stack->alloc);
-       contains_stack->contains_stack[contains_stack->nr].commit = candidate;
-       contains_stack->contains_stack[contains_stack->nr++].parents = candidate->parents;
-}
-
-static enum contains_result contains_tag_algo(struct commit *candidate,
-                                             const struct commit_list *want,
-                                             struct contains_cache *cache)
-{
-       struct contains_stack contains_stack = { 0, 0, NULL };
-       enum contains_result result = contains_test(candidate, want, cache);
-
-       if (result != CONTAINS_UNKNOWN)
-               return result;
-
-       push_to_contains_stack(candidate, &contains_stack);
-       while (contains_stack.nr) {
-               struct contains_stack_entry *entry = &contains_stack.contains_stack[contains_stack.nr - 1];
-               struct commit *commit = entry->commit;
-               struct commit_list *parents = entry->parents;
-
-               if (!parents) {
-                       *contains_cache_at(cache, commit) = CONTAINS_NO;
-                       contains_stack.nr--;
-               }
-               /*
-                * If we just popped the stack, parents->item has been marked,
-                * therefore contains_test will return a meaningful yes/no.
-                */
-               else switch (contains_test(parents->item, want, cache)) {
-               case CONTAINS_YES:
-                       *contains_cache_at(cache, commit) = CONTAINS_YES;
-                       contains_stack.nr--;
-                       break;
-               case CONTAINS_NO:
-                       entry->parents = parents->next;
-                       break;
-               case CONTAINS_UNKNOWN:
-                       push_to_contains_stack(parents->item, &contains_stack);
-                       break;
-               }
-       }
-       free(contains_stack.contains_stack);
-       return contains_test(candidate, want, cache);
-}
-
-static int commit_contains(struct ref_filter *filter, struct commit *commit,
-                          struct commit_list *list, struct contains_cache *cache)
-{
-       if (filter->with_commit_tag_algo)
-               return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
-       return is_descendant_of(commit, list);
-}
-
 /*
  * Return 1 if the refname matches one of the patterns, otherwise 0.
  * A pattern can be a literal prefix (e.g. a refname "refs/heads/master"
@@ -1717,7 +1739,7 @@ static int match_name_as_path(const struct ref_filter *filter, const char *refna
                     refname[plen] == '/' ||
                     p[plen-1] == '/'))
                        return 1;
-               if (!wildmatch(p, refname, WM_PATHNAME))
+               if (!wildmatch(p, refname, flags))
                        return 1;
        }
        return 0;
@@ -1772,6 +1794,15 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
                return for_each_fullref_in("", cb, cb_data, broken);
        }
 
+       if (filter->ignore_case) {
+               /*
+                * we can't handle case-insensitive comparisons,
+                * so just return everything and let the caller
+                * sort it out.
+                */
+               return for_each_fullref_in("", cb, cb_data, broken);
+       }
+
        if (!filter->name_patterns[0]) {
                /* no patterns; we have to look at everything */
                return for_each_fullref_in("", cb, cb_data, broken);
@@ -1817,7 +1848,7 @@ static const struct object_id *match_points_at(struct oid_array *points_at,
 
        if (oid_array_lookup(points_at, oid) >= 0)
                return oid;
-       obj = parse_object(oid);
+       obj = parse_object(the_repository, oid);
        if (!obj)
                die(_("malformed object at '%s'"), refname);
        if (obj->type == OBJ_TAG)
@@ -1827,15 +1858,30 @@ static const struct object_id *match_points_at(struct oid_array *points_at,
        return NULL;
 }
 
-/* Allocate space for a new ref_array_item and copy the objectname and flag to it */
+/*
+ * Allocate space for a new ref_array_item and copy the name and oid to it.
+ *
+ * Callers can then fill in other struct members at their leisure.
+ */
 static struct ref_array_item *new_ref_array_item(const char *refname,
-                                                const unsigned char *objectname,
-                                                int flag)
+                                                const struct object_id *oid)
 {
        struct ref_array_item *ref;
+
        FLEX_ALLOC_STR(ref, refname, refname);
-       hashcpy(ref->objectname.hash, objectname);
-       ref->flag = flag;
+       oidcpy(&ref->objectname, oid);
+
+       return ref;
+}
+
+struct ref_array_item *ref_array_push(struct ref_array *array,
+                                     const char *refname,
+                                     const struct object_id *oid)
+{
+       struct ref_array_item *ref = new_ref_array_item(refname, oid);
+
+       ALLOC_GROW(array->items, array->nr + 1, array->alloc);
+       array->items[array->nr++] = ref;
 
        return ref;
 }
@@ -1873,6 +1919,13 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
        return ref_kind_from_refname(refname);
 }
 
+struct ref_filter_cbdata {
+       struct ref_array *array;
+       struct ref_filter *filter;
+       struct contains_cache contains_cache;
+       struct contains_cache no_contains_cache;
+};
+
 /*
  * A call-back given to for_each_ref().  Filter refs and keep them for
  * later object processing.
@@ -1912,7 +1965,8 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
         * non-commits early. The actual filtering is done later.
         */
        if (filter->merge_commit || filter->with_commit || filter->no_commit || filter->verbose) {
-               commit = lookup_commit_reference_gently(oid, 1);
+               commit = lookup_commit_reference_gently(the_repository, oid,
+                                                       1);
                if (!commit)
                        return 0;
                /* We perform the filtering for the '--contains' option... */
@@ -1930,12 +1984,11 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
         * to do its job and the resulting list may yet to be pruned
         * by maxcount logic.
         */
-       ref = new_ref_array_item(refname, oid->hash, flag);
+       ref = ref_array_push(ref_cbdata->array, refname, oid);
        ref->commit = commit;
-
-       REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1);
-       ref_cbdata->array->items[ref_cbdata->array->nr++] = ref;
+       ref->flag = flag;
        ref->kind = kind;
+
        return 0;
 }
 
@@ -1943,6 +1996,10 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 static void free_array_item(struct ref_array_item *item)
 {
        free((char *)item->symref);
+       if (item->value) {
+               free((char *)item->value->s);
+               free(item->value);
+       }
        free(item);
 }
 
@@ -1951,6 +2008,10 @@ void ref_array_clear(struct ref_array *array)
 {
        int i;
 
+       for (i = 0; i < used_atom_cnt; i++)
+               free((char *)used_atom[i].name);
+       FREE_AND_NULL(used_atom);
+       used_atom_cnt = 0;
        for (i = 0; i < array->nr; i++)
                free_array_item(array->items[i]);
        FREE_AND_NULL(array->items);
@@ -1965,7 +2026,7 @@ static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
        struct ref_array *array = ref_cbdata->array;
        struct commit **to_clear = xcalloc(sizeof(struct commit *), array->nr);
 
-       init_revisions(&revs, NULL);
+       repo_init_revisions(the_repository, &revs, NULL);
 
        for (i = 0; i < array->nr; i++) {
                struct ref_array_item *item = array->items[i];
@@ -2060,9 +2121,13 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
        int cmp;
        cmp_type cmp_type = used_atom[s->atom].type;
        int (*cmp_fn)(const char *, const char *);
+       struct strbuf err = STRBUF_INIT;
 
-       get_ref_atom_value(a, s->atom, &va);
-       get_ref_atom_value(b, s->atom, &vb);
+       if (get_ref_atom_value(a, s->atom, &va, &err))
+               die("%s", err.buf);
+       if (get_ref_atom_value(b, s->atom, &vb, &err))
+               die("%s", err.buf);
+       strbuf_release(&err);
        cmp_fn = s->ignore_case ? strcasecmp : strcmp;
        if (s->version)
                cmp = versioncmp(va->s, vb->s);
@@ -2121,9 +2186,10 @@ static void append_literal(const char *cp, const char *ep, struct ref_formatting
        }
 }
 
-void format_ref_array_item(struct ref_array_item *info,
+int format_ref_array_item(struct ref_array_item *info,
                           const struct ref_format *format,
-                          struct strbuf *final_buf)
+                          struct strbuf *final_buf,
+                          struct strbuf *error_buf)
 {
        const char *cp, *sp, *ep;
        struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
@@ -2133,14 +2199,17 @@ void format_ref_array_item(struct ref_array_item *info,
 
        for (cp = format->format; *cp && (sp = find_next(cp)); cp = ep + 1) {
                struct atom_value *atomv;
+               int pos;
 
                ep = strchr(sp, ')');
                if (cp < sp)
                        append_literal(cp, sp, &state);
-               get_ref_atom_value(info,
-                                  parse_ref_filter_atom(format, sp + 2, ep),
-                                  &atomv);
-               atomv->handler(atomv, &state);
+               pos = parse_ref_filter_atom(format, sp + 2, ep, error_buf);
+               if (pos < 0 || get_ref_atom_value(info, pos, &atomv, error_buf) ||
+                   atomv->handler(atomv, &state, error_buf)) {
+                       pop_stack_element(&state.stack);
+                       return -1;
+               }
        }
        if (*cp) {
                sp = cp + strlen(cp);
@@ -2149,30 +2218,39 @@ void format_ref_array_item(struct ref_array_item *info,
        if (format->need_color_reset_at_eol) {
                struct atom_value resetv;
                resetv.s = GIT_COLOR_RESET;
-               append_atom(&resetv, &state);
+               if (append_atom(&resetv, &state, error_buf)) {
+                       pop_stack_element(&state.stack);
+                       return -1;
+               }
+       }
+       if (state.stack->prev) {
+               pop_stack_element(&state.stack);
+               return strbuf_addf_ret(error_buf, -1, _("format: %%(end) atom missing"));
        }
-       if (state.stack->prev)
-               die(_("format: %%(end) atom missing"));
        strbuf_addbuf(final_buf, &state.stack->output);
        pop_stack_element(&state.stack);
+       return 0;
 }
 
 void show_ref_array_item(struct ref_array_item *info,
                         const struct ref_format *format)
 {
        struct strbuf final_buf = STRBUF_INIT;
+       struct strbuf error_buf = STRBUF_INIT;
 
-       format_ref_array_item(info, format, &final_buf);
+       if (format_ref_array_item(info, format, &final_buf, &error_buf))
+               die("%s", error_buf.buf);
        fwrite(final_buf.buf, 1, final_buf.len, stdout);
+       strbuf_release(&error_buf);
        strbuf_release(&final_buf);
        putchar('\n');
 }
 
-void pretty_print_ref(const char *name, const unsigned char *sha1,
+void pretty_print_ref(const char *name, const struct object_id *oid,
                      const struct ref_format *format)
 {
        struct ref_array_item *ref_item;
-       ref_item = new_ref_array_item(name, sha1, 0);
+       ref_item = new_ref_array_item(name, oid);
        ref_item->kind = ref_kind_from_refname(name);
        show_ref_array_item(ref_item, format);
        free_array_item(ref_item);
@@ -2186,7 +2264,12 @@ static int parse_sorting_atom(const char *atom)
         */
        struct ref_format dummy = REF_FORMAT_INIT;
        const char *end = atom + strlen(atom);
-       return parse_ref_filter_atom(&dummy, atom, end);
+       struct strbuf err = STRBUF_INIT;
+       int res = parse_ref_filter_atom(&dummy, atom, end, &err);
+       if (res < 0)
+               die("%s", err.buf);
+       strbuf_release(&err);
+       return res;
 }
 
 /*  If no sorting option is given, use refname to sort as default */
@@ -2248,7 +2331,8 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
        if (get_oid(arg, &oid))
                die(_("malformed object name %s"), arg);
 
-       rf->merge_commit = lookup_commit_reference_gently(&oid, 0);
+       rf->merge_commit = lookup_commit_reference_gently(the_repository,
+                                                         &oid, 0);
        if (!rf->merge_commit)
                return opterror(opt, "must point to a commit", 0);