Treat D/F conflict entry more carefully in unpack-trees.c::threeway_merge()
[gitweb.git] / setup.c
diff --git a/setup.c b/setup.c
index 2afdba414a073705440f887593a1b5daa1023758..a45ea8309a9773160597f142f1208a6129885499 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -95,6 +95,8 @@ void verify_non_filename(const char *prefix, const char *arg)
        const char *name;
        struct stat st;
 
+       if (is_inside_git_dir())
+               return;
        if (*arg == '-')
                return; /* flag */
        name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
@@ -131,28 +133,69 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
 }
 
 /*
- * Test if it looks like we're at the top level git directory.
+ * Test if it looks like we're at a git directory.
  * We want to see:
  *
- *  - either a .git/objects/ directory _or_ the proper
+ *  - either a objects/ directory _or_ the proper
  *    GIT_OBJECT_DIRECTORY environment variable
- *  - a refs/ directory under ".git"
+ *  - a refs/ directory
  *  - either a HEAD symlink or a HEAD file that is formatted as
- *    a proper "ref:".
+ *    a proper "ref:", or a regular file HEAD that has a properly
+ *    formatted sha1 object name.
  */
-static int is_toplevel_directory(void)
+static int is_git_directory(const char *suspect)
 {
-       if (access(".git/refs/", X_OK) ||
-           access(getenv(DB_ENVIRONMENT) ?
-                  getenv(DB_ENVIRONMENT) : ".git/objects/", X_OK) ||
-           validate_symref(".git/HEAD"))
+       char path[PATH_MAX];
+       size_t len = strlen(suspect);
+
+       strcpy(path, suspect);
+       if (getenv(DB_ENVIRONMENT)) {
+               if (access(getenv(DB_ENVIRONMENT), X_OK))
+                       return 0;
+       }
+       else {
+               strcpy(path + len, "/objects");
+               if (access(path, X_OK))
+                       return 0;
+       }
+
+       strcpy(path + len, "/refs");
+       if (access(path, X_OK))
                return 0;
+
+       strcpy(path + len, "/HEAD");
+       if (validate_headref(path))
+               return 0;
+
        return 1;
 }
 
+static int inside_git_dir = -1;
+
+int is_inside_git_dir(void)
+{
+       if (inside_git_dir < 0) {
+               char buffer[1024];
+
+               if (is_bare_repository())
+                       return (inside_git_dir = 1);
+               if (getcwd(buffer, sizeof(buffer))) {
+                       const char *git_dir = get_git_dir(), *cwd = buffer;
+                       while (*git_dir && *git_dir == *cwd) {
+                               git_dir++;
+                               cwd++;
+                       }
+                       inside_git_dir = !*git_dir;
+               } else
+                       inside_git_dir = 0;
+       }
+       return inside_git_dir;
+}
+
 const char *setup_git_directory_gently(int *nongit_ok)
 {
        static char cwd[PATH_MAX+1];
+       const char *gitdirenv;
        int len, offset;
 
        /*
@@ -160,48 +203,36 @@ const char *setup_git_directory_gently(int *nongit_ok)
         * to do any discovery, but we still do repository
         * validation.
         */
-       if (getenv(GIT_DIR_ENVIRONMENT)) {
-               char path[PATH_MAX];
-               int len = strlen(getenv(GIT_DIR_ENVIRONMENT));
-               if (sizeof(path) - 40 < len)
+       gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
+       if (gitdirenv) {
+               if (PATH_MAX - 40 < strlen(gitdirenv))
                        die("'$%s' too big", GIT_DIR_ENVIRONMENT);
-               memcpy(path, getenv(GIT_DIR_ENVIRONMENT), len);
-               
-               strcpy(path + len, "/refs");
-               if (access(path, X_OK))
-                       goto bad_dir_environ;
-               strcpy(path + len, "/HEAD");
-               if (validate_symref(path))
-                       goto bad_dir_environ;
-               if (getenv(DB_ENVIRONMENT)) {
-                       if (access(getenv(DB_ENVIRONMENT), X_OK))
-                               goto bad_dir_environ;
-               }
-               else {
-                       strcpy(path + len, "/objects");
-                       if (access(path, X_OK))
-                               goto bad_dir_environ;
-               }
-               return NULL;
-       bad_dir_environ:
+               if (is_git_directory(gitdirenv))
+                       return NULL;
                if (nongit_ok) {
                        *nongit_ok = 1;
                        return NULL;
                }
-               path[len] = 0;
-               die("Not a git repository: '%s'", path);
+               die("Not a git repository: '%s'", gitdirenv);
        }
 
-       if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
+       if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
                die("Unable to read current working directory");
 
        offset = len = strlen(cwd);
        for (;;) {
-               if (is_toplevel_directory())
+               if (is_git_directory(".git"))
                        break;
                chdir("..");
                do {
                        if (!offset) {
+                               if (is_git_directory(cwd)) {
+                                       if (chdir(cwd))
+                                               die("Cannot come back to cwd");
+                                       setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
+                                       inside_git_dir = 1;
+                                       return NULL;
+                               }
                                if (nongit_ok) {
                                        if (chdir(cwd))
                                                die("Cannot come back to cwd");
@@ -220,6 +251,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
        offset++;
        cwd[len++] = '/';
        cwd[len] = 0;
+       inside_git_dir = !prefixcmp(cwd + offset, ".git/");
        return cwd + offset;
 }