summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJosh Triplett <josh@joshtriplett.org>2016-04-03 19:24:15 -0700
committerJosh Triplett <josh@joshtriplett.org>2016-06-24 11:44:01 -0700
commited577134a5dcf30c95b740859aceebd9c1137157 (patch)
tree25543c84c8126fa37f30aa72b086fab37cef1ad4 /src
parent37e9278220095435d986e7412d2a97097dab3221 (diff)
downloadlibgit2-ed577134a5dcf30c95b740859aceebd9c1137157.tar.gz
Fix repository discovery with ceiling_dirs at current directory
git only checks ceiling directories when its search ascends to a parent directory. A ceiling directory matching the starting directory will not prevent git from finding a repository in the starting directory or a parent directory. libgit2 handled the former case correctly, but differed from git in the latter case: given a ceiling directory matching the starting directory, but no repository at the starting directory, libgit2 would stop the search at that point rather than finding a repository in a parent directory. Test case using git command-line tools: /tmp$ git init x Initialized empty Git repository in /tmp/x/.git/ /tmp$ cd x/ /tmp/x$ mkdir subdir /tmp/x$ cd subdir/ /tmp/x/subdir$ GIT_CEILING_DIRECTORIES=/tmp/x git rev-parse --git-dir fatal: Not a git repository (or any of the parent directories): .git /tmp/x/subdir$ GIT_CEILING_DIRECTORIES=/tmp/x/subdir git rev-parse --git-dir /tmp/x/.git Fix the testsuite to test this case (in one case fixing a test that depended on the current behavior), and then fix find_repo to handle this case correctly. In the process, simplify and document the logic in find_repo(): - Separate the concepts of "currently checking a .git directory" and "number of iterations left before going further counts as a search" into two separate variables, in_dot_git and min_iterations. - Move the logic to handle in_dot_git and append /.git to the top of the loop. - Only search ceiling_dirs and find ceiling_offset after running out of min_iterations; since ceiling_offset only tracks the longest matching ceiling directory, if ceiling_dirs contained both the current directory and a parent directory, this change makes find_repo stop the search at the parent directory.
Diffstat (limited to 'src')
-rw-r--r--src/repository.c42
1 files changed, 25 insertions, 17 deletions
diff --git a/src/repository.c b/src/repository.c
index d39a9015d..3ea15b790 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,27 @@ 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, 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) {
+ 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 (!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 +429,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)) {