Commit 39c6fca33aa38ad26c7c5f36734c6434593daae1

Josh Triplett 2016-04-03T16:01:01

Add GIT_REPOSITORY_OPEN_NO_DOTGIT flag to avoid appending /.git GIT_REPOSITORY_OPEN_NO_SEARCH does not search up through parent directories, but still tries the specified path both directly and with /.git appended. GIT_REPOSITORY_OPEN_BARE avoids appending /.git, but opens the repository in bare mode even if it has a working directory. To support the semantics git uses when given $GIT_DIR in the environment, provide a new GIT_REPOSITORY_OPEN_NO_DOTGIT flag to not try appending /.git.

diff --git a/include/git2/repository.h b/include/git2/repository.h
index 85b7e68..f7efe01 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -95,11 +95,15 @@ GIT_EXTERN(int) git_repository_discover(
  * * GIT_REPOSITORY_OPEN_BARE - Open repository as a bare repo regardless
  *   of core.bare config, and defer loading config file for faster setup.
  *   Unlike `git_repository_open_bare`, this can follow gitlinks.
+ * * GIT_REPOSITORY_OPEN_NO_DOTGIT - Do not check for a repository by
+ *   appending /.git to the start_path; only open the repository if
+ *   start_path itself points to the git directory.
  */
 typedef enum {
 	GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
 	GIT_REPOSITORY_OPEN_CROSS_FS  = (1 << 1),
 	GIT_REPOSITORY_OPEN_BARE      = (1 << 2),
+	GIT_REPOSITORY_OPEN_NO_DOTGIT = (1 << 3),
 } git_repository_open_flag_t;
 
 /**
diff --git a/src/repository.c b/src/repository.c
index 3ea15b7..5b43467 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -370,11 +370,12 @@ static int find_repo(
 
 	/* 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.
+	 * 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) {
+	if (flags & (GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
 		in_dot_git = true;
 		min_iterations = 1;
 	} else {
@@ -384,10 +385,12 @@ static int find_repo(
 
 	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;
+		if (!(flags & GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
+			if (!in_dot_git)
+				if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
+					break;
+			in_dot_git = !in_dot_git;
+		}
 
 		if (p_stat(path.ptr, &st) == 0) {
 			/* check that we have not crossed device boundaries */
diff --git a/tests/repo/open.c b/tests/repo/open.c
index 7cdd182..5197276 100644
--- a/tests/repo/open.c
+++ b/tests/repo/open.c
@@ -206,6 +206,12 @@ void test_repo_open__failures(void)
 	cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
 	cl_git_fail(git_repository_open_ext(&repo, "alternate/.git", 0, NULL));
 
+	/* fail with no searching and no appending .git */
+	cl_git_fail(git_repository_open_ext(
+		&repo, "attr",
+		GIT_REPOSITORY_OPEN_NO_SEARCH | GIT_REPOSITORY_OPEN_NO_DOTGIT,
+		NULL));
+
 	git_buf_free(&ceiling);
 }