diff --git a/pathspec.c b/pathspec.c index 71e5eaf1b1..82ede57206 100644 --- a/pathspec.c +++ b/pathspec.c @@ -92,9 +92,9 @@ static unsigned prefix_pathspec(struct pathspec_item *item, const char *elt) { unsigned magic = 0, short_magic = 0; - const char *copyfrom = elt; + const char *copyfrom = elt, *long_magic_end = NULL; char *match; - int i; + int i, pathspec_prefix = -1; if (elt[0] != ':') { ; /* nothing to do */ @@ -112,18 +112,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,7 +161,14 @@ static unsigned prefix_pathspec(struct pathspec_item *item, magic |= short_magic; *p_short_magic = short_magic; - if (magic & PATHSPEC_FROMTOP) { + if (pathspec_prefix >= 0 && + (prefixlen || (prefix && *prefix))) + die("BUG: 'prefix' magic is supposed to be used at worktree's root"); + + if (pathspec_prefix >= 0) { + match = xstrdup(copyfrom); + prefixlen = pathspec_prefix; + } else if (magic & PATHSPEC_FROMTOP) { match = xstrdup(copyfrom); prefixlen = 0; } else { @@ -165,7 +183,20 @@ 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 && !limit_pathspec_to_literal()) { + /* Preserve the actual prefix length of each pattern */ + 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