fast-import: add input format tests
[gitweb.git] / setup.c
diff --git a/setup.c b/setup.c
index f0468c6864195e0ff9532f9ec5251d5288b38397..ce87900ce3c68ace0f231827bdda0e9a65ef15b3 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -7,10 +7,13 @@ static int inside_work_tree = -1;
 char *prefix_path(const char *prefix, int len, const char *path)
 {
        const char *orig = path;
-       char *sanitized = xmalloc(len + strlen(path) + 1);
-       if (is_absolute_path(orig))
-               strcpy(sanitized, path);
-       else {
+       char *sanitized;
+       if (is_absolute_path(orig)) {
+               const char *temp = real_path(path);
+               sanitized = xmalloc(len + strlen(temp) + 1);
+               strcpy(sanitized, temp);
+       } else {
+               sanitized = xmalloc(len + strlen(path) + 1);
                if (len)
                        memcpy(sanitized, prefix, len);
                strcpy(sanitized + len, path);
@@ -82,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);
@@ -123,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;
@@ -144,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;
@@ -218,7 +328,7 @@ void setup_work_tree(void)
        work_tree = get_git_work_tree();
        git_dir = get_git_dir();
        if (!is_absolute_path(git_dir))
-               git_dir = make_absolute_path(git_dir);
+               git_dir = real_path(get_git_dir());
        if (!work_tree || chdir(work_tree))
                die("This operation must be run in a work tree");
 
@@ -229,7 +339,7 @@ void setup_work_tree(void)
        if (getenv(GIT_WORK_TREE_ENVIRONMENT))
                setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-       set_git_dir(make_relative_path(git_dir, work_tree));
+       set_git_dir(relative_path(git_dir, work_tree));
        initialized = 1;
 }
 
@@ -272,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;
@@ -309,7 +419,7 @@ const char *read_gitfile_gently(const char *path)
 
        if (!is_git_directory(dir))
                die("Not a git repository: %s", dir);
-       path = make_absolute_path(dir);
+       path = real_path(dir);
 
        free(buf);
        return path;
@@ -390,7 +500,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
 
        offset = dir_inside_of(cwd, worktree);
        if (offset >= 0) {      /* cwd inside worktree? */
-               set_git_dir(make_absolute_path(gitdirenv));
+               set_git_dir(real_path(gitdirenv));
                if (chdir(worktree))
                        die_errno("Could not chdir to '%s'", worktree);
                cwd[len++] = '/';
@@ -415,7 +525,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
        /* --work-tree is set without --git-dir; use discovered one */
        if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
                if (offset != len && !is_absolute_path(gitdir))
-                       gitdir = xstrdup(make_absolute_path(gitdir));
+                       gitdir = xstrdup(real_path(gitdir));
                if (chdir(cwd))
                        die_errno("Could not come back to cwd");
                return setup_explicit_git_dir(gitdir, cwd, len, nongit_ok);
@@ -423,7 +533,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
 
        /* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
        if (is_bare_repository_cfg > 0) {
-               set_git_dir(offset == len ? gitdir : make_absolute_path(gitdir));
+               set_git_dir(offset == len ? gitdir : real_path(gitdir));
                if (chdir(cwd))
                        die_errno("Could not come back to cwd");
                return NULL;