in src/repository.c [443:573]
static int find_repo(
git_buf *gitdir_path,
git_buf *workdir_path,
git_buf *gitlink_path,
git_buf *commondir_path,
const char *start_path,
uint32_t flags,
const char *ceiling_dirs)
{
git_buf path = GIT_BUF_INIT;
git_buf repo_link = GIT_BUF_INIT;
git_buf common_link = GIT_BUF_INIT;
struct stat st;
dev_t initial_device = 0;
int min_iterations;
bool in_dot_git, is_valid;
size_t ceiling_offset = 0;
int error;
git_buf_clear(gitdir_path);
error = git_path_prettify(&path, start_path, NULL);
if (error < 0)
return error;
/* 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;
}
for (;;) {
if (!(flags & GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
if (!in_dot_git) {
if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
goto out;
}
in_dot_git = !in_dot_git;
}
if (p_stat(path.ptr, &st) == 0) {
/* check that we have not crossed device boundaries */
if (initial_device == 0)
initial_device = st.st_dev;
else if (st.st_dev != initial_device &&
!(flags & GIT_REPOSITORY_OPEN_CROSS_FS))
break;
if (S_ISDIR(st.st_mode)) {
if ((error = is_valid_repository_path(&is_valid, &path, &common_link)) < 0)
goto out;
if (is_valid) {
if ((error = git_path_to_dir(&path)) < 0 ||
(error = git_buf_set(gitdir_path, path.ptr, path.size)) < 0)
goto out;
if (gitlink_path)
if ((error = git_buf_attach(gitlink_path, git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0)) < 0)
goto out;
if (commondir_path)
git_buf_swap(&common_link, commondir_path);
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)
goto out;
if (is_valid) {
git_buf_swap(gitdir_path, &repo_link);
if (gitlink_path)
if ((error = git_buf_put(gitlink_path, path.ptr, path.size)) < 0)
goto out;
if (commondir_path)
git_buf_swap(&common_link, commondir_path);
}
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). */
if ((error = git_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. */
if (min_iterations && (--min_iterations == 0))
ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
/* Check if we should stop searching here. */
if (min_iterations == 0 &&
(path.ptr[ceiling_offset] == 0 || (flags & GIT_REPOSITORY_OPEN_NO_SEARCH)))
break;
}
if (workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
if (!git_buf_len(gitdir_path))
git_buf_clear(workdir_path);
else if ((error = git_path_dirname_r(workdir_path, path.ptr)) < 0 ||
(error = git_path_to_dir(workdir_path)) < 0)
goto out;
}
/* If we didn't find the repository, and we don't have any other error
* to report, report that. */
if (!git_buf_len(gitdir_path)) {
git_error_set(GIT_ERROR_REPOSITORY, "could not find repository from '%s'", start_path);
error = GIT_ENOTFOUND;
goto out;
}
out:
git_buf_dispose(&path);
git_buf_dispose(&repo_link);
git_buf_dispose(&common_link);
return error;
}