[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>
---

	On Thu, 15 May 2008, David Reiss wrote:

	> This meets my needs.
	> 
	> Junio, if you'd like, I can incorporate your suggestions of 
	> normalizing paths internally and testing more corner cases.  But
	> if you just want to take this version, I'll be fine.

	For your pleasure, the combined patch of my work (so that it is 
	easier to hack on normalizing paths, if you still want that --
	you might want to look at get_pathspec() and its history in that 
	case).

 Documentation/git.txt          |    6 ++++
 cache.h                        |    2 +
 path.c                         |   25 ++++++++++++++++++
 setup.c                        |   11 ++++++-
 t/t1504-ceiling-directories.sh |   54 ++++++++++++++++++++++++++++++++++++++++
 t/test-lib.sh                  |    1 +
 6 files changed, 97 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..cece3e4 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>")
@@ -453,7 +459,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
 		}
 		chdir("..");
 		do {
-			if (!offset) {
+			if (offset <= min_offset) {
 				if (nongit_ok) {
 					if (chdir(cwd))
 						die("Cannot come back to cwd");
@@ -462,7 +468,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..6c8757d
--- /dev/null
+++ b/t/t1504-ceiling-directories.sh
@@ -0,0 +1,54 @@
+#!/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 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.425.g5f464.dirty

--
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