Merge branch 'jk/submodule-init-segv-fix'
[gitweb.git] / builtin / name-rev.c
index cd89d48b65e8f70819f73ef6e2cfd0634752b284..92a5d8a5d263f78afaa2f426135998348dae53b8 100644 (file)
@@ -108,7 +108,8 @@ static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
 struct name_ref_data {
        int tags_only;
        int name_only;
-       const char *ref_filter;
+       struct string_list ref_filters;
+       struct string_list exclude_filters;
 };
 
 static struct tip_table {
@@ -150,18 +151,49 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
        if (data->tags_only && !starts_with(path, "refs/tags/"))
                return 0;
 
-       if (data->ref_filter) {
-               switch (subpath_matches(path, data->ref_filter)) {
-               case -1: /* did not match */
-                       return 0;
-               case 0:  /* matched fully */
-                       break;
-               default: /* matched subpath */
-                       can_abbreviate_output = 1;
-                       break;
+       if (data->exclude_filters.nr) {
+               struct string_list_item *item;
+
+               for_each_string_list_item(item, &data->exclude_filters) {
+                       if (subpath_matches(path, item->string) >= 0)
+                               return 0;
                }
        }
 
+       if (data->ref_filters.nr) {
+               struct string_list_item *item;
+               int matched = 0;
+
+               /* See if any of the patterns match. */
+               for_each_string_list_item(item, &data->ref_filters) {
+                       /*
+                        * Check all patterns even after finding a match, so
+                        * that we can see if a match with a subpath exists.
+                        * When a user asked for 'refs/tags/v*' and 'v1.*',
+                        * both of which match, the user is showing her
+                        * willingness to accept a shortened output by having
+                        * the 'v1.*' in the acceptable refnames, so we
+                        * shouldn't stop when seeing 'refs/tags/v1.4' matches
+                        * 'refs/tags/v*'.  We should show it as 'v1.4'.
+                        */
+                       switch (subpath_matches(path, item->string)) {
+                       case -1: /* did not match */
+                               break;
+                       case 0: /* matched fully */
+                               matched = 1;
+                               break;
+                       default: /* matched subpath */
+                               matched = 1;
+                               can_abbreviate_output = 1;
+                               break;
+                       }
+               }
+
+               /* If none of the patterns matched, stop now */
+               if (!matched)
+                       return 0;
+       }
+
        add_to_tip_table(oid->hash, path, can_abbreviate_output);
 
        while (o && o->type == OBJ_TAG) {
@@ -206,10 +238,9 @@ static const char *get_exact_ref_match(const struct object *o)
        return NULL;
 }
 
-/* returns a static buffer */
-static const char *get_rev_name(const struct object *o)
+/* may return a constant string or use "buf" as scratch space */
+static const char *get_rev_name(const struct object *o, struct strbuf *buf)
 {
-       static char buffer[1024];
        struct rev_name *n;
        struct commit *c;
 
@@ -226,10 +257,9 @@ static const char *get_rev_name(const struct object *o)
                int len = strlen(n->tip_name);
                if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
                        len -= 2;
-               snprintf(buffer, sizeof(buffer), "%.*s~%d", len, n->tip_name,
-                               n->generation);
-
-               return buffer;
+               strbuf_reset(buf);
+               strbuf_addf(buf, "%.*s~%d", len, n->tip_name, n->generation);
+               return buf->buf;
        }
 }
 
@@ -239,10 +269,11 @@ static void show_name(const struct object *obj,
 {
        const char *name;
        const struct object_id *oid = &obj->oid;
+       struct strbuf buf = STRBUF_INIT;
 
        if (!name_only)
                printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
-       name = get_rev_name(obj);
+       name = get_rev_name(obj, &buf);
        if (name)
                printf("%s\n", name);
        else if (allow_undefined)
@@ -251,6 +282,7 @@ static void show_name(const struct object *obj,
                printf("%s\n", find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
        else
                die("cannot describe '%s'", oid_to_hex(oid));
+       strbuf_release(&buf);
 }
 
 static char const * const name_rev_usage[] = {
@@ -262,6 +294,7 @@ static char const * const name_rev_usage[] = {
 
 static void name_rev_line(char *p, struct name_ref_data *data)
 {
+       struct strbuf buf = STRBUF_INIT;
        int forty = 0;
        char *p_start;
        for (p_start = p; *p; p++) {
@@ -282,7 +315,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
                                struct object *o =
                                        lookup_object(sha1);
                                if (o)
-                                       name = get_rev_name(o);
+                                       name = get_rev_name(o, &buf);
                        }
                        *(p+1) = c;
 
@@ -300,18 +333,22 @@ static void name_rev_line(char *p, struct name_ref_data *data)
        /* flush */
        if (p_start != p)
                fwrite(p_start, p - p_start, 1, stdout);
+
+       strbuf_release(&buf);
 }
 
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
        struct object_array revs = OBJECT_ARRAY_INIT;
        int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
-       struct name_ref_data data = { 0, 0, NULL };
+       struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
        struct option opts[] = {
                OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
                OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
-               OPT_STRING(0, "refs", &data.ref_filter, N_("pattern"),
+               OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"),
                                   N_("only use refs matching <pattern>")),
+               OPT_STRING_LIST(0, "exclude", &data.exclude_filters, N_("pattern"),
+                                  N_("ignore refs matching <pattern>")),
                OPT_GROUP(""),
                OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
                OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")),