packaging: Add contrib installation
[platform/upstream/git.git] / setup.c
diff --git a/setup.c b/setup.c
index e2a479a..c04cd25 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -32,6 +32,7 @@ static int abspath_part_inside_repo(char *path)
        char *path0;
        int off;
        const char *work_tree = get_git_work_tree();
+       struct strbuf realpath = STRBUF_INIT;
 
        if (!work_tree)
                return -1;
@@ -60,8 +61,10 @@ static int abspath_part_inside_repo(char *path)
                path++;
                if (*path == '/') {
                        *path = '\0';
-                       if (fspathcmp(real_path(path0), work_tree) == 0) {
+                       strbuf_realpath(&realpath, path0, 1);
+                       if (fspathcmp(realpath.buf, work_tree) == 0) {
                                memmove(path0, path + 1, len - (path - path0));
+                               strbuf_release(&realpath);
                                return 0;
                        }
                        *path = '/';
@@ -69,11 +72,14 @@ static int abspath_part_inside_repo(char *path)
        }
 
        /* check whole path */
-       if (fspathcmp(real_path(path0), work_tree) == 0) {
+       strbuf_realpath(&realpath, path0, 1);
+       if (fspathcmp(realpath.buf, work_tree) == 0) {
                *path0 = '\0';
+               strbuf_release(&realpath);
                return 0;
        }
 
+       strbuf_release(&realpath);
        return -1;
 }
 
@@ -120,8 +126,13 @@ char *prefix_path_gently(const char *prefix, int len,
 char *prefix_path(const char *prefix, int len, const char *path)
 {
        char *r = prefix_path_gently(prefix, len, NULL, path);
-       if (!r)
-               die(_("'%s' is outside repository"), path);
+       if (!r) {
+               const char *hint_path = get_git_work_tree();
+               if (!hint_path)
+                       hint_path = get_git_dir();
+               die(_("'%s' is outside repository at '%s'"), path,
+                   absolute_path(hint_path));
+       }
        return r;
 }
 
@@ -197,9 +208,26 @@ static void NORETURN die_verify_filename(struct repository *r,
  */
 static int looks_like_pathspec(const char *arg)
 {
-       /* anything with a wildcard character */
-       if (!no_wildcard(arg))
-               return 1;
+       const char *p;
+       int escaped = 0;
+
+       /*
+        * Wildcard characters imply the user is looking to match pathspecs
+        * that aren't in the filesystem. Note that this doesn't include
+        * backslash even though it's a glob special; by itself it doesn't
+        * cause any increase in the match. Likewise ignore backslash-escaped
+        * wildcard characters.
+        */
+       for (p = arg; *p; p++) {
+               if (escaped) {
+                       escaped = 0;
+               } else if (is_glob_special(*p)) {
+                       if (*p == '\\')
+                               escaped = 1;
+                       else
+                               return 1;
+               }
+       }
 
        /* long-form pathspec magic */
        if (starts_with(arg, ":("))
@@ -419,6 +447,63 @@ static int read_worktree_config(const char *var, const char *value, void *vdata)
        return 0;
 }
 
+enum extension_result {
+       EXTENSION_ERROR = -1, /* compatible with error(), etc */
+       EXTENSION_UNKNOWN = 0,
+       EXTENSION_OK = 1
+};
+
+/*
+ * Do not add new extensions to this function. It handles extensions which are
+ * respected even in v0-format repositories for historical compatibility.
+ */
+static enum extension_result handle_extension_v0(const char *var,
+                                                const char *value,
+                                                const char *ext,
+                                                struct repository_format *data)
+{
+               if (!strcmp(ext, "noop")) {
+                       return EXTENSION_OK;
+               } else if (!strcmp(ext, "preciousobjects")) {
+                       data->precious_objects = git_config_bool(var, value);
+                       return EXTENSION_OK;
+               } else if (!strcmp(ext, "partialclone")) {
+                       if (!value)
+                               return config_error_nonbool(var);
+                       data->partial_clone = xstrdup(value);
+                       return EXTENSION_OK;
+               } else if (!strcmp(ext, "worktreeconfig")) {
+                       data->worktree_config = git_config_bool(var, value);
+                       return EXTENSION_OK;
+               }
+
+               return EXTENSION_UNKNOWN;
+}
+
+/*
+ * Record any new extensions in this function.
+ */
+static enum extension_result handle_extension(const char *var,
+                                             const char *value,
+                                             const char *ext,
+                                             struct repository_format *data)
+{
+       if (!strcmp(ext, "noop-v1")) {
+               return EXTENSION_OK;
+       } else if (!strcmp(ext, "objectformat")) {
+               int format;
+
+               if (!value)
+                       return config_error_nonbool(var);
+               format = hash_algo_by_name(value);
+               if (format == GIT_HASH_UNKNOWN)
+                       return error("invalid value for 'extensions.objectformat'");
+               data->hash_algo = format;
+               return EXTENSION_OK;
+       }
+       return EXTENSION_UNKNOWN;
+}
+
 static int check_repo_format(const char *var, const char *value, void *vdata)
 {
        struct repository_format *data = vdata;
@@ -427,23 +512,25 @@ static int check_repo_format(const char *var, const char *value, void *vdata)
        if (strcmp(var, "core.repositoryformatversion") == 0)
                data->version = git_config_int(var, value);
        else if (skip_prefix(var, "extensions.", &ext)) {
-               /*
-                * record any known extensions here; otherwise,
-                * we fall through to recording it as unknown, and
-                * check_repository_format will complain
-                */
-               if (!strcmp(ext, "noop"))
-                       ;
-               else if (!strcmp(ext, "preciousobjects"))
-                       data->precious_objects = git_config_bool(var, value);
-               else if (!strcmp(ext, "partialclone")) {
-                       if (!value)
-                               return config_error_nonbool(var);
-                       data->partial_clone = xstrdup(value);
-               } else if (!strcmp(ext, "worktreeconfig"))
-                       data->worktree_config = git_config_bool(var, value);
-               else
+               switch (handle_extension_v0(var, value, ext, data)) {
+               case EXTENSION_ERROR:
+                       return -1;
+               case EXTENSION_OK:
+                       return 0;
+               case EXTENSION_UNKNOWN:
+                       break;
+               }
+
+               switch (handle_extension(var, value, ext, data)) {
+               case EXTENSION_ERROR:
+                       return -1;
+               case EXTENSION_OK:
+                       string_list_append(&data->v1_only_extensions, ext);
+                       return 0;
+               case EXTENSION_UNKNOWN:
                        string_list_append(&data->unknown_extensions, ext);
+                       return 0;
+               }
        }
 
        return read_worktree_config(var, value, vdata);
@@ -482,6 +569,7 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
        set_repository_format_partial_clone(candidate->partial_clone);
        repository_format_worktree_config = candidate->worktree_config;
        string_list_clear(&candidate->unknown_extensions, 0);
+       string_list_clear(&candidate->v1_only_extensions, 0);
 
        if (repository_format_worktree_config) {
                /*
@@ -510,6 +598,37 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
        return 0;
 }
 
+int upgrade_repository_format(int target_version)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct strbuf err = STRBUF_INIT;
+       struct strbuf repo_version = STRBUF_INIT;
+       struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
+
+       strbuf_git_common_path(&sb, the_repository, "config");
+       read_repository_format(&repo_fmt, sb.buf);
+       strbuf_release(&sb);
+
+       if (repo_fmt.version >= target_version)
+               return 0;
+
+       if (verify_repository_format(&repo_fmt, &err) < 0) {
+               error("cannot upgrade repository format from %d to %d: %s",
+                     repo_fmt.version, target_version, err.buf);
+               strbuf_release(&err);
+               return -1;
+       }
+       if (!repo_fmt.version && repo_fmt.unknown_extensions.nr)
+               return error("cannot upgrade repository format: "
+                            "unknown extension %s",
+                            repo_fmt.unknown_extensions.items[0].string);
+
+       strbuf_addf(&repo_version, "%d", target_version);
+       git_config_set("core.repositoryformatversion", repo_version.buf);
+       strbuf_release(&repo_version);
+       return 1;
+}
+
 static void init_repository_format(struct repository_format *format)
 {
        const struct repository_format fresh = REPOSITORY_FORMAT_INIT;
@@ -529,6 +648,7 @@ int read_repository_format(struct repository_format *format, const char *path)
 void clear_repository_format(struct repository_format *format)
 {
        string_list_clear(&format->unknown_extensions, 0);
+       string_list_clear(&format->v1_only_extensions, 0);
        free(format->work_tree);
        free(format->partial_clone);
        init_repository_format(format);
@@ -554,6 +674,18 @@ int verify_repository_format(const struct repository_format *format,
                return -1;
        }
 
+       if (format->version == 0 && format->v1_only_extensions.nr) {
+               int i;
+
+               strbuf_addstr(err,
+                             _("repo version is 0, but v1-only extensions found:"));
+
+               for (i = 0; i < format->v1_only_extensions.nr; i++)
+                       strbuf_addf(err, "\n\t%s",
+                                   format->v1_only_extensions.items[i].string);
+               return -1;
+       }
+
        return 0;
 }
 
@@ -601,6 +733,7 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
        struct stat st;
        int fd;
        ssize_t len;
+       static struct strbuf realpath = STRBUF_INIT;
 
        if (stat(path, &st)) {
                /* NEEDSWORK: discern between ENOENT vs other errors */
@@ -651,7 +784,9 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
                error_code = READ_GITFILE_ERR_NOT_A_REPO;
                goto cleanup_return;
        }
-       path = real_path(dir);
+
+       strbuf_realpath(&realpath, dir, 1);
+       path = realpath.buf;
 
 cleanup_return:
        if (return_error_code)
@@ -707,7 +842,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
                }
 
                /* #18, #26 */
-               set_git_dir(gitdirenv);
+               set_git_dir(gitdirenv, 0);
                free(gitfile);
                return NULL;
        }
@@ -729,7 +864,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
        }
        else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
                /* #16d */
-               set_git_dir(gitdirenv);
+               set_git_dir(gitdirenv, 0);
                free(gitfile);
                return NULL;
        }
@@ -741,14 +876,14 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
 
        /* both get_git_work_tree() and cwd are already normalized */
        if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */
-               set_git_dir(gitdirenv);
+               set_git_dir(gitdirenv, 0);
                free(gitfile);
                return NULL;
        }
 
        offset = dir_inside_of(cwd->buf, worktree);
        if (offset >= 0) {      /* cwd inside worktree? */
-               set_git_dir(real_path(gitdirenv));
+               set_git_dir(gitdirenv, 1);
                if (chdir(worktree))
                        die_errno(_("cannot chdir to '%s'"), worktree);
                strbuf_addch(cwd, '/');
@@ -757,7 +892,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
        }
 
        /* cwd outside worktree */
-       set_git_dir(gitdirenv);
+       set_git_dir(gitdirenv, 0);
        free(gitfile);
        return NULL;
 }
@@ -786,7 +921,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 == cwd->len ? gitdir : real_path(gitdir));
+               set_git_dir(gitdir, (offset != cwd->len));
                if (chdir(cwd->buf))
                        die_errno(_("cannot come back to cwd"));
                return NULL;
@@ -795,7 +930,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
        /* #0, #1, #5, #8, #9, #12, #13 */
        set_git_work_tree(".");
        if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
-               set_git_dir(gitdir);
+               set_git_dir(gitdir, 0);
        inside_git_dir = 0;
        inside_work_tree = 1;
        if (offset >= cwd->len)
@@ -838,10 +973,10 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
                        die_errno(_("cannot come back to cwd"));
                root_len = offset_1st_component(cwd->buf);
                strbuf_setlen(cwd, offset > root_len ? offset : root_len);
-               set_git_dir(cwd->buf);
+               set_git_dir(cwd->buf, 0);
        }
        else
-               set_git_dir(".");
+               set_git_dir(".", 0);
        return NULL;
 }
 
@@ -859,7 +994,7 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_
 
 /*
  * A "string_list_each_func_t" function that canonicalizes an entry
- * from GIT_CEILING_DIRECTORIES using real_path_if_valid(), or
+ * from GIT_CEILING_DIRECTORIES using real_pathdup(), or
  * discards it if unusable.  The presence of an empty entry in
  * GIT_CEILING_DIRECTORIES turns off canonicalization for all
  * subsequent entries.
@@ -1235,11 +1370,14 @@ int git_config_perm(const char *var, const char *value)
        return -(i & 0666);
 }
 
-void check_repository_format(void)
+void check_repository_format(struct repository_format *fmt)
 {
        struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
-       check_repository_format_gently(get_git_dir(), &repo_fmt, NULL);
+       if (!fmt)
+               fmt = &repo_fmt;
+       check_repository_format_gently(get_git_dir(), fmt, NULL);
        startup_info->have_repository = 1;
+       repo_set_hash_algo(the_repository, fmt->hash_algo);
        clear_repository_format(&repo_fmt);
 }