Imported Upstream version 2.25.1
[platform/upstream/git.git] / setup.c
diff --git a/setup.c b/setup.c
index 1be5037..e2a479a 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -4,6 +4,7 @@
 #include "dir.h"
 #include "string-list.h"
 #include "chdir-notify.h"
+#include "promisor-remote.h"
 
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
@@ -39,7 +40,7 @@ static int abspath_part_inside_repo(char *path)
        off = offset_1st_component(path);
 
        /* check if work tree is already the prefix */
-       if (wtlen <= len && !strncmp(path, work_tree, wtlen)) {
+       if (wtlen <= len && !fspathncmp(path, work_tree, wtlen)) {
                if (path[wtlen] == '/') {
                        memmove(path, path + wtlen + 1, len - wtlen);
                        return 0;
@@ -59,7 +60,7 @@ static int abspath_part_inside_repo(char *path)
                path++;
                if (*path == '/') {
                        *path = '\0';
-                       if (strcmp(real_path(path0), work_tree) == 0) {
+                       if (fspathcmp(real_path(path0), work_tree) == 0) {
                                memmove(path0, path + 1, len - (path - path0));
                                return 0;
                        }
@@ -68,7 +69,7 @@ static int abspath_part_inside_repo(char *path)
        }
 
        /* check whole path */
-       if (strcmp(real_path(path0), work_tree) == 0) {
+       if (fspathcmp(real_path(path0), work_tree) == 0) {
                *path0 = '\0';
                return 0;
        }
@@ -164,7 +165,8 @@ int check_filename(const char *prefix, const char *arg)
        die_errno(_("failed to stat '%s'"), arg);
 }
 
-static void NORETURN die_verify_filename(const char *prefix,
+static void NORETURN die_verify_filename(struct repository *r,
+                                        const char *prefix,
                                         const char *arg,
                                         int diagnose_misspelt_rev)
 {
@@ -179,7 +181,7 @@ static void NORETURN die_verify_filename(const char *prefix,
         * let maybe_die_on_misspelt_object_name() even trigger.
         */
        if (!(arg[0] == ':' && !isalnum(arg[1])))
-               maybe_die_on_misspelt_object_name(arg, prefix);
+               maybe_die_on_misspelt_object_name(r, arg, prefix);
 
        /* ... or fall back the most general message. */
        die(_("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
@@ -234,7 +236,7 @@ void verify_filename(const char *prefix,
                die(_("option '%s' must come before non-option arguments"), arg);
        if (looks_like_pathspec(arg) || check_filename(prefix, arg))
                return;
-       die_verify_filename(prefix, arg, diagnose_misspelt_rev);
+       die_verify_filename(the_repository, prefix, arg, diagnose_misspelt_rev);
 }
 
 /*
@@ -411,6 +413,7 @@ static int read_worktree_config(const char *var, const char *value, void *vdata)
        } else if (strcmp(var, "core.worktree") == 0) {
                if (!value)
                        return config_error_nonbool(var);
+               free(data->work_tree);
                data->work_tree = xstrdup(value);
        }
        return 0;
@@ -476,7 +479,7 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
        }
 
        repository_format_precious_objects = candidate->precious_objects;
-       repository_format_partial_clone = candidate->partial_clone;
+       set_repository_format_partial_clone(candidate->partial_clone);
        repository_format_worktree_config = candidate->worktree_config;
        string_list_clear(&candidate->unknown_extensions, 0);
 
@@ -499,27 +502,38 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
                }
                if (candidate->work_tree) {
                        free(git_work_tree_cfg);
-                       git_work_tree_cfg = candidate->work_tree;
+                       git_work_tree_cfg = xstrdup(candidate->work_tree);
                        inside_work_tree = -1;
                }
-       } else {
-               free(candidate->work_tree);
        }
 
        return 0;
 }
 
+static void init_repository_format(struct repository_format *format)
+{
+       const struct repository_format fresh = REPOSITORY_FORMAT_INIT;
+
+       memcpy(format, &fresh, sizeof(fresh));
+}
+
 int read_repository_format(struct repository_format *format, const char *path)
 {
-       memset(format, 0, sizeof(*format));
-       format->version = -1;
-       format->is_bare = -1;
-       format->hash_algo = GIT_HASH_SHA1;
-       string_list_init(&format->unknown_extensions, 1);
+       clear_repository_format(format);
        git_config_from_file(check_repo_format, path, format);
+       if (format->version == -1)
+               clear_repository_format(format);
        return format->version;
 }
 
+void clear_repository_format(struct repository_format *format)
+{
+       string_list_clear(&format->unknown_extensions, 0);
+       free(format->work_tree);
+       free(format->partial_clone);
+       init_repository_format(format);
+}
+
 int verify_repository_format(const struct repository_format *format,
                             struct strbuf *err)
 {
@@ -784,7 +798,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
                set_git_dir(gitdir);
        inside_git_dir = 0;
        inside_work_tree = 1;
-       if (offset == cwd->len)
+       if (offset >= cwd->len)
                return NULL;
 
        /* Make "offset" point past the '/' (already the case for root dirs) */
@@ -831,16 +845,6 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
        return NULL;
 }
 
-static const char *setup_nongit(const char *cwd, int *nongit_ok)
-{
-       if (!nongit_ok)
-               die(_("not a git repository (or any of the parent directories): %s"), DEFAULT_GIT_DIR_ENVIRONMENT);
-       if (chdir(cwd))
-               die_errno(_("cannot come back to cwd"));
-       *nongit_ok = 1;
-       return NULL;
-}
-
 static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
 {
        struct stat buf;
@@ -916,7 +920,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
        const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
        struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
        const char *gitdirenv;
-       int ceil_offset = -1, min_offset = has_dos_drive_prefix(dir->buf) ? 3 : 1;
+       int ceil_offset = -1, min_offset = offset_1st_component(dir->buf);
        dev_t current_device = 0;
        int one_filesystem = 1;
 
@@ -944,6 +948,12 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
        if (ceil_offset < 0)
                ceil_offset = min_offset - 2;
 
+       if (min_offset && min_offset == dir->len &&
+           !is_dir_sep(dir->buf[min_offset - 1])) {
+               strbuf_addch(dir, '/');
+               min_offset++;
+       }
+
        /*
         * Test in the following order (relative to the dir):
         * - .git (file containing "gitdir: <path>")
@@ -1007,7 +1017,7 @@ int discover_git_directory(struct strbuf *commondir,
        struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
        size_t gitdir_offset = gitdir->len, cwd_len;
        size_t commondir_offset = commondir->len;
-       struct repository_format candidate;
+       struct repository_format candidate = REPOSITORY_FORMAT_INIT;
 
        if (strbuf_getcwd(&dir))
                return -1;
@@ -1044,9 +1054,11 @@ int discover_git_directory(struct strbuf *commondir,
                strbuf_release(&err);
                strbuf_setlen(commondir, commondir_offset);
                strbuf_setlen(gitdir, gitdir_offset);
+               clear_repository_format(&candidate);
                return -1;
        }
 
+       clear_repository_format(&candidate);
        return 0;
 }
 
@@ -1054,8 +1066,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
 {
        static struct strbuf cwd = STRBUF_INIT;
        struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT;
-       const char *prefix;
-       struct repository_format repo_fmt;
+       const char *prefix = NULL;
+       struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
 
        /*
         * We may have read an incomplete configuration before
@@ -1079,9 +1091,6 @@ const char *setup_git_directory_gently(int *nongit_ok)
        strbuf_addbuf(&dir, &cwd);
 
        switch (setup_git_directory_gently_1(&dir, &gitdir, 1)) {
-       case GIT_DIR_NONE:
-               prefix = NULL;
-               break;
        case GIT_DIR_EXPLICIT:
                prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok);
                break;
@@ -1097,29 +1106,51 @@ const char *setup_git_directory_gently(int *nongit_ok)
                prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok);
                break;
        case GIT_DIR_HIT_CEILING:
-               prefix = setup_nongit(cwd.buf, nongit_ok);
+               if (!nongit_ok)
+                       die(_("not a git repository (or any of the parent directories): %s"),
+                           DEFAULT_GIT_DIR_ENVIRONMENT);
+               *nongit_ok = 1;
                break;
        case GIT_DIR_HIT_MOUNT_POINT:
-               if (nongit_ok) {
-                       *nongit_ok = 1;
-                       strbuf_release(&cwd);
-                       strbuf_release(&dir);
-                       return NULL;
-               }
-               die(_("not a git repository (or any parent up to mount point %s)\n"
-                     "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
-                   dir.buf);
+               if (!nongit_ok)
+                       die(_("not a git repository (or any parent up to mount point %s)\n"
+                             "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
+                           dir.buf);
+               *nongit_ok = 1;
+               break;
+       case GIT_DIR_NONE:
+               /*
+                * As a safeguard against setup_git_directory_gently_1 returning
+                * this value, fallthrough to BUG. Otherwise it is possible to
+                * set startup_info->have_repository to 1 when we did nothing to
+                * find a repository.
+                */
        default:
                BUG("unhandled setup_git_directory_1() result");
        }
 
-       if (prefix)
-               setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
-       else
+       /*
+        * At this point, nongit_ok is stable. If it is non-NULL and points
+        * to a non-zero value, then this means that we haven't found a
+        * repository and that the caller expects startup_info to reflect
+        * this.
+        *
+        * Regardless of the state of nongit_ok, startup_info->prefix and
+        * the GIT_PREFIX environment variable must always match. For details
+        * see Documentation/config/alias.txt.
+        */
+       if (nongit_ok && *nongit_ok) {
+               startup_info->have_repository = 0;
+               startup_info->prefix = NULL;
                setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
-
-       startup_info->have_repository = !nongit_ok || !*nongit_ok;
-       startup_info->prefix = prefix;
+       } else {
+               startup_info->have_repository = 1;
+               startup_info->prefix = prefix;
+               if (prefix)
+                       setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
+               else
+                       setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
+       }
 
        /*
         * Not all paths through the setup code will call 'set_git_dir()' (which
@@ -1132,7 +1163,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
         * the user has set GIT_DIR.  It may be beneficial to disallow bogus
         * GIT_DIR values at some point in the future.
         */
-       if (startup_info->have_repository || getenv(GIT_DIR_ENVIRONMENT)) {
+       if (/* GIT_DIR_EXPLICIT, GIT_DIR_DISCOVERED, GIT_DIR_BARE */
+           startup_info->have_repository ||
+           /* GIT_DIR_EXPLICIT */
+           getenv(GIT_DIR_ENVIRONMENT)) {
                if (!the_repository->gitdir) {
                        const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
                        if (!gitdir)
@@ -1145,6 +1179,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
 
        strbuf_release(&dir);
        strbuf_release(&gitdir);
+       clear_repository_format(&repo_fmt);
 
        return prefix;
 }
@@ -1202,9 +1237,10 @@ int git_config_perm(const char *var, const char *value)
 
 void check_repository_format(void)
 {
-       struct repository_format repo_fmt;
+       struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
        check_repository_format_gently(get_git_dir(), &repo_fmt, NULL);
        startup_info->have_repository = 1;
+       clear_repository_format(&repo_fmt);
 }
 
 /*