general improvements
[gitweb.git] / ref-filter.c
index 791f0648a6edc4ecc4629865393703d9b1e4a227..220e9bd74a507cb5e73fcdd64e88ab3ff384baff 100644 (file)
@@ -22,6 +22,7 @@
 #include "commit-reach.h"
 #include "worktree.h"
 #include "hashmap.h"
+#include "argv-array.h"
 
 static struct ref_msg {
        const char *gone;
@@ -1027,7 +1028,7 @@ static const char *copy_name(const char *buf)
                if (!strncmp(cp, " <", 2))
                        return xmemdupz(buf, cp - buf);
        }
-       return "";
+       return xstrdup("");
 }
 
 static const char *copy_email(const char *buf)
@@ -1035,10 +1036,10 @@ static const char *copy_email(const char *buf)
        const char *email = strchr(buf, '<');
        const char *eoemail;
        if (!email)
-               return "";
+               return xstrdup("");
        eoemail = strchr(email, '>');
        if (!eoemail)
-               return "";
+               return xstrdup("");
        return xmemdupz(email, eoemail + 1 - email);
 }
 
@@ -1765,7 +1766,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.
         */
-       oi_deref.oid = ((struct tag *)obj)->tagged->oid;
+       oi_deref.oid = *get_tagged_oid((struct tag *)obj);
 
        /*
         * NEEDSWORK: This derefs tag only once, which
@@ -1863,21 +1864,62 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
        return match_pattern(filter, refname);
 }
 
-/*
- * Find the longest prefix of pattern we can pass to
- * `for_each_fullref_in()`, namely the part of pattern preceding the
- * first glob character. (Note that `for_each_fullref_in()` is
- * perfectly happy working with a prefix that doesn't end at a
- * pathname component boundary.)
- */
-static void find_longest_prefix(struct strbuf *out, const char *pattern)
+static int qsort_strcmp(const void *va, const void *vb)
+{
+       const char *a = *(const char **)va;
+       const char *b = *(const char **)vb;
+
+       return strcmp(a, b);
+}
+
+static void find_longest_prefixes_1(struct string_list *out,
+                                 struct strbuf *prefix,
+                                 const char **patterns, size_t nr)
 {
-       const char *p;
+       size_t i;
 
-       for (p = pattern; *p && !is_glob_special(*p); p++)
-               ;
+       for (i = 0; i < nr; i++) {
+               char c = patterns[i][prefix->len];
+               if (!c || is_glob_special(c)) {
+                       string_list_append(out, prefix->buf);
+                       return;
+               }
+       }
 
-       strbuf_add(out, pattern, p - pattern);
+       i = 0;
+       while (i < nr) {
+               size_t end;
+
+               /*
+               * Set "end" to the index of the element _after_ the last one
+               * in our group.
+               */
+               for (end = i + 1; end < nr; end++) {
+                       if (patterns[i][prefix->len] != patterns[end][prefix->len])
+                               break;
+               }
+
+               strbuf_addch(prefix, patterns[i][prefix->len]);
+               find_longest_prefixes_1(out, prefix, patterns + i, end - i);
+               strbuf_setlen(prefix, prefix->len - 1);
+
+               i = end;
+       }
+}
+
+static void find_longest_prefixes(struct string_list *out,
+                                 const char **patterns)
+{
+       struct argv_array sorted = ARGV_ARRAY_INIT;
+       struct strbuf prefix = STRBUF_INIT;
+
+       argv_array_pushv(&sorted, patterns);
+       QSORT(sorted.argv, sorted.argc, qsort_strcmp);
+
+       find_longest_prefixes_1(out, &prefix, sorted.argv, sorted.argc);
+
+       argv_array_clear(&sorted);
+       strbuf_release(&prefix);
 }
 
 /*
@@ -1890,7 +1932,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
                                       void *cb_data,
                                       int broken)
 {
-       struct strbuf prefix = STRBUF_INIT;
+       struct string_list prefixes = STRING_LIST_INIT_DUP;
+       struct string_list_item *prefix;
        int ret;
 
        if (!filter->match_as_path) {
@@ -1916,21 +1959,15 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
                return for_each_fullref_in("", cb, cb_data, broken);
        }
 
-       if (filter->name_patterns[1]) {
-               /*
-                * multiple patterns; in theory this could still work as long
-                * as the patterns are disjoint. We'd just make multiple calls
-                * to for_each_ref(). But if they're not disjoint, we'd end up
-                * reporting the same ref multiple times. So let's punt on that
-                * for now.
-                */
-               return for_each_fullref_in("", cb, cb_data, broken);
-       }
+       find_longest_prefixes(&prefixes, filter->name_patterns);
 
-       find_longest_prefix(&prefix, filter->name_patterns[0]);
+       for_each_string_list_item(prefix, &prefixes) {
+               ret = for_each_fullref_in(prefix->string, cb, cb_data, broken);
+               if (ret)
+                       break;
+       }
 
-       ret = for_each_fullref_in(prefix.buf, cb, cb_data, broken);
-       strbuf_release(&prefix);
+       string_list_clear(&prefixes, 0);
        return ret;
 }
 
@@ -1960,7 +1997,7 @@ static const struct object_id *match_points_at(struct oid_array *points_at,
        if (!obj)
                die(_("malformed object at '%s'"), refname);
        if (obj->type == OBJ_TAG)
-               tagged_oid = &((struct tag *)obj)->tagged->oid;
+               tagged_oid = get_tagged_oid((struct tag *)obj);
        if (tagged_oid && oid_array_lookup(points_at, tagged_oid) >= 0)
                return tagged_oid;
        return NULL;
@@ -2105,7 +2142,9 @@ static void free_array_item(struct ref_array_item *item)
 {
        free((char *)item->symref);
        if (item->value) {
-               free((char *)item->value->s);
+               int i;
+               for (i = 0; i < used_atom_cnt; i++)
+                       free((char *)item->value[i].s);
                free(item->value);
        }
        free(item);
@@ -2116,14 +2155,16 @@ 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);
        array->nr = array->alloc = 0;
+
+       for (i = 0; i < used_atom_cnt; i++)
+               free((char *)used_atom[i].name);
+       FREE_AND_NULL(used_atom);
+       used_atom_cnt = 0;
+
        if (ref_to_worktree_map.worktrees) {
                hashmap_free(&(ref_to_worktree_map.map), 1);
                free_worktrees(ref_to_worktree_map.worktrees);