[2nd SQUASHED PATCH] Add support for GIT_CEILING_DIRECTORIES

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In certain setups, trying to access a non-existing .git/ can take quite
some time, for example when the directory is an automount directory.

Allow the user to specify directories where Git would stop looking for
a .git/ directory: GIT_CEILING_DIRECTORIES, if set, is expected to be
a colon delimited list of such barrier directories.

Note: if GIT_CEILING_DIRECTORIES=/a/b and your current working directory
is /a, Git will _not_ stop looking.

Note2: you must not specify the directories with trailing slashes.

Initial implementation by David Reiss.

Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx>
---
 Documentation/git.txt          |    6 ++++
 cache.h                        |    2 +
 path.c                         |   25 ++++++++++++++++
 setup.c                        |   14 +++++++-
 t/t1504-ceiling-directories.sh |   63 ++++++++++++++++++++++++++++++++++++++++
 t/test-lib.sh                  |    1 +
 6 files changed, 109 insertions(+), 2 deletions(-)
 create mode 100644 t/t1504-ceiling-directories.sh

diff --git a/Documentation/git.txt b/Documentation/git.txt
index adcd3e0..e4413bf 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -415,6 +415,12 @@ git so take care if using Cogito etc.
 	This can also be controlled by the '--work-tree' command line
 	option and the core.worktree configuration variable.
 
+'GIT_CEILING_DIRECTORIES'::
+	If set (to a colon delimited list of absolute directories), Git
+	will refuse to look for the .git/ directory further when hitting
+	one of those directories (otherwise it would traverse the parent
+	directories until hitting the root directory).
+
 git Commits
 ~~~~~~~~~~~
 'GIT_AUTHOR_NAME'::
diff --git a/cache.h b/cache.h
index a8638b1..c31b4c7 100644
--- a/cache.h
+++ b/cache.h
@@ -300,6 +300,7 @@ static inline enum object_type object_type(unsigned int mode)
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 #define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
+#define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
@@ -522,6 +523,7 @@ static inline int is_absolute_path(const char *path)
 	return path[0] == '/';
 }
 const char *make_absolute_path(const char *path);
+int longest_prefix(const char *path, const char *prefix_list);
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
diff --git a/path.c b/path.c
index b7c24a2..a097ecc 100644
--- a/path.c
+++ b/path.c
@@ -357,3 +357,28 @@ const char *make_absolute_path(const char *path)
 
 	return buf;
 }
+
+static int is_separator(char c)
+{
+	return !c || c == '/';
+}
+
+int longest_prefix(const char *path, const char *prefix_list)
+{
+	int max_length = 0, length = 0, i;
+
+	for (i = 0; prefix_list[i]; i++)
+		if (prefix_list[i] == ':') {
+			if (length > max_length && is_separator(path[length]))
+				max_length = length;
+			length = 0;
+		}
+		else if (length >= 0) {
+			if (prefix_list[i] == path[length])
+				length++;
+			else
+				length = -1;
+		}
+	return max_length > length || !is_separator(path[length]) ?
+		max_length : length;
+}
diff --git a/setup.c b/setup.c
index 9e9a2b1..2f7a17a 100644
--- a/setup.c
+++ b/setup.c
@@ -365,10 +365,13 @@ const char *read_gitfile_gently(const char *path)
 const char *setup_git_directory_gently(int *nongit_ok)
 {
 	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
+	const char *ceiling_directories =
+		getenv(CEILING_DIRECTORIES_ENVIRONMENT);
 	static char cwd[PATH_MAX+1];
 	const char *gitdirenv;
 	const char *gitfile_dir;
 	int len, offset;
+	int min_offset = 0;
 
 	/*
 	 * Let's assume that we are in a git repository.
@@ -422,6 +425,9 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	if (!getcwd(cwd, sizeof(cwd)-1))
 		die("Unable to read current working directory");
 
+	if (ceiling_directories)
+		min_offset = longest_prefix(cwd, ceiling_directories);
+
 	/*
 	 * Test in the following order (relative to the cwd):
 	 * - .git (file containing "gitdir: <path>")
@@ -435,6 +441,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	 */
 	offset = len = strlen(cwd);
 	for (;;) {
+		if (offset <= min_offset)
+			goto non_git;
 		gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
 		if (gitfile_dir) {
 			if (set_git_dir(gitfile_dir))
@@ -453,7 +461,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
 		}
 		chdir("..");
 		do {
-			if (!offset) {
+			if (offset <= min_offset) {
+non_git:
 				if (nongit_ok) {
 					if (chdir(cwd))
 						die("Cannot come back to cwd");
@@ -462,7 +471,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
 				}
 				die("Not a git repository");
 			}
-		} while (cwd[--offset] != '/');
+		} while (offset > min_offset &&
+				--offset >=0 && cwd[offset] != '/');
 	}
 
 	inside_git_dir = 0;
diff --git a/t/t1504-ceiling-directories.sh b/t/t1504-ceiling-directories.sh
new file mode 100644
index 0000000..edc00be
--- /dev/null
+++ b/t/t1504-ceiling-directories.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='test limiting with GIT_CEILING_DIRECTORIES'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+	CWD="$(pwd -P)" &&
+	mkdir subdir
+
+'
+
+test_expect_success 'without GIT_CEILING_DIRECTORIES' '
+
+	test .git = "$(git rev-parse --git-dir)" &&
+	(cd subdir && git rev-parse --git-dir) &&
+	echo "$CWD" &&
+	test "$CWD/.git" = "$(cd subdir && git rev-parse --git-dir)"
+
+'
+
+test_expect_success 'with non-matching ceiling directory' '
+
+	test "$(GIT_CEILING_DIRECTORIES="$CWD/X" \
+		git rev-parse --git-dir)" = .git
+
+'
+
+test_expect_success 'with matching ceiling directory' '
+
+	GIT_CEILING_DIRECTORIES="$CWD" &&
+	export GIT_CEILING_DIRECTORIES &&
+	(cd subdir && test_must_fail git rev-parse --git-dir) &&
+	test_must_fail git rev-parse --git-dir
+
+'
+
+test_expect_success 'with matching ceiling directories' '
+
+	GIT_CEILING_DIRECTORIES="$CWD/X:$CWD/subdir" &&
+	export GIT_CEILING_DIRECTORIES &&
+	(cd subdir && test_must_fail git rev-parse --git-dir) &&
+	git rev-parse --git-dir &&
+	GIT_CEILING_DIRECTORIES="$CWD/subdir:$CWD/X" &&
+	export GIT_CEILING_DIRECTORIES &&
+	(cd subdir && test_must_fail git rev-parse --git-dir) &&
+	git rev-parse --git-dir
+
+'
+
+test_expect_success 'with non-directory prefix' '
+
+	GIT_CEILING_DIRECTORIES="$CWD/sub" &&
+	export GIT_CEILING_DIRECTORIES &&
+	(cd subdir && git rev-parse --git-dir)
+
+'
+
+test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 5002fb0..c3a3167 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -35,6 +35,7 @@ unset GIT_WORK_TREE
 unset GIT_EXTERNAL_DIFF
 unset GIT_INDEX_FILE
 unset GIT_OBJECT_DIRECTORY
+unset GIT_CEILING_DIRECTORIES
 unset SHA1_FILE_DIRECTORIES
 unset SHA1_FILE_DIRECTORY
 GIT_MERGE_VERBOSITY=5
-- 
1.5.5.1.490.g1644c11


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux