Unverified Commit 51f3e0a4 by Edward Thomson Committed by GitHub

Merge pull request #6544 from libgit2/ethomson/repo_env

repo: honor environment variables for more scenarios
parents 2f20fe88 389f9b10
......@@ -66,7 +66,7 @@ static const struct {
static int check_repositoryformatversion(int *version, git_config *config);
static int check_extensions(git_config *config, int version);
static int load_global_config(git_config **config);
static int load_global_config(git_config **config, bool use_env);
static int load_objectformat(git_repository *repo, git_config *config);
#define GIT_COMMONDIR_FILE "commondir"
......@@ -191,11 +191,23 @@ void git_repository_free(git_repository *repo)
}
/* Check if we have a separate commondir (e.g. we have a worktree) */
static int lookup_commondir(bool *separate, git_str *commondir, git_str *repository_path)
static int lookup_commondir(
bool *separate,
git_str *commondir,
git_str *repository_path,
uint32_t flags)
{
git_str common_link = GIT_STR_INIT;
int error;
/* Environment variable overrides configuration */
if ((flags & GIT_REPOSITORY_OPEN_FROM_ENV)) {
error = git__getenv(commondir, "GIT_COMMON_DIR");
if (!error || error != GIT_ENOTFOUND)
goto done;
}
/*
* If there's no commondir file, the repository path is the
* common path, but it needs a trailing slash.
......@@ -222,12 +234,11 @@ static int lookup_commondir(bool *separate, git_str *commondir, git_str *reposit
git_str_swap(commondir, &common_link);
}
git_str_dispose(&common_link);
/* Make sure the commondir path always has a trailing slash */
error = git_fs_path_prettify_dir(commondir, commondir->ptr, NULL);
done:
git_str_dispose(&common_link);
return error;
}
......@@ -252,14 +263,19 @@ GIT_INLINE(int) validate_repo_path(git_str *path)
*
* Open a repository object from its path
*/
static int is_valid_repository_path(bool *out, git_str *repository_path, git_str *common_path)
static int is_valid_repository_path(
bool *out,
git_str *repository_path,
git_str *common_path,
uint32_t flags)
{
bool separate_commondir = false;
int error;
*out = false;
if ((error = lookup_commondir(&separate_commondir, common_path, repository_path)) < 0)
if ((error = lookup_commondir(&separate_commondir,
common_path, repository_path, flags)) < 0)
return error;
/* Ensure HEAD file exists */
......@@ -337,20 +353,43 @@ static int load_config_data(git_repository *repo, const git_config *config)
return 0;
}
static int load_workdir(git_repository *repo, git_config *config, git_str *parent_path)
static int load_workdir(
git_repository *repo,
git_config *config,
git_str *parent_path)
{
int error;
git_config_entry *ce;
git_config_entry *ce = NULL;
git_str worktree = GIT_STR_INIT;
git_str path = GIT_STR_INIT;
git_str workdir_env = GIT_STR_INIT;
const char *value = NULL;
int error;
if (repo->is_bare)
return 0;
if ((error = git_config__lookup_entry(
&ce, config, "core.worktree", false)) < 0)
/* Environment variables are preferred */
if (repo->use_env) {
error = git__getenv(&workdir_env, "GIT_WORK_TREE");
if (error == 0)
value = workdir_env.ptr;
else if (error == GIT_ENOTFOUND)
error = 0;
else
goto cleanup;
}
/* Examine configuration values if necessary */
if (!value) {
if ((error = git_config__lookup_entry(&ce, config,
"core.worktree", false)) < 0)
return error;
if (ce && ce->value)
value = ce->value;
}
if (repo->is_worktree) {
char *gitlink = git_worktree__read_link(repo->gitdir, GIT_GITDIR_FILE);
if (!gitlink) {
......@@ -367,17 +406,21 @@ static int load_workdir(git_repository *repo, git_config *config, git_str *paren
}
repo->workdir = git_str_detach(&worktree);
} else if (value) {
if (!*value) {
git_error_set(GIT_ERROR_NET, "working directory cannot be set to empty path");
error = -1;
goto cleanup;
}
else if (ce && ce->value) {
if ((error = git_fs_path_prettify_dir(
&worktree, ce->value, repo->gitdir)) < 0)
if ((error = git_fs_path_prettify_dir(&worktree,
value, repo->gitdir)) < 0)
goto cleanup;
repo->workdir = git_str_detach(&worktree);
}
else if (parent_path && git_fs_path_isdir(parent_path->ptr))
} else if (parent_path && git_fs_path_isdir(parent_path->ptr)) {
repo->workdir = git_str_detach(parent_path);
else {
} else {
if (git_fs_path_dirname_r(&worktree, repo->gitdir) < 0 ||
git_fs_path_to_dir(&worktree) < 0) {
error = -1;
......@@ -388,8 +431,10 @@ static int load_workdir(git_repository *repo, git_config *config, git_str *paren
}
GIT_ERROR_CHECK_ALLOC(repo->workdir);
cleanup:
git_str_dispose(&path);
git_str_dispose(&workdir_env);
git_config_entry_free(ce);
return error;
}
......@@ -541,7 +586,10 @@ static int validate_ownership_cb(const git_config_entry *entry, void *payload)
return 0;
}
static int validate_ownership_config(bool *is_safe, const char *path)
static int validate_ownership_config(
bool *is_safe,
const char *path,
bool use_env)
{
validate_ownership_data ownership_data = {
path, GIT_STR_INIT, is_safe
......@@ -549,7 +597,7 @@ static int validate_ownership_config(bool *is_safe, const char *path)
git_config *config;
int error;
if (load_global_config(&config) != 0)
if (load_global_config(&config, use_env) != 0)
return 0;
error = git_config_get_multivar_foreach(config,
......@@ -623,7 +671,8 @@ static int validate_ownership(git_repository *repo)
}
if (is_safe ||
(error = validate_ownership_config(&is_safe, validation_paths[0])) < 0)
(error = validate_ownership_config(
&is_safe, validation_paths[0], repo->use_env)) < 0)
goto done;
if (!is_safe) {
......@@ -637,14 +686,28 @@ done:
return error;
}
static int find_repo(
git_str *gitdir_path,
git_str *workdir_path,
git_str *gitlink_path,
git_str *commondir_path,
struct repo_paths {
git_str gitdir;
git_str workdir;
git_str gitlink;
git_str commondir;
};
#define REPO_PATHS_INIT { GIT_STR_INIT }
GIT_INLINE(void) repo_paths_dispose(struct repo_paths *paths)
{
git_str_dispose(&paths->gitdir);
git_str_dispose(&paths->workdir);
git_str_dispose(&paths->gitlink);
git_str_dispose(&paths->commondir);
}
static int find_repo_traverse(
struct repo_paths *out,
const char *start_path,
uint32_t flags,
const char *ceiling_dirs)
const char *ceiling_dirs,
uint32_t flags)
{
git_str path = GIT_STR_INIT;
git_str repo_link = GIT_STR_INIT;
......@@ -656,19 +719,23 @@ static int find_repo(
size_t ceiling_offset = 0;
int error;
git_str_clear(gitdir_path);
git_str_clear(&out->gitdir);
error = git_fs_path_prettify(&path, start_path, NULL);
if (error < 0)
if ((error = git_fs_path_prettify(&path, start_path, NULL)) < 0)
return error;
/* in_dot_git toggles each loop:
/*
* In each loop we look first for a `.git` dir within the
* directory, then to see if the directory itself is a repo.
*
* In other words: if we start in /a/b/c, then we look at:
* /a/b/c/.git, /a/b/c, /a/b/.git, /a/b, /a/.git, /a
* With GIT_REPOSITORY_OPEN_BARE or GIT_REPOSITORY_OPEN_NO_DOTGIT, we
* assume we started with /a/b/c.git and don't append .git the first
* time through.
* min_iterations indicates the number of iterations left before going
* further counts as a search. */
*
* With GIT_REPOSITORY_OPEN_BARE or GIT_REPOSITORY_OPEN_NO_DOTGIT,
* we assume we started with /a/b/c.git and don't append .git the
* first time through. min_iterations indicates the number of
* iterations left before going further counts as a search.
*/
if (flags & (GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
in_dot_git = true;
min_iterations = 1;
......@@ -695,48 +762,51 @@ static int find_repo(
break;
if (S_ISDIR(st.st_mode)) {
if ((error = is_valid_repository_path(&is_valid, &path, &common_link)) < 0)
if ((error = is_valid_repository_path(&is_valid, &path, &common_link, flags)) < 0)
goto out;
if (is_valid) {
if ((error = git_fs_path_to_dir(&path)) < 0 ||
(error = git_str_set(gitdir_path, path.ptr, path.size)) < 0)
(error = git_str_set(&out->gitdir, path.ptr, path.size)) < 0)
goto out;
if (gitlink_path)
if ((error = git_str_attach(gitlink_path, git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0)) < 0)
if ((error = git_str_attach(&out->gitlink, git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0)) < 0)
goto out;
if (commondir_path)
git_str_swap(&common_link, commondir_path);
git_str_swap(&common_link, &out->commondir);
break;
}
} else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) {
if ((error = read_gitfile(&repo_link, path.ptr)) < 0 ||
(error = is_valid_repository_path(&is_valid, &repo_link, &common_link)) < 0)
(error = is_valid_repository_path(&is_valid, &repo_link, &common_link, flags)) < 0)
goto out;
if (is_valid) {
git_str_swap(gitdir_path, &repo_link);
git_str_swap(&out->gitdir, &repo_link);
if (gitlink_path)
if ((error = git_str_put(gitlink_path, path.ptr, path.size)) < 0)
if ((error = git_str_put(&out->gitlink, path.ptr, path.size)) < 0)
goto out;
if (commondir_path)
git_str_swap(&common_link, commondir_path);
git_str_swap(&common_link, &out->commondir);
}
break;
}
}
/* Move up one directory. If we're in_dot_git, we'll search the
* parent itself next. If we're !in_dot_git, we'll search .git
* in the parent directory next (added at the top of the loop). */
/*
* Move up one directory. If we're in_dot_git, we'll
* search the parent itself next. If we're !in_dot_git,
* we'll search .git in the parent directory next (added
* at the top of the loop).
*/
if ((error = git_fs_path_dirname_r(&path, path.ptr)) < 0)
goto out;
/* Once we've checked the directory (and .git if applicable),
* find the ceiling for a search. */
/*
* Once we've checked the directory (and .git if
* applicable), find the ceiling for a search.
*/
if (min_iterations && (--min_iterations == 0))
ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
......@@ -746,29 +816,96 @@ static int find_repo(
break;
}
if (workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
if (!git_str_len(gitdir_path))
git_str_clear(workdir_path);
else if ((error = git_fs_path_dirname_r(workdir_path, path.ptr)) < 0 ||
(error = git_fs_path_to_dir(workdir_path)) < 0)
if (!(flags & GIT_REPOSITORY_OPEN_BARE)) {
if (!git_str_len(&out->gitdir))
git_str_clear(&out->workdir);
else if ((error = git_fs_path_dirname_r(&out->workdir, path.ptr)) < 0 ||
(error = git_fs_path_to_dir(&out->workdir)) < 0)
goto out;
}
/* If we didn't find the repository, and we don't have any other error
* to report, report that. */
if (!git_str_len(gitdir_path)) {
git_error_set(GIT_ERROR_REPOSITORY, "could not find repository from '%s'", start_path);
/* If we didn't find the repository, and we don't have any other
* error to report, report that. */
if (!git_str_len(&out->gitdir)) {
git_error_set(GIT_ERROR_REPOSITORY, "could not find repository at '%s'", start_path);
error = GIT_ENOTFOUND;
goto out;
}
out:
if (error)
repo_paths_dispose(out);
git_str_dispose(&path);
git_str_dispose(&repo_link);
git_str_dispose(&common_link);
return error;
}
static int find_repo(
struct repo_paths *out,
const char *start_path,
const char *ceiling_dirs,
uint32_t flags)
{
bool use_env = !!(flags & GIT_REPOSITORY_OPEN_FROM_ENV);
git_str gitdir_buf = GIT_STR_INIT,
ceiling_dirs_buf = GIT_STR_INIT,
across_fs_buf = GIT_STR_INIT;
int error;
if (use_env && !start_path) {
error = git__getenv(&gitdir_buf, "GIT_DIR");
if (!error) {
start_path = gitdir_buf.ptr;
flags |= GIT_REPOSITORY_OPEN_NO_SEARCH;
flags |= GIT_REPOSITORY_OPEN_NO_DOTGIT;
} else if (error == GIT_ENOTFOUND) {
start_path = ".";
} else {
goto done;
}
}
if (use_env && !ceiling_dirs) {
error = git__getenv(&ceiling_dirs_buf,
"GIT_CEILING_DIRECTORIES");
if (!error)
ceiling_dirs = ceiling_dirs_buf.ptr;
else if (error != GIT_ENOTFOUND)
goto done;
}
if (use_env) {
error = git__getenv(&across_fs_buf,
"GIT_DISCOVERY_ACROSS_FILESYSTEM");
if (!error) {
int across_fs = 0;
if ((error = git_config_parse_bool(&across_fs,
git_str_cstr(&across_fs_buf))) < 0)
goto done;
if (across_fs)
flags |= GIT_REPOSITORY_OPEN_CROSS_FS;
} else if (error != GIT_ENOTFOUND) {
goto done;
}
}
error = find_repo_traverse(out, start_path, ceiling_dirs, flags);
done:
git_str_dispose(&gitdir_buf);
git_str_dispose(&ceiling_dirs_buf);
git_str_dispose(&across_fs_buf);
return error;
}
static int obtain_config_and_set_oid_type(
git_config **config_ptr,
git_repository *repo)
......@@ -817,7 +954,7 @@ int git_repository_open_bare(
git_config *config;
if ((error = git_fs_path_prettify_dir(&path, bare_path, NULL)) < 0 ||
(error = is_valid_repository_path(&is_valid, &path, &common_path)) < 0)
(error = is_valid_repository_path(&is_valid, &path, &common_path, 0)) < 0)
return error;
if (!is_valid) {
......@@ -851,173 +988,22 @@ cleanup:
return error;
}
static int _git_repository_open_ext_from_env(
git_repository **out,
const char *start_path)
static int repo_load_namespace(git_repository *repo)
{
git_repository *repo = NULL;
git_index *index = NULL;
git_odb *odb = NULL;
git_str dir_buf = GIT_STR_INIT;
git_str ceiling_dirs_buf = GIT_STR_INIT;
git_str across_fs_buf = GIT_STR_INIT;
git_str index_file_buf = GIT_STR_INIT;
git_str namespace_buf = GIT_STR_INIT;
git_str object_dir_buf = GIT_STR_INIT;
git_str alts_buf = GIT_STR_INIT;
git_str work_tree_buf = GIT_STR_INIT;
git_str common_dir_buf = GIT_STR_INIT;
const char *ceiling_dirs = NULL;
unsigned flags = 0;
int error;
if (!start_path) {
error = git__getenv(&dir_buf, "GIT_DIR");
if (error == GIT_ENOTFOUND) {
git_error_clear();
start_path = ".";
} else if (error < 0)
goto error;
else {
start_path = git_str_cstr(&dir_buf);
flags |= GIT_REPOSITORY_OPEN_NO_SEARCH;
flags |= GIT_REPOSITORY_OPEN_NO_DOTGIT;
}
}
error = git__getenv(&ceiling_dirs_buf, "GIT_CEILING_DIRECTORIES");
if (error == GIT_ENOTFOUND)
git_error_clear();
else if (error < 0)
goto error;
else
ceiling_dirs = git_str_cstr(&ceiling_dirs_buf);
error = git__getenv(&across_fs_buf, "GIT_DISCOVERY_ACROSS_FILESYSTEM");
if (error == GIT_ENOTFOUND)
git_error_clear();
else if (error < 0)
goto error;
else {
int across_fs = 0;
error = git_config_parse_bool(&across_fs, git_str_cstr(&across_fs_buf));
if (error < 0)
goto error;
if (across_fs)
flags |= GIT_REPOSITORY_OPEN_CROSS_FS;
}
error = git__getenv(&index_file_buf, "GIT_INDEX_FILE");
if (error == GIT_ENOTFOUND)
git_error_clear();
else if (error < 0)
goto error;
else {
error = git_index_open(&index, git_str_cstr(&index_file_buf));
if (error < 0)
goto error;
}
if (!repo->use_env)
return 0;
error = git__getenv(&namespace_buf, "GIT_NAMESPACE");
if (error == GIT_ENOTFOUND)
git_error_clear();
else if (error < 0)
goto error;
error = git__getenv(&object_dir_buf, "GIT_OBJECT_DIRECTORY");
if (error == GIT_ENOTFOUND)
git_error_clear();
else if (error < 0)
goto error;
else {
error = git_odb__open(&odb, git_str_cstr(&object_dir_buf), NULL);
if (error < 0)
goto error;
}
error = git__getenv(&work_tree_buf, "GIT_WORK_TREE");
if (error == GIT_ENOTFOUND)
git_error_clear();
else if (error < 0)
goto error;
else {
git_error_set(GIT_ERROR_INVALID, "GIT_WORK_TREE unimplemented");
error = GIT_ERROR;
goto error;
}
error = git__getenv(&work_tree_buf, "GIT_COMMON_DIR");
if (error == GIT_ENOTFOUND)
git_error_clear();
else if (error < 0)
goto error;
else {
git_error_set(GIT_ERROR_INVALID, "GIT_COMMON_DIR unimplemented");
error = GIT_ERROR;
goto error;
}
error = git_repository_open_ext(&repo, start_path, flags, ceiling_dirs);
if (error < 0)
goto error;
if (odb)
git_repository_set_odb(repo, odb);
error = git__getenv(&alts_buf, "GIT_ALTERNATE_OBJECT_DIRECTORIES");
if (error == GIT_ENOTFOUND) {
git_error_clear();
error = 0;
} else if (error < 0)
goto error;
else {
const char *end;
char *alt, *sep;
if (!odb) {
error = git_repository_odb(&odb, repo);
if (error < 0)
goto error;
}
end = git_str_cstr(&alts_buf) + git_str_len(&alts_buf);
for (sep = alt = alts_buf.ptr; sep != end; alt = sep+1) {
for (sep = alt; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++)
;
if (*sep)
*sep = '\0';
error = git_odb_add_disk_alternate(odb, alt);
if (error < 0)
goto error;
}
}
if (git_str_len(&namespace_buf)) {
error = git_repository_set_namespace(repo, git_str_cstr(&namespace_buf));
if (error < 0)
goto error;
}
git_repository_set_index(repo, index);
if (out) {
*out = repo;
goto success;
}
error:
git_repository_free(repo);
success:
git_odb_free(odb);
git_index_free(index);
git_str_dispose(&common_dir_buf);
git_str_dispose(&work_tree_buf);
git_str_dispose(&alts_buf);
git_str_dispose(&object_dir_buf);
git_str_dispose(&namespace_buf);
git_str_dispose(&index_file_buf);
git_str_dispose(&across_fs_buf);
git_str_dispose(&ceiling_dirs_buf);
git_str_dispose(&dir_buf);
if (error == 0)
repo->namespace = git_str_detach(&namespace_buf);
else if (error != GIT_ENOTFOUND)
return error;
return 0;
}
static int repo_is_worktree(unsigned *out, const git_repository *repo)
......@@ -1049,21 +1035,16 @@ int git_repository_open_ext(
unsigned int flags,
const char *ceiling_dirs)
{
int error;
unsigned is_worktree;
git_str gitdir = GIT_STR_INIT, workdir = GIT_STR_INIT,
gitlink = GIT_STR_INIT, commondir = GIT_STR_INIT;
struct repo_paths paths = { GIT_STR_INIT };
git_repository *repo = NULL;
git_config *config = NULL;
if (flags & GIT_REPOSITORY_OPEN_FROM_ENV)
return _git_repository_open_ext_from_env(repo_ptr, start_path);
unsigned is_worktree;
int error;
if (repo_ptr)
*repo_ptr = NULL;
error = find_repo(
&gitdir, &workdir, &gitlink, &commondir, start_path, flags, ceiling_dirs);
error = find_repo(&paths, start_path, ceiling_dirs, flags);
if (error < 0 || !repo_ptr)
goto cleanup;
......@@ -1071,20 +1052,23 @@ int git_repository_open_ext(
repo = repository_alloc();
GIT_ERROR_CHECK_ALLOC(repo);
repo->gitdir = git_str_detach(&gitdir);
repo->use_env = !!(flags & GIT_REPOSITORY_OPEN_FROM_ENV);
repo->gitdir = git_str_detach(&paths.gitdir);
GIT_ERROR_CHECK_ALLOC(repo->gitdir);
if (gitlink.size) {
repo->gitlink = git_str_detach(&gitlink);
if (paths.gitlink.size) {
repo->gitlink = git_str_detach(&paths.gitlink);
GIT_ERROR_CHECK_ALLOC(repo->gitlink);
}
if (commondir.size) {
repo->commondir = git_str_detach(&commondir);
if (paths.commondir.size) {
repo->commondir = git_str_detach(&paths.commondir);
GIT_ERROR_CHECK_ALLOC(repo->commondir);
}
if ((error = repo_is_worktree(&is_worktree, repo)) < 0)
goto cleanup;
repo->is_worktree = is_worktree;
error = obtain_config_and_set_oid_type(&config, repo);
......@@ -1096,10 +1080,13 @@ int git_repository_open_ext(
} else {
if (config &&
((error = load_config_data(repo, config)) < 0 ||
(error = load_workdir(repo, config, &workdir)) < 0))
(error = load_workdir(repo, config, &paths.workdir)) < 0))
goto cleanup;
}
if ((error = repo_load_namespace(repo)) < 0)
goto cleanup;
/*
* Ensure that the git directory and worktree are
* owned by the current user.
......@@ -1109,10 +1096,7 @@ int git_repository_open_ext(
goto cleanup;
cleanup:
git_str_dispose(&gitdir);
git_str_dispose(&workdir);
git_str_dispose(&gitlink);
git_str_dispose(&commondir);
repo_paths_dispose(&paths);
git_config_free(config);
if (error < 0)
......@@ -1180,11 +1164,17 @@ int git_repository_discover(
int across_fs,
const char *ceiling_dirs)
{
struct repo_paths paths = { GIT_STR_INIT };
uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
int error;
GIT_ASSERT_ARG(start_path);
GIT_BUF_WRAP_PRIVATE(out, find_repo, NULL, NULL, NULL, start_path, flags, ceiling_dirs);
if ((error = find_repo(&paths, start_path, ceiling_dirs, flags)) == 0)
error = git_buf_fromstr(out, &paths.gitdir);
repo_paths_dispose(&paths);
return error;
}
static int load_config(
......@@ -1255,23 +1245,70 @@ static const char *path_unless_empty(git_str *buf)
return git_str_len(buf) > 0 ? git_str_cstr(buf) : NULL;
}
GIT_INLINE(int) config_path_system(git_str *out, bool use_env)
{
if (use_env) {
git_str no_system_buf = GIT_STR_INIT;
int no_system = 0;
int error;
error = git__getenv(&no_system_buf, "GIT_CONFIG_NOSYSTEM");
if (error && error != GIT_ENOTFOUND)
return error;
error = git_config_parse_bool(&no_system, no_system_buf.ptr);
git_str_dispose(&no_system_buf);
if (no_system)
return 0;
error = git__getenv(out, "GIT_CONFIG_SYSTEM");
if (error == 0 || error != GIT_ENOTFOUND)
return 0;
}
git_config__find_system(out);
return 0;
}
GIT_INLINE(int) config_path_global(git_str *out, bool use_env)
{
if (use_env) {
int error = git__getenv(out, "GIT_CONFIG_GLOBAL");
if (error == 0 || error != GIT_ENOTFOUND)
return 0;
}
git_config__find_global(out);
return 0;
}
int git_repository_config__weakptr(git_config **out, git_repository *repo)
{
int error = 0;
if (repo->_config == NULL) {
git_str system_buf = GIT_STR_INIT;
git_str global_buf = GIT_STR_INIT;
git_str xdg_buf = GIT_STR_INIT;
git_str system_buf = GIT_STR_INIT;
git_str programdata_buf = GIT_STR_INIT;
bool use_env = repo->use_env;
git_config *config;
git_config__find_global(&global_buf);
if (!(error = config_path_system(&system_buf, use_env)) &&
!(error = config_path_global(&global_buf, use_env))) {
git_config__find_xdg(&xdg_buf);
git_config__find_system(&system_buf);
git_config__find_programdata(&programdata_buf);
}
/* If there is no global file, open a backend for it anyway */
if (!error) {
/*
* If there is no global file, open a backend
* for it anyway.
*/
if (git_str_len(&global_buf) == 0)
git_config__global_location(&global_buf);
......@@ -1281,6 +1318,8 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo)
path_unless_empty(&xdg_buf),
path_unless_empty(&system_buf),
path_unless_empty(&programdata_buf));
}
if (!error) {
GIT_REFCOUNT_OWN(config, repo);
......@@ -1329,6 +1368,56 @@ int git_repository_set_config(git_repository *repo, git_config *config)
return 0;
}
static int repository_odb_path(git_str *out, git_repository *repo)
{
int error = GIT_ENOTFOUND;
if (repo->use_env)
error = git__getenv(out, "GIT_OBJECT_DIRECTORY");
if (error == GIT_ENOTFOUND)
error = git_repository__item_path(out, repo,
GIT_REPOSITORY_ITEM_OBJECTS);
return error;
}
static int repository_odb_alternates(
git_odb *odb,
git_repository *repo)
{
git_str alternates = GIT_STR_INIT;
char *sep, *alt;
int error;
if (!repo->use_env)
return 0;
error = git__getenv(&alternates, "GIT_ALTERNATE_OBJECT_DIRECTORIES");
if (error != 0)
return (error == GIT_ENOTFOUND) ? 0 : error;
alt = alternates.ptr;
while (*alt) {
sep = strchr(alt, GIT_PATH_LIST_SEPARATOR);
if (sep)
*sep = '\0';
error = git_odb_add_disk_alternate(odb, alt);
if (sep)
alt = sep + 1;
else
break;
}
git_str_dispose(&alternates);
return 0;
}
int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
{
int error = 0;
......@@ -1344,9 +1433,9 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
odb_opts.oid_type = repo->oid_type;
if ((error = git_repository__item_path(&odb_path, repo,
GIT_REPOSITORY_ITEM_OBJECTS)) < 0 ||
(error = git_odb__new(&odb, &odb_opts)) < 0)
if ((error = repository_odb_path(&odb_path, repo)) < 0 ||
(error = git_odb__new(&odb, &odb_opts)) < 0 ||
(error = repository_odb_alternates(odb, repo)) < 0)
return error;
GIT_REFCOUNT_OWN(odb, repo);
......@@ -1430,6 +1519,20 @@ int git_repository_set_refdb(git_repository *repo, git_refdb *refdb)
return 0;
}
static int repository_index_path(git_str *out, git_repository *repo)
{
int error = GIT_ENOTFOUND;
if (repo->use_env)
error = git__getenv(out, "GIT_INDEX_FILE");
if (error == GIT_ENOTFOUND)
error = git_repository__item_path(out, repo,
GIT_REPOSITORY_ITEM_INDEX);
return error;
}
int git_repository_index__weakptr(git_index **out, git_repository *repo)
{
int error = 0;
......@@ -1441,7 +1544,7 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo)
git_str index_path = GIT_STR_INIT;
git_index *index;
if ((error = git_str_joinpath(&index_path, repo->gitdir, GIT_INDEX_FILE)) < 0)
if ((error = repository_index_path(&index_path, repo)) < 0)
return error;
error = git_index_open(&index, index_path.ptr);
......@@ -1916,7 +2019,7 @@ static bool is_filesystem_case_insensitive(const char *gitdir_path)
* Return a configuration object with only the global and system
* configurations; no repository-level configuration.
*/
static int load_global_config(git_config **config)
static int load_global_config(git_config **config, bool use_env)
{
git_str global_buf = GIT_STR_INIT;
git_str xdg_buf = GIT_STR_INIT;
......@@ -1924,9 +2027,9 @@ static int load_global_config(git_config **config)
git_str programdata_buf = GIT_STR_INIT;
int error;
git_config__find_global(&global_buf);
if (!(error = config_path_system(&system_buf, use_env)) &&
!(error = config_path_global(&global_buf, use_env))) {
git_config__find_xdg(&xdg_buf);
git_config__find_system(&system_buf);
git_config__find_programdata(&programdata_buf);
error = load_config(config, NULL,
......@@ -1934,6 +2037,7 @@ static int load_global_config(git_config **config)
path_unless_empty(&xdg_buf),
path_unless_empty(&system_buf),
path_unless_empty(&programdata_buf));
}
git_str_dispose(&global_buf);
git_str_dispose(&xdg_buf);
......@@ -1943,7 +2047,7 @@ static int load_global_config(git_config **config)
return error;
}
static bool are_symlinks_supported(const char *wd_path)
static bool are_symlinks_supported(const char *wd_path, bool use_env)
{
git_config *config = NULL;
int symlinks = 0;
......@@ -1956,10 +2060,12 @@ static bool are_symlinks_supported(const char *wd_path)
* _not_ set, then we do not test or enable symlink support.
*/
#ifdef GIT_WIN32
if (load_global_config(&config) < 0 ||
if (load_global_config(&config, use_env) < 0 ||
git_config_get_bool(&symlinks, config, "core.symlinks") < 0 ||
!symlinks)
goto done;
#else
GIT_UNUSED(use_env);
#endif
if (!(symlinks = git_fs_path_supports_symlinks(wd_path)))
......@@ -2032,7 +2138,8 @@ static int repo_init_fs_configs(
const char *cfg_path,
const char *repo_dir,
const char *work_dir,
bool update_ignorecase)
bool update_ignorecase,
bool use_env)
{
int error = 0;
......@@ -2043,7 +2150,7 @@ static int repo_init_fs_configs(
cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
return error;
if (!are_symlinks_supported(work_dir)) {
if (!are_symlinks_supported(work_dir, use_env)) {
if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
return error;
} else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
......@@ -2080,6 +2187,7 @@ static int repo_init_config(
git_config *config = NULL;
bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
bool use_env = ((flags & GIT_REPOSITORY_OPEN_FROM_ENV) != 0);
int version = GIT_REPO_VERSION_DEFAULT;
if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
......@@ -2100,7 +2208,8 @@ static int repo_init_config(
SET_REPO_CONFIG(int32, "core.repositoryformatversion", version);
if ((error = repo_init_fs_configs(
config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
config, cfg_path.ptr, repo_dir, work_dir,
!is_reinit, use_env)) < 0)
goto cleanup;
if (!is_bare) {
......@@ -2164,8 +2273,8 @@ int git_repository_reinit_filesystem(git_repository *repo, int recurse)
const char *repo_dir = git_repository_path(repo);
if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
error = repo_init_fs_configs(
config, path.ptr, repo_dir, git_repository_workdir(repo), true);
error = repo_init_fs_configs(config, path.ptr, repo_dir,
git_repository_workdir(repo), true, repo->use_env);
git_config_free(config);
git_str_dispose(&path);
......@@ -2634,7 +2743,7 @@ int git_repository_init_ext(
wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_str_cstr(&wd_path);
if ((error = is_valid_repository_path(&is_valid, &repo_path, &common_path)) < 0)
if ((error = is_valid_repository_path(&is_valid, &repo_path, &common_path, opts->flags)) < 0)
goto out;
if (is_valid) {
......
......@@ -151,8 +151,9 @@ struct git_repository {
git_array_t(git_str) reserved_names;
unsigned is_bare:1;
unsigned is_worktree:1;
unsigned use_env:1,
is_bare:1,
is_worktree:1;
git_oid_t oid_type;
unsigned int lru_counter;
......
......@@ -122,7 +122,10 @@ void test_repo_discover__cleanup(void)
void test_repo_discover__discovering_repo_with_exact_path_succeeds(void)
{
cl_git_pass(git_repository_discover(&discovered, DISCOVER_FOLDER, 0, ceiling_dirs.ptr));
git_buf_dispose(&discovered);
cl_git_pass(git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs.ptr));
git_buf_dispose(&discovered);
}
void test_repo_discover__discovering_nonexistent_dir_fails(void)
......
......@@ -31,6 +31,10 @@ void test_repo_env__cleanup(void)
if (git_fs_path_isdir("peeled.git"))
git_futils_rmdir_r("peeled.git", NULL, GIT_RMDIR_REMOVE_FILES);
cl_fixture_cleanup("test_workdir");
cl_fixture_cleanup("test_global_conf");
cl_fixture_cleanup("test_system_conf");
clear_git_env();
}
......@@ -275,3 +279,91 @@ void test_repo_env__open(void)
clear_git_env();
}
void test_repo_env__work_tree(void)
{
git_repository *repo;
const char *test_path;
cl_fixture_sandbox("attr");
cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
cl_must_pass(p_mkdir("test_workdir", 0777));
test_path = cl_git_sandbox_path(1, "test_workdir", NULL);
cl_setenv("GIT_WORK_TREE", test_path);
cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
cl_assert_equal_s(test_path, git_repository_workdir(repo));
git_repository_free(repo);
cl_setenv("GIT_WORK_TREE", NULL);
}
void test_repo_env__commondir(void)
{
git_repository *repo;
const char *test_path;
cl_fixture_sandbox("attr");
cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
cl_fixture_sandbox("testrepo.git");
cl_git_pass(p_rename("testrepo.git", "test_commondir"));
test_path = cl_git_sandbox_path(1, "test_commondir", NULL);
cl_setenv("GIT_COMMON_DIR", test_path);
cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
cl_assert_equal_s(test_path, git_repository_commondir(repo));
git_repository_free(repo);
cl_setenv("GIT_COMMON_DIR", NULL);
}
void test_repo_env__config(void)
{
git_repository *repo;
git_config *config;
const char *system_path, *global_path;
int s, g;
cl_fixture_sandbox("attr");
cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
cl_git_rewritefile("test_system_conf", "[tttest]\n\tsys = true\n");
cl_git_rewritefile("test_global_conf", "[tttest]\n\tglb = true\n");
system_path = cl_git_sandbox_path(0, "test_system_conf", NULL);
cl_setenv("GIT_CONFIG_SYSTEM", system_path);
global_path = cl_git_sandbox_path(0, "test_global_conf", NULL);
cl_setenv("GIT_CONFIG_GLOBAL", global_path);
/* Ensure we can override the system and global files */
cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_get_bool(&s, config, "tttest.sys"));
cl_assert_equal_i(1, s);
cl_git_pass(git_config_get_bool(&g, config, "tttest.glb"));
cl_assert_equal_i(1, g);
git_config_free(config);
git_repository_free(repo);
/* Further ensure we can ignore the system file. */
cl_setenv("GIT_CONFIG_NOSYSTEM", "TrUe");
cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
cl_git_pass(git_repository_config(&config, repo));
cl_git_fail_with(GIT_ENOTFOUND, git_config_get_bool(&s, config, "tttest.sys"));
cl_git_pass(git_config_get_bool(&g, config, "tttest.glb"));
cl_assert_equal_i(1, g);
git_config_free(config);
git_repository_free(repo);
cl_setenv("GIT_CONFIG_NOSYSTEM", NULL);
cl_setenv("GIT_CONFIG_SYSTEM", NULL);
cl_setenv("GIT_CONFIG_GLOBAL", NULL);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment