diff options
author | Josh Triplett <josh@joshtriplett.org> | 2016-04-03 19:24:15 -0700 |
---|---|---|
committer | Josh Triplett <josh@joshtriplett.org> | 2016-06-24 11:44:01 -0700 |
commit | ed577134a5dcf30c95b740859aceebd9c1137157 (patch) | |
tree | 25543c84c8126fa37f30aa72b086fab37cef1ad4 | |
parent | 37e9278220095435d986e7412d2a97097dab3221 (diff) | |
download | libgit2-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.
-rw-r--r-- | src/repository.c | 42 | ||||
-rw-r--r-- | tests/repo/discover.c | 12 | ||||
-rw-r--r-- | tests/repo/open.c | 3 |
3 files changed, 38 insertions, 19 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)) { diff --git a/tests/repo/discover.c b/tests/repo/discover.c index 86bd7458f..358daee9f 100644 --- a/tests/repo/discover.c +++ b/tests/repo/discover.c @@ -118,12 +118,22 @@ void test_repo_discover__0(void) cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs)); cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs)); + append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER_SUB); + ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf); + + /* this must pass as ceiling_directories cannot prevent the current + * working directory to be checked */ + ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs, &sub_repository_path); + ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, &sub_repository_path); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs)); + append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER); ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf); //this must pass as ceiling_directories cannot predent the current //working directory to be checked - cl_git_pass(git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs)); + ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs, &sub_repository_path); cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs)); cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs)); cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs)); diff --git a/tests/repo/open.c b/tests/repo/open.c index d3d087231..7cdd182a7 100644 --- a/tests/repo/open.c +++ b/tests/repo/open.c @@ -196,8 +196,9 @@ void test_repo_open__failures(void) &repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)); /* fail with ceiling too low */ - cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub")); cl_git_fail(git_repository_open_ext(&repo, "attr/sub", 0, ceiling.ptr)); + cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub")); + cl_git_fail(git_repository_open_ext(&repo, "attr/sub/sub", 0, ceiling.ptr)); /* fail with no repo */ cl_git_pass(p_mkdir("alternate", 0777)); |