* If seen[] has not already been written to, it may make sense
* to use find_pathspecs_matching_against_index() instead.
*/
-void add_pathspec_matches_against_index(const char **pathspec,
- char *seen, int specs)
+void add_pathspec_matches_against_index(const struct pathspec *pathspec,
+ char *seen)
{
int num_unmatched = 0, i;
* mistakenly think that the user gave a pathspec that did not match
* anything.
*/
- for (i = 0; i < specs; i++)
+ for (i = 0; i < pathspec->nr; i++)
if (!seen[i])
num_unmatched++;
if (!num_unmatched)
return;
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
+ const struct cache_entry *ce = active_cache[i];
+ match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen);
}
}
* nature of the "closest" (i.e. most specific) matches which each of the
* given pathspecs achieves against all items in the index.
*/
-char *find_pathspecs_matching_against_index(const char **pathspec)
+char *find_pathspecs_matching_against_index(const struct pathspec *pathspec)
{
- char *seen;
- int i;
-
- for (i = 0; pathspec[i]; i++)
- ; /* just counting */
- seen = xcalloc(i, 1);
- add_pathspec_matches_against_index(pathspec, seen, i);
+ char *seen = xcalloc(pathspec->nr, 1);
+ add_pathspec_matches_against_index(pathspec, seen);
return seen;
}
-/*
- * Check the index to see whether path refers to a submodule, or
- * something inside a submodule. If the former, returns the path with
- * any trailing slash stripped. If the latter, dies with an error
- * message.
- */
-const char *check_path_for_gitlink(const char *path)
-{
- int i, path_len = strlen(path);
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (S_ISGITLINK(ce->ce_mode)) {
- int ce_len = ce_namelen(ce);
- if (path_len <= ce_len || path[ce_len] != '/' ||
- memcmp(ce->name, path, ce_len))
- /* path does not refer to this
- * submodule or anything inside it */
- continue;
- if (path_len == ce_len + 1) {
- /* path refers to submodule;
- * strip trailing slash */
- return xstrndup(ce->name, ce_len);
- } else {
- die (_("Path '%s' is in submodule '%.*s'"),
- path, ce_len, ce->name);
- }
- }
- }
- return path;
-}
-
-/*
- * Dies if the given path refers to a file inside a symlinked
- * directory in the index.
- */
-void die_if_path_beyond_symlink(const char *path, const char *prefix)
-{
- if (has_symlink_leading_path(path, strlen(path))) {
- int len = prefix ? strlen(prefix) : 0;
- die(_("'%s' is beyond a symbolic link"), path + len);
- }
-}
-
/*
* Magic pathspec
*
* Possible future magic semantics include stuff like:
*
- * { PATHSPEC_NOGLOB, '!', "noglob" },
- * { PATHSPEC_ICASE, '\0', "icase" },
* { PATHSPEC_RECURSIVE, '*', "recursive" },
* { PATHSPEC_REGEXP, '\0', "regexp" },
*
const char *name;
} pathspec_magic[] = {
{ PATHSPEC_FROMTOP, '/', "top" },
+ { PATHSPEC_LITERAL, 0, "literal" },
+ { PATHSPEC_GLOB, '\0', "glob" },
+ { PATHSPEC_ICASE, '\0', "icase" },
};
/*
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 (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] != ':') {
+ if (elt[0] != ':' || literal_global ||
+ (flags & PATHSPEC_LITERAL_PATH)) {
; /* nothing to do */
} else if (elt[1] == '(') {
/* longhand */
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 */
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;
- item->original = elt;
+ /*
+ * Prefix the pathspec (keep all magic) and assign to
+ * original. Useful for passing to another command.
+ */
+ if (flags & PATHSPEC_PREFIX_ORIGIN) {
+ struct strbuf sb = STRBUF_INIT;
+ 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] == '/') &&
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;
}
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;
- pathspec->raw = raw;
+ pathspec->_raw = raw;
return;
}
pathspec->nr = n;
pathspec->items = item = xmalloc(sizeof(*item) * n);
- pathspec->raw = argv;
+ pathspec->_raw = argv;
prefixlen = prefix ? strlen(prefix) : 0;
for (i = 0; i < n; i++) {
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,
pathspec->magic |= item[i].magic;
}
- if (pathspec->magic & PATHSPEC_MAXDEPTH)
+
+ if (pathspec->magic & PATHSPEC_MAXDEPTH) {
+ if (flags & PATHSPEC_KEEP_ORDER)
+ die("BUG: PATHSPEC_MAXDEPTH_VALID and PATHSPEC_KEEP_ORDER are incompatible");
qsort(pathspec->items, pathspec->nr,
sizeof(struct pathspec_item), pathspec_item_cmp);
+ }
}
/*
{
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;
+ return ps._raw;
}
void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
memcpy(dst->items, src->items,
sizeof(struct pathspec_item) * dst->nr);
}
+
+void free_pathspec(struct pathspec *pathspec)
+{
+ free(pathspec->items);
+ pathspec->items = NULL;
+}