Merge branch 'kn/ref-filter-branch-list' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 5 Jun 2017 00:03:13 +0000 (09:03 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 5 Jun 2017 00:03:13 +0000 (09:03 +0900)
"git for-each-ref --format=..." with %(HEAD) in the format used to
resolve the HEAD symref as many times as it had processed refs,
which was wasteful, and "git branch" shared the same problem.

* kn/ref-filter-branch-list:
ref-filter: resolve HEAD when parsing %(HEAD) atom

1  2 
ref-filter.c
diff --combined ref-filter.c
index 563284175380cbd8cb3c503390b52781b16cf739,f4c68237275d49af3f1665f40788bc8287e3f879..2cc7b01277ca8565302eb1648c065ecbb1a25f47
@@@ -15,7 -15,6 +15,7 @@@
  #include "version.h"
  #include "trailer.h"
  #include "wt-status.h"
 +#include "commit-slab.h"
  
  static struct ref_msg {
        const char *gone;
@@@ -93,6 -92,7 +93,7 @@@ static struct used_atom 
                        unsigned int length;
                } objectname;
                struct refname_atom refname;
+               char *head;
        } u;
  } *used_atom;
  static int used_atom_cnt, need_tagged, need_symref;
@@@ -287,6 -287,12 +288,12 @@@ static void if_atom_parser(struct used_
        }
  }
  
+ static void head_atom_parser(struct used_atom *atom, const char *arg)
+ {
+       unsigned char unused[GIT_SHA1_RAWSZ];
+       atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, unused, NULL);
+ }
  
  static struct {
        const char *name;
        { "push", FIELD_STR, remote_ref_atom_parser },
        { "symref", FIELD_STR, refname_atom_parser },
        { "flag" },
-       { "HEAD" },
+       { "HEAD", FIELD_STR, head_atom_parser },
        { "color", FIELD_STR, color_atom_parser },
        { "align", FIELD_STR, align_atom_parser },
        { "end" },
@@@ -1250,18 -1256,12 +1257,18 @@@ char *get_head_description(void
                strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
                            state.branch);
        else if (state.detached_from) {
 -              /* TRANSLATORS: make sure these match _("HEAD detached at ")
 -                 and _("HEAD detached from ") in wt-status.c */
                if (state.detached_at)
 +                      /*
 +                       * TRANSLATORS: make sure this matches "HEAD
 +                       * detached at " in wt-status.c
 +                       */
                        strbuf_addf(&desc, _("(HEAD detached at %s)"),
                                state.detached_from);
                else
 +                      /*
 +                       * TRANSLATORS: make sure this matches "HEAD
 +                       * detached from " in wt-status.c
 +                       */
                        strbuf_addf(&desc, _("(HEAD detached from %s)"),
                                state.detached_from);
        }
@@@ -1302,9 -1302,9 +1309,9 @@@ static void populate_value(struct ref_a
        ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));
  
        if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
 -              unsigned char unused1[20];
 +              struct object_id unused1;
                ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
 -                                           unused1, NULL);
 +                                           unused1.hash, NULL);
                if (!ref->symref)
                        ref->symref = "";
        }
                } else if (!deref && grab_objectname(name, ref->objectname, v, atom)) {
                        continue;
                } else if (!strcmp(name, "HEAD")) {
-                       const char *head;
-                       unsigned char sha1[20];
-                       head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-                                                 sha1, NULL);
-                       if (head && !strcmp(ref->refname, head))
+                       if (atom->u.head && !strcmp(ref->refname, atom->u.head))
                                v->s = "*";
                        else
                                v->s = " ";
@@@ -1475,23 -1470,10 +1477,23 @@@ static void get_ref_atom_value(struct r
        *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 = -1,
 -      CONTAINS_NO = 0,
 -      CONTAINS_YES = 1
 +      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;
  };
  
  /*
@@@ -1522,24 -1504,24 +1524,24 @@@ static int in_commit_list(const struct 
   * 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)
 +                                        const struct commit_list *want,
 +                                        struct contains_cache *cache)
  {
 -      /* was it previously marked as containing a want commit? */
 -      if (candidate->object.flags & TMP_MARK)
 -              return 1;
 -      /* or marked as not possibly containing a want commit? */
 -      if (candidate->object.flags & UNINTERESTING)
 -              return 0;
 +      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)) {
 -              candidate->object.flags |= TMP_MARK;
 -              return 1;
 +              *cached = CONTAINS_YES;
 +              return CONTAINS_YES;
        }
  
 -      if (parse_commit(candidate) < 0)
 -              return 0;
 -
 -      return -1;
 +      /* 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)
  }
  
  static enum contains_result contains_tag_algo(struct commit *candidate,
 -              const struct commit_list *want)
 +                                            const struct commit_list *want,
 +                                            struct contains_cache *cache)
  {
        struct contains_stack contains_stack = { 0, 0, NULL };
 -      int result = contains_test(candidate, want);
 +      enum contains_result result = contains_test(candidate, want, cache);
  
        if (result != CONTAINS_UNKNOWN)
                return result;
                struct commit_list *parents = entry->parents;
  
                if (!parents) {
 -                      commit->object.flags |= UNINTERESTING;
 +                      *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 0 or 1.
 +               * therefore contains_test will return a meaningful yes/no.
                 */
 -              else switch (contains_test(parents->item, want)) {
 +              else switch (contains_test(parents->item, want, cache)) {
                case CONTAINS_YES:
 -                      commit->object.flags |= TMP_MARK;
 +                      *contains_cache_at(cache, commit) = CONTAINS_YES;
                        contains_stack.nr--;
                        break;
                case CONTAINS_NO:
                }
        }
        free(contains_stack.contains_stack);
 -      return contains_test(candidate, want);
 +      return contains_test(candidate, want, cache);
  }
  
 -static int commit_contains(struct ref_filter *filter, struct commit *commit)
 +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, filter->with_commit);
 -      return is_descendant_of(commit, filter->with_commit);
 +              return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
 +      return is_descendant_of(commit, list);
  }
  
  /*
@@@ -1682,22 -1662,22 +1684,22 @@@ static int filter_pattern_match(struct 
   * the need to parse the object via parse_object(). peel_ref() might be a
   * more efficient alternative to obtain the pointee.
   */
 -static const unsigned char *match_points_at(struct sha1_array *points_at,
 -                                          const unsigned char *sha1,
 -                                          const char *refname)
 +static const struct object_id *match_points_at(struct oid_array *points_at,
 +                                             const struct object_id *oid,
 +                                             const char *refname)
  {
 -      const unsigned char *tagged_sha1 = NULL;
 +      const struct object_id *tagged_oid = NULL;
        struct object *obj;
  
 -      if (sha1_array_lookup(points_at, sha1) >= 0)
 -              return sha1;
 -      obj = parse_object(sha1);
 +      if (oid_array_lookup(points_at, oid) >= 0)
 +              return oid;
 +      obj = parse_object(oid->hash);
        if (!obj)
                die(_("malformed object at '%s'"), refname);
        if (obj->type == OBJ_TAG)
 -              tagged_sha1 = ((struct tag *)obj)->tagged->oid.hash;
 -      if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)
 -              return tagged_sha1;
 +              tagged_oid = &((struct tag *)obj)->tagged->oid;
 +      if (tagged_oid && oid_array_lookup(points_at, tagged_oid) >= 0)
 +              return tagged_oid;
        return NULL;
  }
  
@@@ -1714,7 -1694,7 +1716,7 @@@ static struct ref_array_item *new_ref_a
        return ref;
  }
  
 -static int filter_ref_kind(struct ref_filter *filter, const char *refname)
 +static int ref_kind_from_refname(const char *refname)
  {
        unsigned int i;
  
                { "refs/tags/", 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"))
 +      if (!strcmp(refname, "HEAD"))
                return FILTER_REFS_DETACHED_HEAD;
  
        for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
        return FILTER_REFS_OTHERS;
  }
  
 +static int filter_ref_kind(struct ref_filter *filter, const char *refname)
 +{
 +      if (filter->kind == FILTER_REFS_BRANCHES ||
 +          filter->kind == FILTER_REFS_REMOTES ||
 +          filter->kind == FILTER_REFS_TAGS)
 +              return filter->kind;
 +      return ref_kind_from_refname(refname);
 +}
 +
  /*
   * A call-back given to for_each_ref().  Filter refs and keep them for
   * later object processing.
@@@ -1777,7 -1752,7 +1779,7 @@@ static int ref_filter_handler(const cha
        if (!filter_pattern_match(filter, refname))
                return 0;
  
 -      if (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname))
 +      if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
                return 0;
  
        /*
         * obtain the commit using the 'oid' available and discard all
         * non-commits early. The actual filtering is done later.
         */
 -      if (filter->merge_commit || filter->with_commit || filter->verbose) {
 +      if (filter->merge_commit || filter->with_commit || filter->no_commit || filter->verbose) {
                commit = lookup_commit_reference_gently(oid->hash, 1);
                if (!commit)
                        return 0;
 -              /* We perform the filtering for the '--contains' option */
 +              /* We perform the filtering for the '--contains' option... */
                if (filter->with_commit &&
 -                  !commit_contains(filter, commit))
 +                  !commit_contains(filter, commit, filter->with_commit, &ref_cbdata->contains_cache))
 +                      return 0;
 +              /* ...or for the `--no-contains' option */
 +              if (filter->no_commit &&
 +                  commit_contains(filter, commit, filter->no_commit, &ref_cbdata->no_contains_cache))
                        return 0;
        }
  
@@@ -1895,9 -1866,6 +1897,9 @@@ int filter_refs(struct ref_array *array
                broken = 1;
        filter->kind = type & FILTER_REFS_KIND_MASK;
  
 +      init_contains_cache(&ref_cbdata.contains_cache);
 +      init_contains_cache(&ref_cbdata.no_contains_cache);
 +
        /*  Simple per-ref filtering */
        if (!filter->kind)
                die("filter_refs: invalid type");
                        head_ref(ref_filter_handler, &ref_cbdata);
        }
  
 +      clear_contains_cache(&ref_cbdata.contains_cache);
 +      clear_contains_cache(&ref_cbdata.no_contains_cache);
  
        /*  Filters that need revision walking */
        if (filter->merge_commit)
@@@ -1956,7 -1922,8 +1958,7 @@@ static int cmp_ref_sorting(struct ref_s
        return (s->reverse) ? -cmp : cmp;
  }
  
 -static struct ref_sorting *ref_sorting;
 -static int compare_refs(const void *a_, const void *b_)
 +static int compare_refs(const void *a_, const void *b_, void *ref_sorting)
  {
        struct ref_array_item *a = *((struct ref_array_item **)a_);
        struct ref_array_item *b = *((struct ref_array_item **)b_);
  
  void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
  {
 -      ref_sorting = sorting;
 -      QSORT(array->items, array->nr, compare_refs);
 +      QSORT_S(array->items, array->nr, compare_refs, sorting);
  }
  
  static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state)
@@@ -2044,16 -2012,6 +2046,16 @@@ void show_ref_array_item(struct ref_arr
        putchar('\n');
  }
  
 +void pretty_print_ref(const char *name, const unsigned char *sha1,
 +              const char *format)
 +{
 +      struct ref_array_item *ref_item;
 +      ref_item = new_ref_array_item(name, sha1, 0);
 +      ref_item->kind = ref_kind_from_refname(name);
 +      show_ref_array_item(ref_item, format, 0);
 +      free_array_item(ref_item);
 +}
 +
  /*  If no sorting option is given, use refname to sort as default */
  struct ref_sorting *ref_default_sorting(void)
  {
@@@ -2095,17 -2053,8 +2097,17 @@@ int parse_opt_merge_filter(const struc
  {
        struct ref_filter *rf = opt->value;
        unsigned char sha1[20];
 +      int no_merged = starts_with(opt->long_name, "no");
 +
 +      if (rf->merge) {
 +              if (no_merged) {
 +                      return opterror(opt, "is incompatible with --merged", 0);
 +              } else {
 +                      return opterror(opt, "is incompatible with --no-merged", 0);
 +              }
 +      }
  
 -      rf->merge = starts_with(opt->long_name, "no")
 +      rf->merge = no_merged
                ? REF_FILTER_MERGED_OMIT
                : REF_FILTER_MERGED_INCLUDE;