diff options
Diffstat (limited to 'src/repository.c')
-rw-r--r-- | src/repository.c | 214 |
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; |