summaryrefslogtreecommitdiff
path: root/setup.c
diff options
context:
space:
mode:
Diffstat (limited to 'setup.c')
-rw-r--r--setup.c124
1 files changed, 116 insertions, 8 deletions
diff --git a/setup.c b/setup.c
index 03cd84f..ce87900 100644
--- a/setup.c
+++ b/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;
@@ -275,7 +382,7 @@ const char *read_gitfile_gently(const char *path)
const char *slash;
struct stat st;
int fd;
- size_t len;
+ ssize_t len;
if (stat(path, &st))
return NULL;
@@ -325,6 +432,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
const char *worktree;
char *gitfile;
+ int offset;
if (PATH_MAX - 40 < strlen(gitdirenv))
die("'$%s' too big", GIT_DIR_ENVIRONMENT);
@@ -390,15 +498,15 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
return NULL;
}
- if (!prefixcmp(cwd, worktree) &&
- cwd[strlen(worktree)] == '/') { /* cwd inside worktree */
+ offset = dir_inside_of(cwd, worktree);
+ if (offset >= 0) { /* cwd inside worktree? */
set_git_dir(real_path(gitdirenv));
if (chdir(worktree))
die_errno("Could not chdir to '%s'", worktree);
cwd[len++] = '/';
cwd[len] = '\0';
free(gitfile);
- return cwd + strlen(worktree) + 1;
+ return cwd + offset;
}
/* cwd outside worktree */