Merge branch 'jc/magic-pathspec'
* jc/magic-pathspec: setup.c: Fix some "symbol not declared" sparse warnings t3703: Skip tests using directory name ":" on Windows revision.c: leave a note for "a lone :" enhancement t3703, t4208: add test cases for magic pathspec rev/path disambiguation: further restrict "misspelled index entry" diag fix overslow :/no-such-string-ever-existed diagnostics fix overstrict :<path> diagnosis grep: use get_pathspec() correctly pathspec: drop "lone : means no pathspec" from get_pathspec() Revert "magic pathspec: add ":(icase)path" to match case insensitively" magic pathspec: add ":(icase)path" to match case insensitively magic pathspec: futureproof shorthand form magic pathspec: add tentative ":/path/from/top/level" pathspec support
This commit is contained in:
115
setup.c
115
setup.c
@ -85,8 +85,17 @@ static void NORETURN die_verify_filename(const char *prefix, const char *arg)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
unsigned mode;
|
||||
/* try a detailed diagnostic ... */
|
||||
get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
|
||||
|
||||
/*
|
||||
* Saying "'(icase)foo' does not exist in the index" when the
|
||||
* user gave us ":(icase)foo" is just stupid. A magic pathspec
|
||||
* begins with a colon and is followed by a non-alnum; do not
|
||||
* let get_sha1_with_mode_1(only_to_die=1) to even trigger.
|
||||
*/
|
||||
if (!(arg[0] == ':' && !isalnum(arg[1])))
|
||||
/* try a detailed diagnostic ... */
|
||||
get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix);
|
||||
|
||||
/* ... or fall back the most general message. */
|
||||
die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
|
||||
"Use '--' to separate paths from revisions", arg);
|
||||
@ -126,6 +135,105 @@ void verify_non_filename(const char *prefix, const char *arg)
|
||||
"Use '--' to separate filenames from revisions", arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Magic pathspec
|
||||
*
|
||||
* NEEDSWORK: These need to be moved to dir.h or even to a new
|
||||
* pathspec.h when we restructure get_pathspec() users to use the
|
||||
* "struct pathspec" interface.
|
||||
*
|
||||
* Possible future magic semantics include stuff like:
|
||||
*
|
||||
* { PATHSPEC_NOGLOB, '!', "noglob" },
|
||||
* { PATHSPEC_ICASE, '\0', "icase" },
|
||||
* { PATHSPEC_RECURSIVE, '*', "recursive" },
|
||||
* { PATHSPEC_REGEXP, '\0', "regexp" },
|
||||
*
|
||||
*/
|
||||
#define PATHSPEC_FROMTOP (1<<0)
|
||||
|
||||
static struct pathspec_magic {
|
||||
unsigned bit;
|
||||
char mnemonic; /* this cannot be ':'! */
|
||||
const char *name;
|
||||
} pathspec_magic[] = {
|
||||
{ PATHSPEC_FROMTOP, '/', "top" },
|
||||
};
|
||||
|
||||
/*
|
||||
* Take an element of a pathspec and check for magic signatures.
|
||||
* Append the result to the prefix.
|
||||
*
|
||||
* For now, we only parse the syntax and throw out anything other than
|
||||
* "top" magic.
|
||||
*
|
||||
* NEEDSWORK: This needs to be rewritten when we start migrating
|
||||
* get_pathspec() users to use the "struct pathspec" interface. For
|
||||
* example, a pathspec element may be marked as case-insensitive, but
|
||||
* the prefix part must always match literally, and a single stupid
|
||||
* string cannot express such a case.
|
||||
*/
|
||||
static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
|
||||
{
|
||||
unsigned magic = 0;
|
||||
const char *copyfrom = elt;
|
||||
int i;
|
||||
|
||||
if (elt[0] != ':') {
|
||||
; /* nothing to do */
|
||||
} else if (elt[1] == '(') {
|
||||
/* longhand */
|
||||
const char *nextat;
|
||||
for (copyfrom = elt + 2;
|
||||
*copyfrom && *copyfrom != ')';
|
||||
copyfrom = nextat) {
|
||||
size_t len = strcspn(copyfrom, ",)");
|
||||
if (copyfrom[len] == ')')
|
||||
nextat = copyfrom + len;
|
||||
else
|
||||
nextat = copyfrom + len + 1;
|
||||
if (!len)
|
||||
continue;
|
||||
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 (ARRAY_SIZE(pathspec_magic) <= i)
|
||||
die("Invalid pathspec magic '%.*s' in '%s'",
|
||||
(int) len, copyfrom, elt);
|
||||
}
|
||||
if (*copyfrom == ')')
|
||||
copyfrom++;
|
||||
} else {
|
||||
/* shorthand */
|
||||
for (copyfrom = elt + 1;
|
||||
*copyfrom && *copyfrom != ':';
|
||||
copyfrom++) {
|
||||
char ch = *copyfrom;
|
||||
|
||||
if (!is_pathspec_magic(ch))
|
||||
break;
|
||||
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
|
||||
if (pathspec_magic[i].mnemonic == ch) {
|
||||
magic |= pathspec_magic[i].bit;
|
||||
break;
|
||||
}
|
||||
if (ARRAY_SIZE(pathspec_magic) <= i)
|
||||
die("Unimplemented pathspec magic '%c' in '%s'",
|
||||
ch, elt);
|
||||
}
|
||||
if (*copyfrom == ':')
|
||||
copyfrom++;
|
||||
}
|
||||
|
||||
if (magic & PATHSPEC_FROMTOP)
|
||||
return xstrdup(copyfrom);
|
||||
else
|
||||
return prefix_path(prefix, prefixlen, copyfrom);
|
||||
}
|
||||
|
||||
const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||
{
|
||||
const char *entry = *pathspec;
|
||||
@ -147,8 +255,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
|
||||
dst = pathspec;
|
||||
prefixlen = prefix ? strlen(prefix) : 0;
|
||||
while (*src) {
|
||||
const char *p = prefix_path(prefix, prefixlen, *src);
|
||||
*(dst++) = p;
|
||||
*(dst++) = prefix_pathspec(prefix, prefixlen, *src);
|
||||
src++;
|
||||
}
|
||||
*dst = NULL;
|
||||
|
Reference in New Issue
Block a user