refs.c: kill register_ref_store(), add register_submodule_ref_store()
[gitweb.git] / ref-filter.c
index 2840ca18e3f1c81b14bf5bc5db08d3efa913a89e..9c82b5b9d632bbbe66332b6c54badb5bc88d28d8 100644 (file)
 #include "version.h"
 #include "trailer.h"
 #include "wt-status.h"
+#include "commit-slab.h"
+
+static struct ref_msg {
+       const char *gone;
+       const char *ahead;
+       const char *behind;
+       const char *ahead_behind;
+} msgs = {
+        /* Untranslated plumbing messages: */
+       "gone",
+       "ahead %d",
+       "behind %d",
+       "ahead %d, behind %d"
+};
+
+void setup_ref_filter_porcelain_msg(void)
+{
+       msgs.gone = _("gone");
+       msgs.ahead = _("ahead %d");
+       msgs.behind = _("behind %d");
+       msgs.ahead_behind = _("ahead %d, behind %d");
+}
 
 typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
 typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;
@@ -33,8 +55,8 @@ struct if_then_else {
 };
 
 struct refname_atom {
-       enum { R_NORMAL, R_SHORT, R_LSTRIP } option;
-       int lstrip;
+       enum { R_NORMAL, R_SHORT, R_LSTRIP, R_RSTRIP } option;
+       int lstrip, rstrip;
 };
 
 /*
@@ -91,10 +113,15 @@ static void refname_atom_parser_internal(struct refname_atom *atom,
                atom->option = R_NORMAL;
        else if (!strcmp(arg, "short"))
                atom->option = R_SHORT;
-       else if (skip_prefix(arg, "lstrip=", &arg)) {
+       else if (skip_prefix(arg, "lstrip=", &arg) ||
+                skip_prefix(arg, "strip=", &arg)) {
                atom->option = R_LSTRIP;
                if (strtol_i(arg, 10, &atom->lstrip))
                        die(_("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);
        } else
                die(_("unrecognized %%(%s) argument: %s"), name, arg);
 }
@@ -1125,12 +1152,45 @@ static const char *lstrip_ref_components(const char *refname, int len)
        return start;
 }
 
+static const char *rstrip_ref_components(const char *refname, int len)
+{
+       long remaining = len;
+       char *start = xstrdup(refname);
+
+       if (len < 0) {
+               int i;
+               const char *p = refname;
+
+               /* Find total no of '/' separated path-components */
+               for (i = 0; p[i]; p[i] == '/' ? i++ : *p++)
+                       ;
+               /*
+                * The number of components we need to strip is now
+                * the total minus the components to be left (Plus one
+                * because we count the number of '/', but the number
+                * of components is one more than the no of '/').
+                */
+               remaining = i + len + 1;
+       }
+
+       while (remaining-- > 0) {
+               char *p = strrchr(start, '/');
+               if (p == NULL)
+                       return "";
+               else
+                       p[0] = '\0';
+       }
+       return start;
+}
+
 static const char *show_ref(struct refname_atom *atom, const char *refname)
 {
        if (atom->option == R_SHORT)
                return shorten_unambiguous_ref(refname, warn_ambiguous_refs);
        else if (atom->option == R_LSTRIP)
                return lstrip_ref_components(refname, atom->lstrip);
+       else if (atom->option == R_RSTRIP)
+               return rstrip_ref_components(refname, atom->rstrip);
        else
                return refname;
 }
@@ -1144,15 +1204,15 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
        else if (atom->u.remote_ref.option == RR_TRACK) {
                if (stat_tracking_info(branch, &num_ours,
                                       &num_theirs, NULL)) {
-                       *s = xstrdup("gone");
+                       *s = xstrdup(msgs.gone);
                } else if (!num_ours && !num_theirs)
                        *s = "";
                else if (!num_ours)
-                       *s = xstrfmt("behind %d", num_theirs);
+                       *s = xstrfmt(msgs.behind, num_theirs);
                else if (!num_theirs)
-                       *s = xstrfmt("ahead %d", num_ours);
+                       *s = xstrfmt(msgs.ahead, num_ours);
                else
-                       *s = xstrfmt("ahead %d, behind %d",
+                       *s = xstrfmt(msgs.ahead_behind,
                                     num_ours, num_theirs);
                if (!atom->u.remote_ref.nobracket && *s[0]) {
                        const char *to_free = *s;
@@ -1190,12 +1250,14 @@ 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);
        }
@@ -1236,9 +1298,9 @@ static void populate_value(struct ref_array_item *ref)
        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 = "";
        }
@@ -1409,10 +1471,22 @@ static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom
        *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;
 };
 
 /*
@@ -1443,24 +1517,24 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
  * 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)
@@ -1471,10 +1545,11 @@ static void push_to_contains_stack(struct commit *candidate, struct contains_sta
 }
 
 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;
@@ -1486,16 +1561,16 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                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:
@@ -1507,13 +1582,14 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                }
        }
        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 contains_cache *cache)
 {
        if (filter->with_commit_tag_algo)
-               return contains_tag_algo(commit, filter->with_commit);
+               return contains_tag_algo(commit, filter->with_commit, cache) == CONTAINS_YES;
        return is_descendant_of(commit, filter->with_commit);
 }
 
@@ -1633,7 +1709,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
        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;
 
@@ -1646,11 +1722,7 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
                { "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++) {
@@ -1661,6 +1733,15 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
        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.
@@ -1705,7 +1786,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
                        return 0;
                /* We perform the filtering for the '--contains' option */
                if (filter->with_commit &&
-                   !commit_contains(filter, commit))
+                   !commit_contains(filter, commit, &ref_cbdata->contains_cache))
                        return 0;
        }
 
@@ -1805,6 +1886,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
                broken = 1;
        filter->kind = type & FILTER_REFS_KIND_MASK;
 
+       init_contains_cache(&ref_cbdata.contains_cache);
+
        /*  Simple per-ref filtering */
        if (!filter->kind)
                die("filter_refs: invalid type");
@@ -1827,6 +1910,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
                        head_ref(ref_filter_handler, &ref_cbdata);
        }
 
+       clear_contains_cache(&ref_cbdata.contains_cache);
 
        /*  Filters that need revision walking */
        if (filter->merge_commit)
@@ -1861,8 +1945,7 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
        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_);
@@ -1878,8 +1961,7 @@ static int compare_refs(const void *a_, const void *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)
@@ -1951,6 +2033,16 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
        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)
 {