rev-list: disable object/refname ambiguity check with --stdin
[gitweb.git] / pathspec.c
index da802e22a01ca0bc255961c745e14841fa7d2990..87b3b82f1fd9baee7758159e5e3119b6da0655e3 100644 (file)
@@ -32,7 +32,7 @@ void add_pathspec_matches_against_index(const struct pathspec *pathspec,
        if (!num_unmatched)
                return;
        for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen);
        }
 }
@@ -57,8 +57,6 @@ char *find_pathspecs_matching_against_index(const struct pathspec *pathspec)
  *
  * Possible future magic semantics include stuff like:
  *
- *     { PATHSPEC_NOGLOB, '!', "noglob" },
- *     { PATHSPEC_ICASE, '\0', "icase" },
  *     { PATHSPEC_RECURSIVE, '*', "recursive" },
  *     { PATHSPEC_REGEXP, '\0', "regexp" },
  *
@@ -70,6 +68,9 @@ static struct pathspec_magic {
        const char *name;
 } pathspec_magic[] = {
        { PATHSPEC_FROMTOP, '/', "top" },
+       { PATHSPEC_LITERAL,   0, "literal" },
+       { PATHSPEC_GLOB,   '\0', "glob" },
+       { PATHSPEC_ICASE,  '\0', "icase" },
 };
 
 /*
@@ -91,12 +92,47 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
                                const char *prefix, int prefixlen,
                                const char *elt)
 {
-       unsigned magic = 0, short_magic = 0;
-       const char *copyfrom = elt;
+       static int literal_global = -1;
+       static int glob_global = -1;
+       static int noglob_global = -1;
+       static int icase_global = -1;
+       unsigned magic = 0, short_magic = 0, global_magic = 0;
+       const char *copyfrom = elt, *long_magic_end = NULL;
        char *match;
-       int i;
+       int i, pathspec_prefix = -1;
 
-       if (elt[0] != ':') {
+       if (literal_global < 0)
+               literal_global = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
+       if (literal_global)
+               global_magic |= PATHSPEC_LITERAL;
+
+       if (glob_global < 0)
+               glob_global = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT, 0);
+       if (glob_global)
+               global_magic |= PATHSPEC_GLOB;
+
+       if (noglob_global < 0)
+               noglob_global = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, 0);
+
+       if (glob_global && noglob_global)
+               die(_("global 'glob' and 'noglob' pathspec settings are incompatible"));
+
+
+       if (icase_global < 0)
+               icase_global = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT, 0);
+       if (icase_global)
+               global_magic |= PATHSPEC_ICASE;
+
+       if ((global_magic & PATHSPEC_LITERAL) &&
+           (global_magic & ~PATHSPEC_LITERAL))
+               die(_("global 'literal' pathspec setting is incompatible "
+                     "with all other global pathspec settings"));
+
+       if (flags & PATHSPEC_LITERAL_PATH)
+               global_magic = 0;
+
+       if (elt[0] != ':' || literal_global ||
+           (flags & PATHSPEC_LITERAL_PATH)) {
                ; /* nothing to do */
        } else if (elt[1] == '(') {
                /* longhand */
@@ -112,18 +148,29 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
                                nextat = copyfrom + len;
                        if (!len)
                                continue;
-                       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
+                       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
                                if (strlen(pathspec_magic[i].name) == len &&
                                    !strncmp(pathspec_magic[i].name, copyfrom, len)) {
                                        magic |= pathspec_magic[i].bit;
                                        break;
                                }
+                               if (!prefixcmp(copyfrom, "prefix:")) {
+                                       char *endptr;
+                                       pathspec_prefix = strtol(copyfrom + 7,
+                                                                &endptr, 10);
+                                       if (endptr - copyfrom != len)
+                                               die(_("invalid parameter for pathspec magic 'prefix'"));
+                                       /* "i" would be wrong, but it does not matter */
+                                       break;
+                               }
+                       }
                        if (ARRAY_SIZE(pathspec_magic) <= i)
                                die(_("Invalid pathspec magic '%.*s' in '%s'"),
                                    (int) len, copyfrom, elt);
                }
                if (*copyfrom != ')')
                        die(_("Missing ')' at the end of pathspec magic in '%s'"), elt);
+               long_magic_end = copyfrom;
                copyfrom++;
        } else {
                /* shorthand */
@@ -150,10 +197,34 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
        magic |= short_magic;
        *p_short_magic = short_magic;
 
-       if (magic & PATHSPEC_FROMTOP)
+       /* --noglob-pathspec adds :(literal) _unless_ :(glob) is specified */
+       if (noglob_global && !(magic & PATHSPEC_GLOB))
+               global_magic |= PATHSPEC_LITERAL;
+
+       /* --glob-pathspec is overridden by :(literal) */
+       if ((global_magic & PATHSPEC_GLOB) && (magic & PATHSPEC_LITERAL))
+               global_magic &= ~PATHSPEC_GLOB;
+
+       magic |= global_magic;
+
+       if (pathspec_prefix >= 0 &&
+           (prefixlen || (prefix && *prefix)))
+               die("BUG: 'prefix' magic is supposed to be used at worktree's root");
+
+       if ((magic & PATHSPEC_LITERAL) && (magic & PATHSPEC_GLOB))
+               die(_("%s: 'literal' and 'glob' are incompatible"), elt);
+
+       if (pathspec_prefix >= 0) {
+               match = xstrdup(copyfrom);
+               prefixlen = pathspec_prefix;
+       } else if (magic & PATHSPEC_FROMTOP) {
                match = xstrdup(copyfrom);
-       else
-               match = prefix_path(prefix, prefixlen, copyfrom);
+               prefixlen = 0;
+       } else {
+               match = prefix_path_gently(prefix, prefixlen, &prefixlen, copyfrom);
+               if (!match)
+                       die(_("%s: '%s' is outside repository"), elt, copyfrom);
+       }
        *raw = item->match = match;
        /*
         * Prefix the pathspec (keep all magic) and assign to
@@ -161,12 +232,28 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
         */
        if (flags & PATHSPEC_PREFIX_ORIGIN) {
                struct strbuf sb = STRBUF_INIT;
-               strbuf_add(&sb, elt, copyfrom - elt);
+               const char *start = elt;
+               if (prefixlen && !literal_global) {
+                       /* Preserve the actual prefix length of each pattern */
+                       if (short_magic)
+                               die("BUG: prefixing on short magic is not supported");
+                       else if (long_magic_end) {
+                               strbuf_add(&sb, start, long_magic_end - start);
+                               strbuf_addf(&sb, ",prefix:%d", prefixlen);
+                               start = long_magic_end;
+                       } else {
+                               if (*start == ':')
+                                       start++;
+                               strbuf_addf(&sb, ":(prefix:%d)", prefixlen);
+                       }
+               }
+               strbuf_add(&sb, start, copyfrom - start);
                strbuf_addstr(&sb, match);
                item->original = strbuf_detach(&sb, NULL);
        } else
                item->original = elt;
        item->len = strlen(item->match);
+       item->prefix = prefixlen;
 
        if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) &&
            (item->len >= 1 && item->match[item->len - 1] == '/') &&
@@ -196,15 +283,29 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
                                     elt, ce_len, ce->name);
                }
 
-       if (limit_pathspec_to_literal())
+       if (magic & PATHSPEC_LITERAL)
                item->nowildcard_len = item->len;
-       else
+       else {
                item->nowildcard_len = simple_length(item->match);
+               if (item->nowildcard_len < prefixlen)
+                       item->nowildcard_len = prefixlen;
+       }
        item->flags = 0;
-       if (item->nowildcard_len < item->len &&
-           item->match[item->nowildcard_len] == '*' &&
-           no_wildcard(item->match + item->nowildcard_len + 1))
-               item->flags |= PATHSPEC_ONESTAR;
+       if (magic & PATHSPEC_GLOB) {
+               /*
+                * FIXME: should we enable ONESTAR in _GLOB for
+                * pattern "* * / * . c"?
+                */
+       } else {
+               if (item->nowildcard_len < item->len &&
+                   item->match[item->nowildcard_len] == '*' &&
+                   no_wildcard(item->match + item->nowildcard_len + 1))
+                       item->flags |= PATHSPEC_ONESTAR;
+       }
+
+       /* sanity checks, pathspec matchers assume these are sane */
+       assert(item->nowildcard_len <= item->len &&
+              item->prefix         <= item->len);
        return magic;
 }
 
@@ -284,6 +385,7 @@ void parse_pathspec(struct pathspec *pathspec,
                item->match = prefix;
                item->original = prefix;
                item->nowildcard_len = item->len = strlen(prefix);
+               item->prefix = item->len;
                raw[0] = prefix;
                raw[1] = NULL;
                pathspec->nr = 1;
@@ -307,6 +409,9 @@ void parse_pathspec(struct pathspec *pathspec,
                item[i].magic = prefix_pathspec(item + i, &short_magic,
                                                argv + i, flags,
                                                prefix, prefixlen, entry);
+               if ((flags & PATHSPEC_LITERAL_PATH) &&
+                   !(magic_mask & PATHSPEC_LITERAL))
+                       item[i].magic |= PATHSPEC_LITERAL;
                if (item[i].magic & magic_mask)
                        unsupported_magic(entry,
                                          item[i].magic & magic_mask,
@@ -354,7 +459,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
 {
        struct pathspec ps;
        parse_pathspec(&ps,
-                      PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP,
+                      PATHSPEC_ALL_MAGIC &
+                      ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
                       PATHSPEC_PREFER_CWD,
                       prefix, pathspec);
        return ps._raw;