revision.c: reduce implicit dependency the_repository
[gitweb.git] / ref-filter.c
index 39e2744c949bb7c4ffbcb7c7e08d3757a6b732de..f081a290cb2e246d7774f2cbb266fcd4781280c6 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,7 @@
 #include "trailer.h"
 #include "wt-status.h"
 #include "commit-slab.h"
+#include "commit-graph.h"
 
 static struct ref_msg {
        const char *gone;
@@ -40,6 +43,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 +63,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 +87,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;
@@ -199,6 +215,30 @@ static int remote_ref_atom_parser(const struct ref_format *format, struct used_a
        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 int objectsize_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, _("%%(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)
 {
@@ -379,49 +419,50 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a
 
 static struct {
        const char *name;
+       info_source source;
        cmp_type cmp_type;
        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 }
@@ -497,6 +538,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) {
@@ -792,24 +840,6 @@ int verify_ref_format(struct ref_format *format)
        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_object_file(oid, &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 struct object_id *oid,
                           struct atom_value *v, struct used_atom *atom)
 {
@@ -824,13 +854,13 @@ static int grab_objectname(const char *name, const struct object_id *oid,
                        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;
 
@@ -842,13 +872,13 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
                if (deref)
                        name++;
                if (!strcmp(name, "objecttype"))
-                       v->s = type_name(obj->type);
+                       v->s = 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, v, &used_atom[i]);
+                       grab_objectname(name, &oi->oid, v, &used_atom[i]);
        }
 }
 
@@ -888,7 +918,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);
@@ -1207,7 +1237,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);
@@ -1372,7 +1401,7 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                else
                        *s = "";
        } else
-               die("BUG: unhandled RR_* enum");
+               BUG("unhandled RR_* enum");
 }
 
 char *get_head_description(void)
@@ -1431,24 +1460,36 @@ 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, const struct object_id *oid,
-                      int deref, struct object **obj, struct strbuf *err)
+static int get_object(struct ref_array_item *ref, int deref, struct object **obj,
+                     struct expand_data *oi, struct strbuf *err)
 {
-       int eaten;
-       int ret = 0;
-       unsigned long size;
-       void *buf = get_obj(oid, obj, &size, &eaten);
-       if (!buf)
-               ret = strbuf_addf_ret(err, -1, _("missing object %s for %s"),
-                                     oid_to_hex(oid), ref->refname);
-       else if (!*obj)
-               ret = strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
-                                     oid_to_hex(oid), ref->refname);
-       else
-               grab_values(ref->value, deref, *obj, buf, size);
+       /* 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(buf);
-       return ret;
+               free(oi->content);
+       return 0;
 }
 
 /*
@@ -1458,7 +1499,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 {
        struct object *obj;
        int i;
-       const struct object_id *tagged;
+       struct object_info empty = OBJECT_INFO_INIT;
 
        ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));
 
@@ -1492,6 +1533,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        refname = get_symref(atom, ref);
                else if (starts_with(name, "upstream")) {
                        const char *branch_name;
+                       v->s = "";
                        /* only local branches may have an upstream */
                        if (!skip_prefix(ref->refname, "refs/heads/",
                                         &branch_name))
@@ -1504,6 +1546,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        continue;
                } else if (atom->u.remote_ref.push) {
                        const char *branch_name;
+                       v->s = "";
                        if (!skip_prefix(ref->refname, "refs/heads/",
                                         &branch_name))
                                continue;
@@ -1544,22 +1587,26 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        continue;
                } else if (starts_with(name, "align")) {
                        v->handler = align_atom_handler;
+                       v->s = "";
                        continue;
                } else if (!strcmp(name, "end")) {
                        v->handler = end_atom_handler;
+                       v->s = "";
                        continue;
                } else if (starts_with(name, "if")) {
                        const char *s;
-
+                       v->s = "";
                        if (skip_prefix(name, "if:", &s))
                                v->s = xstrdup(s);
                        v->handler = if_atom_handler;
                        continue;
                } else if (!strcmp(name, "then")) {
                        v->handler = then_atom_handler;
+                       v->s = "";
                        continue;
                } else if (!strcmp(name, "else")) {
                        v->handler = else_atom_handler;
+                       v->s = "";
                        continue;
                } else
                        continue;
@@ -1572,13 +1619,20 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
        for (i = 0; i < used_atom_cnt; i++) {
                struct atom_value *v = &ref->value[i];
-               if (v->s == NULL)
-                       break;
+               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);
        }
-       if (used_atom_cnt <= i)
+
+       if (need_tagged)
+               oi.info.contentp = &oi.content;
+       if (!memcmp(&oi.info, &empty, sizeof(empty)) &&
+           !memcmp(&oi_deref.info, &empty, sizeof(empty)))
                return 0;
 
-       if (get_object(ref, &ref->objectname, 0, &obj, err))
+
+       oi.oid = ref->objectname;
+       if (get_object(ref, 0, &obj, &oi, err))
                return -1;
 
        /*
@@ -1592,7 +1646,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
         * 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
@@ -1600,7 +1654,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
         * is not consistent with what deref_tag() does
         * which peels the onion to the core.
         */
-       return get_object(ref, tagged, 1, &obj, err);
+       return get_object(ref, 1, &obj, &oi_deref, err);
 }
 
 /*
@@ -1662,12 +1716,13 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
 }
 
 /*
- * Test whether the candidate or one of its parents is contained in the list.
+ * Test whether the candidate 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)
+                                         struct contains_cache *cache,
+                                         uint32_t cutoff)
 {
        enum contains_result *cached = contains_cache_at(cache, candidate);
 
@@ -1683,6 +1738,10 @@ static enum contains_result contains_test(struct commit *candidate,
 
        /* Otherwise, we don't know; prepare to recurse */
        parse_commit_or_die(candidate);
+
+       if (candidate->generation < cutoff)
+               return CONTAINS_NO;
+
        return CONTAINS_UNKNOWN;
 }
 
@@ -1698,8 +1757,18 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                                              struct contains_cache *cache)
 {
        struct contains_stack contains_stack = { 0, 0, NULL };
-       enum contains_result result = contains_test(candidate, want, cache);
+       enum contains_result result;
+       uint32_t cutoff = GENERATION_NUMBER_INFINITY;
+       const struct commit_list *p;
+
+       for (p = want; p; p = p->next) {
+               struct commit *c = p->item;
+               load_commit_graph_info(the_repository, c);
+               if (c->generation < cutoff)
+                       cutoff = c->generation;
+       }
 
+       result = contains_test(candidate, want, cache, cutoff);
        if (result != CONTAINS_UNKNOWN)
                return result;
 
@@ -1717,7 +1786,7 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                 * 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)) {
+               else switch (contains_test(parents->item, want, cache, cutoff)) {
                case CONTAINS_YES:
                        *contains_cache_at(cache, commit) = CONTAINS_YES;
                        contains_stack.nr--;
@@ -1731,7 +1800,7 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                }
        }
        free(contains_stack.contains_stack);
-       return contains_test(candidate, want, cache);
+       return contains_test(candidate, want, cache, cutoff);
 }
 
 static int commit_contains(struct ref_filter *filter, struct commit *commit,
@@ -1797,7 +1866,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;
@@ -1852,6 +1921,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);
@@ -1897,7 +1975,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)
@@ -2007,7 +2085,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... */
@@ -2059,7 +2138,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];
@@ -2364,7 +2443,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);