summaryrefslogtreecommitdiff
path: root/src/repository.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/repository.c')
-rw-r--r--src/repository.c214
1 files changed, 197 insertions, 17 deletions
diff --git a/src/repository.c b/src/repository.c
index d39a9015d..635b13b84 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -359,7 +359,8 @@ static int find_repo(
git_buf path = GIT_BUF_INIT;
struct stat st;
dev_t initial_device = 0;
- bool try_with_dot_git = ((flags & GIT_REPOSITORY_OPEN_BARE) != 0);
+ int min_iterations;
+ bool in_dot_git;
int ceiling_offset;
git_buf_free(repo_path);
@@ -367,13 +368,30 @@ static int find_repo(
if ((error = git_path_prettify(&path, start_path, NULL)) < 0)
return error;
- ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
+ /* in_dot_git toggles each loop:
+ * /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. */
+ if (flags & (GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
+ in_dot_git = true;
+ min_iterations = 1;
+ } else {
+ in_dot_git = false;
+ min_iterations = 2;
+ }
- if (!try_with_dot_git &&
- (error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
- return error;
+ while (!error && (min_iterations || !(path.ptr[ceiling_offset] == 0 ||
+ (flags & GIT_REPOSITORY_OPEN_NO_SEARCH)))) {
+ if (!(flags & GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
+ if (!in_dot_git)
+ if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
+ break;
+ in_dot_git = !in_dot_git;
+ }
- while (!error && !git_buf_len(repo_path)) {
if (p_stat(path.ptr, &st) == 0) {
/* check that we have not crossed device boundaries */
if (initial_device == 0)
@@ -414,17 +432,10 @@ static int find_repo(
break;
}
- if (try_with_dot_git) {
- /* if we tried original dir with and without .git AND either hit
- * directory ceiling or NO_SEARCH was requested, then be done.
- */
- if (path.ptr[ceiling_offset] == '\0' ||
- (flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0)
- break;
- /* otherwise look first for .git item */
- error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
- }
- try_with_dot_git = !try_with_dot_git;
+ /* 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);
}
if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
@@ -480,6 +491,172 @@ int git_repository_open_bare(
return 0;
}
+static int _git_repository_open_ext_from_env(
+ git_repository **out,
+ const char *start_path)
+{
+ git_repository *repo = NULL;
+ git_index *index = NULL;
+ git_odb *odb = NULL;
+ git_buf dir_buf = GIT_BUF_INIT;
+ git_buf ceiling_dirs_buf = GIT_BUF_INIT;
+ git_buf across_fs_buf = GIT_BUF_INIT;
+ git_buf index_file_buf = GIT_BUF_INIT;
+ git_buf namespace_buf = GIT_BUF_INIT;
+ git_buf object_dir_buf = GIT_BUF_INIT;
+ git_buf alts_buf = GIT_BUF_INIT;
+ git_buf work_tree_buf = GIT_BUF_INIT;
+ git_buf common_dir_buf = GIT_BUF_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) {
+ giterr_clear();
+ start_path = ".";
+ } else if (error < 0)
+ goto error;
+ else {
+ start_path = git_buf_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)
+ giterr_clear();
+ else if (error < 0)
+ goto error;
+ else
+ ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
+
+ error = git__getenv(&across_fs_buf, "GIT_DISCOVERY_ACROSS_FILESYSTEM");
+ if (error == GIT_ENOTFOUND)
+ giterr_clear();
+ else if (error < 0)
+ goto error;
+ else {
+ int across_fs = 0;
+ error = git_config_parse_bool(&across_fs, git_buf_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)
+ giterr_clear();
+ else if (error < 0)
+ goto error;
+ else {
+ error = git_index_open(&index, git_buf_cstr(&index_file_buf));
+ if (error < 0)
+ goto error;
+ }
+
+ error = git__getenv(&namespace_buf, "GIT_NAMESPACE");
+ if (error == GIT_ENOTFOUND)
+ giterr_clear();
+ else if (error < 0)
+ goto error;
+
+ error = git__getenv(&object_dir_buf, "GIT_OBJECT_DIRECTORY");
+ if (error == GIT_ENOTFOUND)
+ giterr_clear();
+ else if (error < 0)
+ goto error;
+ else {
+ error = git_odb_open(&odb, git_buf_cstr(&object_dir_buf));
+ if (error < 0)
+ goto error;
+ }
+
+ error = git__getenv(&work_tree_buf, "GIT_WORK_TREE");
+ if (error == GIT_ENOTFOUND)
+ giterr_clear();
+ else if (error < 0)
+ goto error;
+ else {
+ giterr_set(GITERR_INVALID, "GIT_WORK_TREE unimplemented");
+ error = GIT_ERROR;
+ goto error;
+ }
+
+ error = git__getenv(&work_tree_buf, "GIT_COMMON_DIR");
+ if (error == GIT_ENOTFOUND)
+ giterr_clear();
+ else if (error < 0)
+ goto error;
+ else {
+ giterr_set(GITERR_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)
+ giterr_clear();
+ 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_buf_cstr(&alts_buf) + git_buf_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;
+ }
+ }
+
+ error = git_repository_set_namespace(repo, git_buf_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_buf_free(&common_dir_buf);
+ git_buf_free(&work_tree_buf);
+ git_buf_free(&alts_buf);
+ git_buf_free(&object_dir_buf);
+ git_buf_free(&namespace_buf);
+ git_buf_free(&index_file_buf);
+ git_buf_free(&across_fs_buf);
+ git_buf_free(&ceiling_dirs_buf);
+ git_buf_free(&dir_buf);
+ return error;
+}
+
int git_repository_open_ext(
git_repository **repo_ptr,
const char *start_path,
@@ -492,6 +669,9 @@ int git_repository_open_ext(
git_repository *repo;
git_config *config = NULL;
+ if (flags & GIT_REPOSITORY_OPEN_FROM_ENV)
+ return _git_repository_open_ext_from_env(repo_ptr, start_path);
+
if (repo_ptr)
*repo_ptr = NULL;