diff --git a/cache.h b/cache.h index 83ba2d2b03..6e45c1b537 100644 --- a/cache.h +++ b/cache.h @@ -459,7 +459,7 @@ static inline enum object_type object_type(unsigned int mode) */ extern const char * const local_repo_env[]; -extern void setup_git_env(void); +extern void setup_git_env(const char *git_dir); /* * Returns true iff we have a configured git repository (either via diff --git a/common-main.c b/common-main.c index 6a689007e7..7d716d5a54 100644 --- a/common-main.c +++ b/common-main.c @@ -34,6 +34,8 @@ int main(int argc, const char **argv) git_setup_gettext(); + initialize_the_repository(); + attr_start(); git_extract_argv0_path(argv[0]); diff --git a/environment.c b/environment.c index d6dd64662c..21565c3c52 100644 --- a/environment.c +++ b/environment.c @@ -13,6 +13,7 @@ #include "refs.h" #include "fmt-merge-msg.h" #include "commit.h" +#include "argv-array.h" int trust_executable_bit = 1; int trust_ctime = 1; @@ -147,10 +148,35 @@ static char *expand_namespace(const char *raw_namespace) return strbuf_detach(&buf, NULL); } -void setup_git_env(void) +/* + * Wrapper of getenv() that returns a strdup value. This value is kept + * in argv to be freed later. + */ +static const char *getenv_safe(struct argv_array *argv, const char *name) +{ + const char *value = getenv(name); + + if (!value) + return NULL; + + argv_array_push(argv, value); + return argv->argv[argv->argc - 1]; +} + +void setup_git_env(const char *git_dir) { const char *shallow_file; const char *replace_ref_base; + struct set_gitdir_args args = { NULL }; + struct argv_array to_free = ARGV_ARRAY_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); + repo_set_gitdir(the_repository, git_dir, &args); + argv_array_clear(&to_free); if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT)) check_replace_refs = 0; @@ -300,8 +326,7 @@ int set_git_dir(const char *path) { if (setenv(GIT_DIR_ENVIRONMENT, path, 1)) return error("Could not set GIT_DIR to '%s'", path); - repo_set_gitdir(the_repository, path); - setup_git_env(); + setup_git_env(path); return 0; } diff --git a/repository.c b/repository.c index 4ffbe9bc94..62f52f47fc 100644 --- a/repository.c +++ b/repository.c @@ -4,64 +4,68 @@ #include "submodule-config.h" /* The main repository */ -static struct repository the_repo = { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &the_index, &hash_algos[GIT_HASH_SHA1], 0, 0 -}; -struct repository *the_repository = &the_repo; +static struct repository the_repo; +struct repository *the_repository; -static char *git_path_from_env(const char *envvar, const char *git_dir, - const char *path, int fromenv) +void initialize_the_repository(void) { - if (fromenv) { - const char *value = getenv(envvar); - if (value) - return xstrdup(value); - } + the_repository = &the_repo; - return xstrfmt("%s/%s", git_dir, path); + the_repo.index = &the_index; + repo_set_hash_algo(&the_repo, GIT_HASH_SHA1); } -static int find_common_dir(struct strbuf *sb, const char *gitdir, int fromenv) +static void expand_base_dir(char **out, const char *in, + const char *base_dir, const char *def_in) { - if (fromenv) { - const char *value = getenv(GIT_COMMON_DIR_ENVIRONMENT); - if (value) { - strbuf_addstr(sb, value); - return 1; - } - } - - return get_common_dir_noenv(sb, gitdir); + free(*out); + if (in) + *out = xstrdup(in); + else + *out = xstrfmt("%s/%s", base_dir, def_in); } -static void repo_setup_env(struct repository *repo) +static void repo_set_commondir(struct repository *repo, + const char *commondir) { struct strbuf sb = STRBUF_INIT; - repo->different_commondir = find_common_dir(&sb, repo->gitdir, - !repo->ignore_env); free(repo->commondir); + + if (commondir) { + repo->different_commondir = 1; + repo->commondir = xstrdup(commondir); + return; + } + + repo->different_commondir = get_common_dir_noenv(&sb, repo->gitdir); repo->commondir = strbuf_detach(&sb, NULL); - free(repo->objectdir); - repo->objectdir = git_path_from_env(DB_ENVIRONMENT, repo->commondir, - "objects", !repo->ignore_env); - free(repo->graft_file); - repo->graft_file = git_path_from_env(GRAFT_ENVIRONMENT, repo->commondir, - "info/grafts", !repo->ignore_env); - free(repo->index_file); - repo->index_file = git_path_from_env(INDEX_ENVIRONMENT, repo->gitdir, - "index", !repo->ignore_env); } -void repo_set_gitdir(struct repository *repo, const char *path) +void repo_set_gitdir(struct repository *repo, + const char *root, + const struct set_gitdir_args *o) { - const char *gitfile = read_gitfile(path); + const char *gitfile = read_gitfile(root); + /* + * repo->gitdir is saved because the caller could pass "root" + * that also points to repo->gitdir. We want to keep it alive + * until after xstrdup(root). Then we can free it. + */ char *old_gitdir = repo->gitdir; - repo->gitdir = xstrdup(gitfile ? gitfile : path); - repo_setup_env(repo); - + repo->gitdir = xstrdup(gitfile ? gitfile : root); free(old_gitdir); + + repo_set_commondir(repo, o->commondir); + expand_base_dir(&repo->objectdir, o->object_dir, + repo->commondir, "objects"); + free(repo->alternate_db); + repo->alternate_db = xstrdup_or_null(o->alternate_db); + expand_base_dir(&repo->graft_file, o->graft_file, + repo->commondir, "info/grafts"); + expand_base_dir(&repo->index_file, o->index_file, + repo->gitdir, "index"); } void repo_set_hash_algo(struct repository *repo, int hash_algo) @@ -79,6 +83,7 @@ static int repo_init_gitdir(struct repository *repo, const char *gitdir) int error = 0; char *abspath = NULL; const char *resolved_gitdir; + struct set_gitdir_args args = { NULL }; abspath = real_pathdup(gitdir, 0); if (!abspath) { @@ -93,7 +98,7 @@ static int repo_init_gitdir(struct repository *repo, const char *gitdir) goto out; } - repo_set_gitdir(repo, resolved_gitdir); + repo_set_gitdir(repo, resolved_gitdir, &args); out: free(abspath); @@ -128,13 +133,13 @@ static int read_and_verify_repository_format(struct repository_format *format, * Initialize 'repo' based on the provided 'gitdir'. * Return 0 upon success and a non-zero value upon failure. */ -int repo_init(struct repository *repo, const char *gitdir, const char *worktree) +static int repo_init(struct repository *repo, + const char *gitdir, + const char *worktree) { struct repository_format format; memset(repo, 0, sizeof(*repo)); - repo->ignore_env = 1; - if (repo_init_gitdir(repo, gitdir)) goto error; @@ -210,6 +215,7 @@ void repo_clear(struct repository *repo) FREE_AND_NULL(repo->gitdir); FREE_AND_NULL(repo->commondir); FREE_AND_NULL(repo->objectdir); + FREE_AND_NULL(repo->alternate_db); FREE_AND_NULL(repo->graft_file); FREE_AND_NULL(repo->index_file); FREE_AND_NULL(repo->worktree); diff --git a/repository.h b/repository.h index 0329e40c7f..f21fd93f72 100644 --- a/repository.h +++ b/repository.h @@ -26,6 +26,9 @@ struct repository { */ char *objectdir; + /* Path to extra alternate object database if not NULL */ + char *alternate_db; + /* * Path to the repository's graft file. * Cannot be NULL after initialization. @@ -72,15 +75,6 @@ struct repository { const struct git_hash_algo *hash_algo; /* Configurations */ - /* - * Bit used during initialization to indicate if repository state (like - * the location of the 'objectdir') should be read from the - * environment. By default this bit will be set at the begining of - * 'repo_init()' so that all repositories will ignore the environment. - * The exception to this is 'the_repository', which doesn't go through - * the normal 'repo_init()' process. - */ - unsigned ignore_env:1; /* Indicate if a repository has a different 'commondir' from 'gitdir' */ unsigned different_commondir:1; @@ -88,10 +82,24 @@ struct repository { extern struct repository *the_repository; -extern void repo_set_gitdir(struct repository *repo, const char *path); +/* + * Define a custom repository layout. Any field can be NULL, which + * will default back to the path according to the default layout. + */ +struct set_gitdir_args { + const char *commondir; + const char *object_dir; + const char *graft_file; + const char *index_file; + const char *alternate_db; +}; + +extern void repo_set_gitdir(struct repository *repo, + const char *root, + const struct set_gitdir_args *extra_args); extern void repo_set_worktree(struct repository *repo, const char *path); extern void repo_set_hash_algo(struct repository *repo, int algo); -extern int repo_init(struct repository *repo, const char *gitdir, const char *worktree); +extern void initialize_the_repository(void); extern int repo_submodule_init(struct repository *submodule, struct repository *superproject, const char *path); diff --git a/setup.c b/setup.c index 7287779642..664453fcef 100644 --- a/setup.c +++ b/setup.c @@ -1116,8 +1116,7 @@ const char *setup_git_directory_gently(int *nongit_ok) const char *gitdir = getenv(GIT_DIR_ENVIRONMENT); if (!gitdir) gitdir = DEFAULT_GIT_DIR_ENVIRONMENT; - repo_set_gitdir(the_repository, gitdir); - setup_git_env(); + setup_git_env(gitdir); } if (startup_info->have_repository) repo_set_hash_algo(the_repository, repo_fmt.hash_algo); diff --git a/sha1_file.c b/sha1_file.c index 1c61d9d927..aea9124a78 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -668,15 +668,11 @@ int foreach_alt_odb(alt_odb_fn fn, void *cb) void prepare_alt_odb(void) { - const char *alt; - if (alt_odb_tail) return; - alt = getenv(ALTERNATE_DB_ENVIRONMENT); - alt_odb_tail = &alt_odb_list; - link_alt_odb_entries(alt, PATH_SEP, NULL, 0); + link_alt_odb_entries(the_repository->alternate_db, PATH_SEP, NULL, 0); read_info_alternates(get_object_directory(), 0); }