tag: factor out get_tagged_oid()
[gitweb.git] / ref-filter.c
index 791f0648a6edc4ecc4629865393703d9b1e4a227..f27cfc8c3e358fa27d7aec78c0b3c44c816402b3 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;
@@ -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 (i = 0; i < nr; i++) {
+               char c = patterns[i][prefix->len];
+               if (!c || is_glob_special(c)) {
+                       string_list_append(out, prefix->buf);
+                       return;
+               }
+       }
+
+       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;
 
-       for (p = pattern; *p && !is_glob_special(*p); p++)
-               ;
+       argv_array_pushv(&sorted, patterns);
+       QSORT(sorted.argv, sorted.argc, qsort_strcmp);
 
-       strbuf_add(out, pattern, p - pattern);
+       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;
 }
 
@@ -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);