for-each-ref: rename some functions and make them public
[gitweb.git] / builtin / for-each-ref.c
index 008513c2f135b5a77d1642ff88e272a7e2f9f9d5..5700b25d51f070ec35b2d40b3707c5a907bd3276 100644 (file)
@@ -31,12 +31,26 @@ struct ref_sort {
        unsigned reverse : 1;
 };
 
-struct refinfo {
-       char *refname;
+struct ref_array_item {
        unsigned char objectname[20];
        int flag;
        const char *symref;
        struct atom_value *value;
+       char *refname;
+};
+
+struct ref_array {
+       int nr, alloc;
+       struct ref_array_item **items;
+};
+
+struct ref_filter {
+       const char **name_patterns;
+};
+
+struct ref_filter_cbdata {
+       struct ref_array array;
+       struct ref_filter filter;
 };
 
 static struct {
@@ -74,6 +88,7 @@ static struct {
        { "contents:body" },
        { "contents:signature" },
        { "upstream" },
+       { "push" },
        { "symref" },
        { "flag" },
        { "HEAD" },
@@ -85,7 +100,7 @@ static struct {
  * a "*" to denote deref_tag().
  *
  * We parse given format string and sort specifiers, and make a list
- * of properties that we need to extract out of objects.  refinfo
+ * of properties that we need to extract out of objects.  ref_array_item
  * structure will hold an array of values extracted that can be
  * indexed with the "atom number", which is an index into this
  * array.
@@ -98,7 +113,7 @@ static int need_color_reset_at_eol;
 /*
  * Used to parse format string and sort specifiers
  */
-static int parse_atom(const char *atom, const char *ep)
+int parse_ref_filter_atom(const char *atom, const char *ep)
 {
        const char *sp;
        int i, at;
@@ -175,7 +190,7 @@ static const char *find_next(const char *cp)
  * Make sure the format string is well formed, and parse out
  * the used atoms.
  */
-static int verify_format(const char *format)
+int verify_ref_format(const char *format)
 {
        const char *cp, *sp;
 
@@ -187,7 +202,7 @@ static int verify_format(const char *format)
                if (!ep)
                        return error("malformed format string %s", sp);
                /* sp points at "%(" and ep points at the closing ")" */
-               at = parse_atom(sp + 2, ep);
+               at = parse_ref_filter_atom(sp + 2, ep);
                cp = ep + 1;
 
                if (skip_prefix(used_atom[at], "color:", &color))
@@ -394,7 +409,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
        /*
         * We got here because atomname ends in "date" or "date<something>";
         * it's not possible that <something> is not ":<format>" because
-        * parse_atom() wouldn't have allowed it, so we can assume that no
+        * parse_ref_filter_atom() wouldn't have allowed it, so we can assume that no
         * ":" means no format is specified, and use the default.
         */
        formatp = strchr(atomname, ':');
@@ -622,7 +637,7 @@ static inline char *copy_advance(char *dst, const char *src)
 /*
  * Parse the object referred by ref, and grab needed value.
  */
-static void populate_value(struct refinfo *ref)
+static void populate_value(struct ref_array_item *ref)
 {
        void *buf;
        struct object *obj;
@@ -659,15 +674,26 @@ static void populate_value(struct refinfo *ref)
                else if (starts_with(name, "symref"))
                        refname = ref->symref ? ref->symref : "";
                else if (starts_with(name, "upstream")) {
+                       const char *branch_name;
                        /* only local branches may have an upstream */
-                       if (!starts_with(ref->refname, "refs/heads/"))
+                       if (!skip_prefix(ref->refname, "refs/heads/",
+                                        &branch_name))
                                continue;
-                       branch = branch_get(ref->refname + 11);
+                       branch = branch_get(branch_name);
 
-                       if (!branch || !branch->merge || !branch->merge[0] ||
-                           !branch->merge[0]->dst)
+                       refname = branch_get_upstream(branch, NULL);
+                       if (!refname)
+                               continue;
+               } else if (starts_with(name, "push")) {
+                       const char *branch_name;
+                       if (!skip_prefix(ref->refname, "refs/heads/",
+                                        &branch_name))
+                               continue;
+                       branch = branch_get(branch_name);
+
+                       refname = branch_get_push(branch, NULL);
+                       if (!refname)
                                continue;
-                       refname = branch->merge[0]->dst;
                } else if (starts_with(name, "color:")) {
                        char color[COLOR_MAXLEN] = "";
 
@@ -713,11 +739,12 @@ static void populate_value(struct refinfo *ref)
                                refname = shorten_unambiguous_ref(refname,
                                                      warn_ambiguous_refs);
                        else if (!strcmp(formatp, "track") &&
-                                starts_with(name, "upstream")) {
+                                (starts_with(name, "upstream") ||
+                                 starts_with(name, "push"))) {
                                char buf[40];
 
                                if (stat_tracking_info(branch, &num_ours,
-                                                      &num_theirs) != 1)
+                                                      &num_theirs, NULL))
                                        continue;
 
                                if (!num_ours && !num_theirs)
@@ -735,11 +762,12 @@ static void populate_value(struct refinfo *ref)
                                }
                                continue;
                        } else if (!strcmp(formatp, "trackshort") &&
-                                  starts_with(name, "upstream")) {
+                                  (starts_with(name, "upstream") ||
+                                   starts_with(name, "push"))) {
                                assert(branch);
 
                                if (stat_tracking_info(branch, &num_ours,
-                                                       &num_theirs) != 1)
+                                                       &num_theirs, NULL))
                                        continue;
 
                                if (!num_ours && !num_theirs)
@@ -821,7 +849,7 @@ static void populate_value(struct refinfo *ref)
  * Given a ref, return the value for the atom.  This lazily gets value
  * out of the object by calling populate value.
  */
-static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
+static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v)
 {
        if (!ref->value) {
                populate_value(ref);
@@ -830,72 +858,102 @@ static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
        *v = &ref->value[atom];
 }
 
-struct grab_ref_cbdata {
-       struct refinfo **grab_array;
-       const char **grab_pattern;
-       int grab_cnt;
-};
+/*
+ * Return 1 if the refname matches one of the patterns, otherwise 0.
+ * A pattern can be path prefix (e.g. a refname "refs/heads/master"
+ * matches a pattern "refs/heads/") or a wildcard (e.g. the same ref
+ * matches "refs/heads/m*",too).
+ */
+static int match_name_as_path(const char **pattern, const char *refname)
+{
+       int namelen = strlen(refname);
+       for (; *pattern; pattern++) {
+               const char *p = *pattern;
+               int plen = strlen(p);
+
+               if ((plen <= namelen) &&
+                   !strncmp(refname, p, plen) &&
+                   (refname[plen] == '\0' ||
+                    refname[plen] == '/' ||
+                    p[plen-1] == '/'))
+                       return 1;
+               if (!wildmatch(p, refname, WM_PATHNAME, NULL))
+                       return 1;
+       }
+       return 0;
+}
+
+/* Allocate space for a new ref_array_item and copy the objectname and flag to it */
+static struct ref_array_item *new_ref_array_item(const char *refname,
+                                                const unsigned char *objectname,
+                                                int flag)
+{
+       struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item));
+       ref->refname = xstrdup(refname);
+       hashcpy(ref->objectname, objectname);
+       ref->flag = flag;
+
+       return ref;
+}
 
 /*
  * A call-back given to for_each_ref().  Filter refs and keep them for
  * later object processing.
  */
-static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)
 {
-       struct grab_ref_cbdata *cb = cb_data;
-       struct refinfo *ref;
-       int cnt;
+       struct ref_filter_cbdata *ref_cbdata = cb_data;
+       struct ref_filter *filter = &ref_cbdata->filter;
+       struct ref_array_item *ref;
 
        if (flag & REF_BAD_NAME) {
                  warning("ignoring ref with broken name %s", refname);
                  return 0;
        }
 
-       if (*cb->grab_pattern) {
-               const char **pattern;
-               int namelen = strlen(refname);
-               for (pattern = cb->grab_pattern; *pattern; pattern++) {
-                       const char *p = *pattern;
-                       int plen = strlen(p);
-
-                       if ((plen <= namelen) &&
-                           !strncmp(refname, p, plen) &&
-                           (refname[plen] == '\0' ||
-                            refname[plen] == '/' ||
-                            p[plen-1] == '/'))
-                               break;
-                       if (!wildmatch(p, refname, WM_PATHNAME, NULL))
-                               break;
-               }
-               if (!*pattern)
-                       return 0;
-       }
+       if (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))
+               return 0;
 
        /*
         * We do not open the object yet; sort may only need refname
         * to do its job and the resulting list may yet to be pruned
         * by maxcount logic.
         */
-       ref = xcalloc(1, sizeof(*ref));
-       ref->refname = xstrdup(refname);
-       hashcpy(ref->objectname, sha1);
-       ref->flag = flag;
+       ref = new_ref_array_item(refname, oid->hash, flag);
 
-       cnt = cb->grab_cnt;
-       REALLOC_ARRAY(cb->grab_array, cnt + 1);
-       cb->grab_array[cnt++] = ref;
-       cb->grab_cnt = cnt;
+       REALLOC_ARRAY(ref_cbdata->array.items, ref_cbdata->array.nr + 1);
+       ref_cbdata->array.items[ref_cbdata->array.nr++] = ref;
        return 0;
 }
 
-static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
+/*  Free memory allocated for a ref_array_item */
+static void free_array_item(struct ref_array_item *item)
+{
+       free((char *)item->symref);
+       free(item->refname);
+       free(item);
+}
+
+/* Free all memory allocated for ref_array */
+void ref_array_clear(struct ref_array *array)
+{
+       int i;
+
+       for (i = 0; i < array->nr; i++)
+               free_array_item(array->items[i]);
+       free(array->items);
+       array->items = NULL;
+       array->nr = array->alloc = 0;
+}
+
+static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)
 {
        struct atom_value *va, *vb;
        int cmp;
        cmp_type cmp_type = used_atom_type[s->atom];
 
-       get_value(a, s->atom, &va);
-       get_value(b, s->atom, &vb);
+       get_ref_atom_value(a, s->atom, &va);
+       get_ref_atom_value(b, s->atom, &vb);
        switch (cmp_type) {
        case FIELD_STR:
                cmp = strcmp(va->s, vb->s);
@@ -912,25 +970,25 @@ static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b
        return (s->reverse) ? -cmp : cmp;
 }
 
-static struct ref_sort *ref_sort;
+static struct ref_sorting *ref_sorting;
 static int compare_refs(const void *a_, const void *b_)
 {
-       struct refinfo *a = *((struct refinfo **)a_);
-       struct refinfo *b = *((struct refinfo **)b_);
-       struct ref_sort *s;
+       struct ref_array_item *a = *((struct ref_array_item **)a_);
+       struct ref_array_item *b = *((struct ref_array_item **)b_);
+       struct ref_sorting *s;
 
-       for (s = ref_sort; s; s = s->next) {
-               int cmp = cmp_ref_sort(s, a, b);
+       for (s = ref_sorting; s; s = s->next) {
+               int cmp = cmp_ref_sorting(s, a, b);
                if (cmp)
                        return cmp;
        }
        return 0;
 }
 
-static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)
+void ref_array_sort(struct ref_sorting *sort, struct ref_array *array)
 {
-       ref_sort = sort;
-       qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
+       ref_sorting = sort;
+       qsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);
 }
 
 static void print_value(struct atom_value *v, int quote_style)
@@ -997,7 +1055,7 @@ static void emit(const char *cp, const char *ep)
        }
 }
 
-static void show_ref(struct refinfo *info, const char *format, int quote_style)
+void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
 {
        const char *cp, *sp, *ep;
 
@@ -1007,7 +1065,7 @@ static void show_ref(struct refinfo *info, const char *format, int quote_style)
                ep = strchr(sp, ')');
                if (cp < sp)
                        emit(cp, sp);
-               get_value(info, parse_atom(sp + 2, ep), &atomv);
+               get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
                print_value(atomv, quote_style);
        }
        if (*cp) {
@@ -1026,21 +1084,22 @@ static void show_ref(struct refinfo *info, const char *format, int quote_style)
        putchar('\n');
 }
 
-static struct ref_sort *default_sort(void)
+/*  If no sorting option is given, use refname to sort as default */
+struct ref_sorting *ref_default_sorting(void)
 {
        static const char cstr_name[] = "refname";
 
-       struct ref_sort *sort = xcalloc(1, sizeof(*sort));
+       struct ref_sorting *sort = xcalloc(1, sizeof(*sort));
 
        sort->next = NULL;
-       sort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));
+       sort->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));
        return sort;
 }
 
-static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
+int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
 {
-       struct ref_sort **sort_tail = opt->value;
-       struct ref_sort *s;
+       struct ref_sorting **sort_tail = opt->value;
+       struct ref_sorting *s;
        int len;
 
        if (!arg) /* should --no-sort void the list ? */
@@ -1055,23 +1114,22 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
                arg++;
        }
        len = strlen(arg);
-       s->atom = parse_atom(arg, arg+len);
+       s->atom = parse_ref_filter_atom(arg, arg+len);
        return 0;
 }
 
 static char const * const for_each_ref_usage[] = {
-       N_("git for-each-ref [options] [<pattern>]"),
+       N_("git for-each-ref [<options>] [<pattern>]"),
        NULL
 };
 
 int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 {
-       int i, num_refs;
+       int i;
        const char *format = "%(objectname) %(objecttype)\t%(refname)";
-       struct ref_sort *sort = NULL, **sort_tail = &sort;
+       struct ref_sorting *sort = NULL, **sort_tail = &sort;
        int maxcount = 0, quote_style = 0;
-       struct refinfo **refs;
-       struct grab_ref_cbdata cbdata;
+       struct ref_filter_cbdata ref_cbdata;
 
        struct option opts[] = {
                OPT_BIT('s', "shell", &quote_style,
@@ -1087,7 +1145,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
                OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
                OPT_CALLBACK(0 , "sort", sort_tail, N_("key"),
-                           N_("field name to sort on"), &opt_parse_sort),
+                           N_("field name to sort on"), &parse_opt_ref_sorting),
                OPT_END(),
        };
 
@@ -1100,26 +1158,25 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                error("more than one quoting style?");
                usage_with_options(for_each_ref_usage, opts);
        }
-       if (verify_format(format))
+       if (verify_ref_format(format))
                usage_with_options(for_each_ref_usage, opts);
 
        if (!sort)
-               sort = default_sort();
+               sort = ref_default_sorting();
 
        /* for warn_ambiguous_refs */
        git_config(git_default_config, NULL);
 
-       memset(&cbdata, 0, sizeof(cbdata));
-       cbdata.grab_pattern = argv;
-       for_each_rawref(grab_single_ref, &cbdata);
-       refs = cbdata.grab_array;
-       num_refs = cbdata.grab_cnt;
+       memset(&ref_cbdata, 0, sizeof(ref_cbdata));
+       ref_cbdata.filter.name_patterns = argv;
+       for_each_rawref(ref_filter_handler, &ref_cbdata);
 
-       sort_refs(sort, refs, num_refs);
+       ref_array_sort(sort, &ref_cbdata.array);
 
-       if (!maxcount || num_refs < maxcount)
-               maxcount = num_refs;
+       if (!maxcount || ref_cbdata.array.nr < maxcount)
+               maxcount = ref_cbdata.array.nr;
        for (i = 0; i < maxcount; i++)
-               show_ref(refs[i], format, quote_style);
+               show_ref_array_item(ref_cbdata.array.items[i], format, quote_style);
+       ref_array_clear(&ref_cbdata.array);
        return 0;
 }