Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 47d263c

Browse files
committed
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.
1 parent 7b29be3 commit 47d263c

File tree

3 files changed

+39
-20
lines changed

3 files changed

+39
-20
lines changed

src/repository.c

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -359,21 +359,36 @@ static int find_repo(
359359
git_buf path = GIT_BUF_INIT;
360360
struct stat st;
361361
dev_t initial_device = 0;
362-
bool try_with_dot_git = ((flags & GIT_REPOSITORY_OPEN_BARE) != 0);
362+
int min_iterations;
363+
bool in_dot_git;
363364
int ceiling_offset;
364365

365366
git_buf_free(repo_path);
366367

367368
if ((error = git_path_prettify(&path, start_path, NULL)) < 0)
368369
return error;
369370

370-
ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
371+
/* in_dot_git toggles each loop:
372+
* /a/b/c/.git, /a/b/c, /a/b/.git, /a/b, /a/.git, /a
373+
* With GIT_REPOSITORY_OPEN_BARE, we assume we started with /a/b/c.git
374+
* and don't append .git the first time through.
375+
* min_iterations indicates the number of iterations left before going
376+
* further counts as a search. */
377+
if (flags & GIT_REPOSITORY_OPEN_BARE) {
378+
in_dot_git = true;
379+
min_iterations = 1;
380+
} else {
381+
in_dot_git = false;
382+
min_iterations = 2;
383+
}
371384

372-
if (!try_with_dot_git &&
373-
(error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
374-
return error;
385+
while (!error && (min_iterations || !(path.ptr[ceiling_offset] == 0 ||
386+
(flags & GIT_REPOSITORY_OPEN_NO_SEARCH)))) {
387+
if (!in_dot_git)
388+
if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
389+
break;
390+
in_dot_git = !in_dot_git;
375391

376-
while (!error && !git_buf_len(repo_path)) {
377392
if (p_stat(path.ptr, &st) == 0) {
378393
/* check that we have not crossed device boundaries */
379394
if (initial_device == 0)
@@ -414,17 +429,10 @@ static int find_repo(
414429
break;
415430
}
416431

417-
if (try_with_dot_git) {
418-
/* if we tried original dir with and without .git AND either hit
419-
* directory ceiling or NO_SEARCH was requested, then be done.
420-
*/
421-
if (path.ptr[ceiling_offset] == '\0' ||
422-
(flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0)
423-
break;
424-
/* otherwise look first for .git item */
425-
error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
426-
}
427-
try_with_dot_git = !try_with_dot_git;
432+
/* Once we've checked the directory (and .git if applicable),
433+
* find the ceiling for a search. */
434+
if (min_iterations && (--min_iterations == 0))
435+
ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
428436
}
429437

430438
if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {

tests/repo/discover.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,22 @@ void test_repo_discover__0(void)
118118
cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs));
119119
cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs));
120120

121+
append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER_SUB);
122+
ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
123+
124+
//this must pass as ceiling_directories cannot prevent the current
125+
//working directory to be checked
126+
ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs, &sub_repository_path);
127+
ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, &sub_repository_path);
128+
cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs));
129+
cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs));
130+
121131
append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER);
122132
ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
123133

124-
//this must pass as ceiling_directories cannot predent the current
134+
//this must pass as ceiling_directories cannot prevent the current
125135
//working directory to be checked
126-
cl_git_pass(git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
136+
ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs, &sub_repository_path);
127137
cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs));
128138
cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs));
129139
cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs));

tests/repo/open.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,9 @@ void test_repo_open__failures(void)
196196
&repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL));
197197

198198
/* fail with ceiling too low */
199-
cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub"));
200199
cl_git_fail(git_repository_open_ext(&repo, "attr/sub", 0, ceiling.ptr));
200+
cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub"));
201+
cl_git_fail(git_repository_open_ext(&repo, "attr/sub/sub", 0, ceiling.ptr));
201202

202203
/* fail with no repo */
203204
cl_git_pass(p_mkdir("alternate", 0777));

0 commit comments

Comments
 (0)