Commit a200dc9e by Josh Triplett Committed by Carlos Martín Nieto

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.
parent c7a03369
...@@ -359,7 +359,8 @@ static int find_repo( ...@@ -359,7 +359,8 @@ static int find_repo(
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
struct stat st; struct stat st;
dev_t initial_device = 0; 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; int ceiling_offset;
git_buf_free(repo_path); git_buf_free(repo_path);
...@@ -367,13 +368,27 @@ static int find_repo( ...@@ -367,13 +368,27 @@ static int find_repo(
if ((error = git_path_prettify(&path, start_path, NULL)) < 0) if ((error = git_path_prettify(&path, start_path, NULL)) < 0)
return error; 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 && while (!error && (min_iterations || !(path.ptr[ceiling_offset] == 0 ||
(error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0) (flags & GIT_REPOSITORY_OPEN_NO_SEARCH)))) {
return error; 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) { if (p_stat(path.ptr, &st) == 0) {
/* check that we have not crossed device boundaries */ /* check that we have not crossed device boundaries */
if (initial_device == 0) if (initial_device == 0)
...@@ -414,17 +429,10 @@ static int find_repo( ...@@ -414,17 +429,10 @@ static int find_repo(
break; break;
} }
if (try_with_dot_git) { /* Once we've checked the directory (and .git if applicable),
/* if we tried original dir with and without .git AND either hit * find the ceiling for a search. */
* directory ceiling or NO_SEARCH was requested, then be done. if (min_iterations && (--min_iterations == 0))
*/ ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
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;
} }
if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
......
...@@ -118,12 +118,22 @@ void test_repo_discover__0(void) ...@@ -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_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)); 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); append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER);
ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf); ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
//this must pass as ceiling_directories cannot predent the current //this must pass as ceiling_directories cannot predent the current
//working directory to be checked //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, 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, 0, ceiling_dirs));
cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_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));
......
...@@ -196,8 +196,9 @@ void test_repo_open__failures(void) ...@@ -196,8 +196,9 @@ void test_repo_open__failures(void)
&repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)); &repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL));
/* fail with ceiling too low */ /* 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_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 */ /* fail with no repo */
cl_git_pass(p_mkdir("alternate", 0777)); cl_git_pass(p_mkdir("alternate", 0777));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment