aboutsummaryrefslogtreecommitdiff
path: root/setup.c
diff options
context:
space:
mode:
Diffstat (limited to 'setup.c')
-rw-r--r--setup.c377
1 files changed, 286 insertions, 91 deletions
diff --git a/setup.c b/setup.c
index 7086741e6c..7ec4427368 100644
--- a/setup.c
+++ b/setup.c
@@ -22,11 +22,9 @@
#include "chdir-notify.h"
#include "path.h"
#include "quote.h"
-#include "tmp-objdir.h"
#include "trace.h"
#include "trace2.h"
#include "worktree.h"
-#include "exec-cmd.h"
static int inside_git_dir = -1;
static int inside_work_tree = -1;
@@ -633,6 +631,21 @@ static enum extension_result handle_extension_v0(const char *var,
return EXTENSION_UNKNOWN;
}
+static void parse_reference_uri(const char *value, char **format,
+ char **payload)
+{
+ const char *schema_end;
+
+ schema_end = strstr(value, "://");
+ if (!schema_end) {
+ *format = xstrdup(value);
+ *payload = NULL;
+ } else {
+ *format = xstrndup(value, schema_end - value);
+ *payload = xstrdup_or_null(schema_end + 3);
+ }
+}
+
/*
* Record any new extensions in this function.
*/
@@ -675,10 +688,17 @@ static enum extension_result handle_extension(const char *var,
return EXTENSION_OK;
} else if (!strcmp(ext, "refstorage")) {
unsigned int format;
+ char *format_str;
if (!value)
return config_error_nonbool(var);
- format = ref_storage_format_by_name(value);
+
+ parse_reference_uri(value, &format_str,
+ &data->ref_storage_payload);
+
+ format = ref_storage_format_by_name(format_str);
+ free(format_str);
+
if (format == REF_STORAGE_FORMAT_UNKNOWN)
return error(_("invalid value for '%s': '%s'"),
"extensions.refstorage", value);
@@ -687,6 +707,9 @@ static enum extension_result handle_extension(const char *var,
} else if (!strcmp(ext, "relativeworktrees")) {
data->relative_worktrees = git_config_bool(var, value);
return EXTENSION_OK;
+ } else if (!strcmp(ext, "submodulepathconfig")) {
+ data->submodule_path_cfg = git_config_bool(var, value);
+ return EXTENSION_OK;
}
return EXTENSION_UNKNOWN;
}
@@ -851,6 +874,7 @@ void clear_repository_format(struct repository_format *format)
string_list_clear(&format->v1_only_extensions, 0);
free(format->work_tree);
free(format->partial_clone);
+ free(format->ref_storage_payload);
init_repository_format(format);
}
@@ -896,8 +920,10 @@ int verify_repository_format(const struct repository_format *format,
void read_gitfile_error_die(int error_code, const char *path, const char *dir)
{
switch (error_code) {
- case READ_GITFILE_ERR_STAT_FAILED:
case READ_GITFILE_ERR_NOT_A_FILE:
+ case READ_GITFILE_ERR_STAT_FAILED:
+ case READ_GITFILE_ERR_MISSING:
+ case READ_GITFILE_ERR_IS_A_DIR:
/* non-fatal; follow return path */
break;
case READ_GITFILE_ERR_OPEN_FAILED:
@@ -940,8 +966,14 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
static struct strbuf realpath = STRBUF_INIT;
if (stat(path, &st)) {
- /* NEEDSWORK: discern between ENOENT vs other errors */
- error_code = READ_GITFILE_ERR_STAT_FAILED;
+ if (errno == ENOENT || errno == ENOTDIR)
+ error_code = READ_GITFILE_ERR_MISSING;
+ else
+ error_code = READ_GITFILE_ERR_STAT_FAILED;
+ goto cleanup_return;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ error_code = READ_GITFILE_ERR_IS_A_DIR;
goto cleanup_return;
}
if (!S_ISREG(st.st_mode)) {
@@ -1002,6 +1034,83 @@ cleanup_return:
return error_code ? NULL : path;
}
+static void setup_git_env_internal(const char *git_dir,
+ bool skip_initializing_odb)
+{
+ char *git_replace_ref_base;
+ const char *shallow_file;
+ const char *replace_ref_base;
+ struct set_gitdir_args args = { NULL };
+ struct strvec to_free = STRVEC_INIT;
+
+ args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);
+ args.object_dir = getenv_safe(&to_free, DB_ENVIRONMENT);
+ args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
+ args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
+ args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
+ if (getenv(GIT_QUARANTINE_ENVIRONMENT))
+ args.disable_ref_updates = true;
+ args.skip_initializing_odb = skip_initializing_odb;
+
+ repo_set_gitdir(the_repository, git_dir, &args);
+ strvec_clear(&to_free);
+
+ if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
+ disable_replace_refs();
+ replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
+ git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
+ : "refs/replace/");
+ update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);
+
+ shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
+ if (shallow_file)
+ set_alternate_shallow_file(the_repository, shallow_file, 0);
+
+ if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
+ fetch_if_missing = 0;
+}
+
+void setup_git_env(const char *git_dir)
+{
+ setup_git_env_internal(git_dir, false);
+}
+
+static void set_git_dir_1(const char *path, bool skip_initializing_odb)
+{
+ xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
+ setup_git_env_internal(path, skip_initializing_odb);
+}
+
+static void update_relative_gitdir(const char *name UNUSED,
+ const char *old_cwd,
+ const char *new_cwd,
+ void *data UNUSED)
+{
+ char *path = reparent_relative_path(old_cwd, new_cwd,
+ repo_get_git_dir(the_repository));
+ trace_printf_key(&trace_setup_key,
+ "setup: move $GIT_DIR to '%s'",
+ path);
+ set_git_dir_1(path, true);
+ free(path);
+}
+
+static void set_git_dir(const char *path, int make_realpath)
+{
+ struct strbuf realpath = STRBUF_INIT;
+
+ if (make_realpath) {
+ strbuf_realpath(&realpath, path, 1);
+ path = realpath.buf;
+ }
+
+ set_git_dir_1(path, false);
+ if (!is_absolute_path(path))
+ chdir_notify_register(NULL, update_relative_gitdir, NULL);
+
+ strbuf_release(&realpath);
+}
+
static const char *setup_explicit_git_dir(const char *gitdirenv,
struct strbuf *cwd,
struct repository_format *repo_fmt,
@@ -1248,7 +1357,7 @@ static int safe_directory_cb(const char *key, const char *value,
} else {
char *allowed = NULL;
- if (!git_config_pathname(&allowed, key, value)) {
+ if (!git_config_pathname(&allowed, key, value) && allowed) {
char *normalized = NULL;
/*
@@ -1500,20 +1609,37 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
if (offset > min_offset)
strbuf_addch(dir, '/');
strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);
- gitdirenv = read_gitfile_gently(dir->buf, die_on_error ?
- NULL : &error_code);
+ gitdirenv = read_gitfile_gently(dir->buf, &error_code);
if (!gitdirenv) {
- if (die_on_error ||
- error_code == READ_GITFILE_ERR_NOT_A_FILE) {
- /* NEEDSWORK: fail if .git is not file nor dir */
+ switch (error_code) {
+ case READ_GITFILE_ERR_MISSING:
+ /* no .git in this directory, move on */
+ break;
+ case READ_GITFILE_ERR_IS_A_DIR:
if (is_git_directory(dir->buf)) {
gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
gitdir_path = xstrdup(dir->buf);
}
- } else if (error_code != READ_GITFILE_ERR_STAT_FAILED)
- return GIT_DIR_INVALID_GITFILE;
- } else
+ break;
+ case READ_GITFILE_ERR_STAT_FAILED:
+ if (die_on_error)
+ die(_("error reading '%s'"), dir->buf);
+ else
+ return GIT_DIR_INVALID_GITFILE;
+ case READ_GITFILE_ERR_NOT_A_FILE:
+ if (die_on_error)
+ die(_("not a regular file: '%s'"), dir->buf);
+ else
+ return GIT_DIR_INVALID_GITFILE;
+ default:
+ if (die_on_error)
+ read_gitfile_error_die(error_code, dir->buf, NULL);
+ else
+ return GIT_DIR_INVALID_GITFILE;
+ }
+ } else {
gitfile = xstrdup(dir->buf);
+ }
/*
* Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT
* to check that directory for a repository.
@@ -1628,79 +1754,85 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
return result;
}
-void setup_git_env(const char *git_dir)
+const char *enter_repo(const char *path, unsigned flags)
{
- char *git_replace_ref_base;
- const char *shallow_file;
- const char *replace_ref_base;
- struct set_gitdir_args args = { NULL };
- struct strvec to_free = STRVEC_INIT;
-
- args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);
- args.object_dir = getenv_safe(&to_free, DB_ENVIRONMENT);
- args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
- args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
- args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
- if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
- args.disable_ref_updates = 1;
- }
-
- repo_set_gitdir(the_repository, git_dir, &args);
- strvec_clear(&to_free);
-
- if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
- disable_replace_refs();
- replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
- git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
- : "refs/replace/");
- update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);
-
- shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
- if (shallow_file)
- set_alternate_shallow_file(the_repository, shallow_file, 0);
-
- if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
- fetch_if_missing = 0;
-}
+ static struct strbuf validated_path = STRBUF_INIT;
+ static struct strbuf used_path = STRBUF_INIT;
-static void set_git_dir_1(const char *path)
-{
- xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
- setup_git_env(path);
-}
+ if (!path)
+ return NULL;
-static void update_relative_gitdir(const char *name UNUSED,
- const char *old_cwd,
- const char *new_cwd,
- void *data UNUSED)
-{
- char *path = reparent_relative_path(old_cwd, new_cwd,
- repo_get_git_dir(the_repository));
- struct tmp_objdir *tmp_objdir = tmp_objdir_unapply_primary_odb();
+ if (!(flags & ENTER_REPO_STRICT)) {
+ static const char *suffix[] = {
+ "/.git", "", ".git/.git", ".git", NULL,
+ };
+ const char *gitfile;
+ int len = strlen(path);
+ int i;
+ while ((1 < len) && (path[len-1] == '/'))
+ len--;
- trace_printf_key(&trace_setup_key,
- "setup: move $GIT_DIR to '%s'",
- path);
- set_git_dir_1(path);
- if (tmp_objdir)
- tmp_objdir_reapply_primary_odb(tmp_objdir, old_cwd, new_cwd);
- free(path);
-}
+ /*
+ * We can handle arbitrary-sized buffers, but this remains as a
+ * sanity check on untrusted input.
+ */
+ if (PATH_MAX <= len)
+ return NULL;
-void set_git_dir(const char *path, int make_realpath)
-{
- struct strbuf realpath = STRBUF_INIT;
+ strbuf_reset(&used_path);
+ strbuf_reset(&validated_path);
+ strbuf_add(&used_path, path, len);
+ strbuf_add(&validated_path, path, len);
- if (make_realpath) {
- strbuf_realpath(&realpath, path, 1);
- path = realpath.buf;
+ if (used_path.buf[0] == '~') {
+ char *newpath = interpolate_path(used_path.buf, 0);
+ if (!newpath)
+ return NULL;
+ strbuf_attach(&used_path, newpath, strlen(newpath),
+ strlen(newpath));
+ }
+ for (i = 0; suffix[i]; i++) {
+ struct stat st;
+ size_t baselen = used_path.len;
+ strbuf_addstr(&used_path, suffix[i]);
+ if (!stat(used_path.buf, &st) &&
+ (S_ISREG(st.st_mode) ||
+ (S_ISDIR(st.st_mode) && is_git_directory(used_path.buf)))) {
+ strbuf_addstr(&validated_path, suffix[i]);
+ break;
+ }
+ strbuf_setlen(&used_path, baselen);
+ }
+ if (!suffix[i])
+ return NULL;
+ gitfile = read_gitfile(used_path.buf);
+ if (!(flags & ENTER_REPO_ANY_OWNER_OK))
+ die_upon_dubious_ownership(gitfile, NULL, used_path.buf);
+ if (gitfile) {
+ strbuf_reset(&used_path);
+ strbuf_addstr(&used_path, gitfile);
+ }
+ if (chdir(used_path.buf))
+ return NULL;
+ path = validated_path.buf;
+ }
+ else {
+ const char *gitfile = read_gitfile(path);
+ if (!(flags & ENTER_REPO_ANY_OWNER_OK))
+ die_upon_dubious_ownership(gitfile, NULL, path);
+ if (gitfile)
+ path = gitfile;
+ if (chdir(path))
+ return NULL;
}
- set_git_dir_1(path);
- if (!is_absolute_path(path))
- chdir_notify_register(NULL, update_relative_gitdir, NULL);
+ if (is_git_directory(".")) {
+ set_git_dir(".", 0);
+ check_repository_format(NULL);
+ return path;
+ }
- strbuf_release(&realpath);
+ return NULL;
}
static int git_work_tree_initialized;
@@ -1733,6 +1865,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
static struct strbuf cwd = STRBUF_INIT;
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT;
const char *prefix = NULL;
+ const char *ref_backend_uri;
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
/*
@@ -1860,11 +1993,14 @@ const char *setup_git_directory_gently(int *nongit_ok)
repo_set_compat_hash_algo(the_repository,
repo_fmt.compat_hash_algo);
repo_set_ref_storage_format(the_repository,
- repo_fmt.ref_storage_format);
+ repo_fmt.ref_storage_format,
+ repo_fmt.ref_storage_payload);
the_repository->repository_format_worktree_config =
repo_fmt.worktree_config;
the_repository->repository_format_relative_worktrees =
repo_fmt.relative_worktrees;
+ the_repository->repository_format_submodule_path_cfg =
+ repo_fmt.submodule_path_cfg;
/* take ownership of repo_fmt.partial_clone */
the_repository->repository_format_partial_clone =
repo_fmt.partial_clone;
@@ -1889,6 +2025,25 @@ const char *setup_git_directory_gently(int *nongit_ok)
setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
}
+ /*
+ * The env variable should override the repository config
+ * for 'extensions.refStorage'.
+ */
+ ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT);
+ if (ref_backend_uri) {
+ char *backend, *payload;
+ enum ref_storage_format format;
+
+ parse_reference_uri(ref_backend_uri, &backend, &payload);
+ format = ref_storage_format_by_name(backend);
+ if (format == REF_STORAGE_FORMAT_UNKNOWN)
+ die(_("unknown ref storage format: '%s'"), backend);
+ repo_set_ref_storage_format(the_repository, format, payload);
+
+ free(backend);
+ free(payload);
+ }
+
setup_original_cwd();
strbuf_release(&dir);
@@ -1960,9 +2115,12 @@ void check_repository_format(struct repository_format *fmt)
repo_set_hash_algo(the_repository, fmt->hash_algo);
repo_set_compat_hash_algo(the_repository, fmt->compat_hash_algo);
repo_set_ref_storage_format(the_repository,
- fmt->ref_storage_format);
+ fmt->ref_storage_format,
+ fmt->ref_storage_payload);
the_repository->repository_format_worktree_config =
fmt->worktree_config;
+ the_repository->repository_format_submodule_path_cfg =
+ fmt->submodule_path_cfg;
the_repository->repository_format_relative_worktrees =
fmt->relative_worktrees;
the_repository->repository_format_partial_clone =
@@ -2221,6 +2379,7 @@ void initialize_repository_version(int hash_algo,
{
struct strbuf repo_version = STRBUF_INIT;
int target_version = GIT_REPO_VERSION;
+ int default_submodule_path_config = 0;
/*
* Note that we initialize the repository version to 1 when the ref
@@ -2230,7 +2389,8 @@ void initialize_repository_version(int hash_algo,
* the remote repository's format.
*/
if (hash_algo != GIT_HASH_SHA1_LEGACY ||
- ref_storage_format != REF_STORAGE_FORMAT_FILES)
+ ref_storage_format != REF_STORAGE_FORMAT_FILES ||
+ the_repository->ref_storage_payload)
target_version = GIT_REPO_VERSION_READ;
if (hash_algo != GIT_HASH_SHA1_LEGACY && hash_algo != GIT_HASH_UNKNOWN)
@@ -2239,11 +2399,20 @@ void initialize_repository_version(int hash_algo,
else if (reinit)
repo_config_set_gently(the_repository, "extensions.objectformat", NULL);
- if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
+ if (the_repository->ref_storage_payload) {
+ struct strbuf ref_uri = STRBUF_INIT;
+
+ strbuf_addf(&ref_uri, "%s://%s",
+ ref_storage_format_to_name(ref_storage_format),
+ the_repository->ref_storage_payload);
+ repo_config_set(the_repository, "extensions.refstorage", ref_uri.buf);
+ strbuf_release(&ref_uri);
+ } else if (ref_storage_format != REF_STORAGE_FORMAT_FILES) {
repo_config_set(the_repository, "extensions.refstorage",
ref_storage_format_to_name(ref_storage_format));
- else if (reinit)
+ } else if (reinit) {
repo_config_set_gently(the_repository, "extensions.refstorage", NULL);
+ }
if (reinit) {
struct strbuf config = STRBUF_INIT;
@@ -2259,6 +2428,15 @@ void initialize_repository_version(int hash_algo,
clear_repository_format(&repo_fmt);
}
+ repo_config_get_bool(the_repository, "init.defaultSubmodulePathConfig",
+ &default_submodule_path_config);
+ if (default_submodule_path_config) {
+ /* extensions.submodulepathconfig requires at least version 1 */
+ if (target_version == 0)
+ target_version = 1;
+ repo_config_set(the_repository, "extensions.submodulepathconfig", "true");
+ }
+
strbuf_addf(&repo_version, "%d", target_version);
repo_config_set(the_repository, "core.repositoryformatversion", repo_version.buf);
@@ -2277,14 +2455,12 @@ static int is_reinit(void)
return ret;
}
-void create_reference_database(enum ref_storage_format ref_storage_format,
- const char *initial_branch, int quiet)
+void create_reference_database(const char *initial_branch, int quiet)
{
struct strbuf err = STRBUF_INIT;
char *to_free = NULL;
int reinit = is_reinit();
- repo_set_ref_storage_format(the_repository, ref_storage_format);
if (ref_store_create_on_disk(get_main_ref_store(the_repository), 0, &err))
die("failed to set up refs db: %s", err.buf);
@@ -2518,6 +2694,7 @@ static void repository_format_configure(struct repository_format *repo_fmt,
.ignore_repo = 1,
.ignore_worktree = 1,
};
+ const char *ref_backend_uri;
const char *env;
config_with_options(read_default_format_config, &cfg, NULL, NULL, &opts);
@@ -2563,7 +2740,26 @@ static void repository_format_configure(struct repository_format *repo_fmt,
} else {
repo_fmt->ref_storage_format = REF_STORAGE_FORMAT_DEFAULT;
}
- repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format);
+
+
+ ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT);
+ if (ref_backend_uri) {
+ char *backend, *payload;
+ enum ref_storage_format format;
+
+ parse_reference_uri(ref_backend_uri, &backend, &payload);
+ format = ref_storage_format_by_name(backend);
+ if (format == REF_STORAGE_FORMAT_UNKNOWN)
+ die(_("unknown ref storage format: '%s'"), backend);
+
+ repo_fmt->ref_storage_format = format;
+ repo_fmt->ref_storage_payload = payload;
+
+ free(backend);
+ }
+
+ repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format,
+ repo_fmt->ref_storage_payload);
}
int init_db(const char *git_dir, const char *real_git_dir,
@@ -2611,7 +2807,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
* have set up the repository format such that we can evaluate
* includeIf conditions correctly in the case of re-initialization.
*/
- repo_config(the_repository, platform_core_config, NULL);
+ repo_config(the_repository, git_default_core_config, NULL);
safe_create_dir(the_repository, git_dir, 0);
@@ -2619,8 +2815,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
&repo_fmt, init_shared_repository);
if (!(flags & INIT_DB_SKIP_REFDB))
- create_reference_database(repo_fmt.ref_storage_format,
- initial_branch, flags & INIT_DB_QUIET);
+ create_reference_database(initial_branch, flags & INIT_DB_QUIET);
create_object_directory();
if (repo_settings_get_shared_repository(the_repository)) {