>From f29e8a524f770596450dbbe338433f0dfc365377 Mon Sep 17 00:00:00 2001 From: David Reiss <dreiss@xxxxxxxxxxxx> Date: Sun, 20 Apr 2008 12:27:52 -0700 Subject: [PATCH] chdir after computing new cwd in check_repository_format_gently In preparation for adding a check before the chdir. This should be a no-op because the cwd is not read in the interim and any nonlocal exits either chdir to an absolute path or die. Signed-off-by: David Reiss <dreiss@xxxxxxxxxxxx> --- setup.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/setup.c b/setup.c index 3d2d958..a7b7f56 100644 --- a/setup.c +++ b/setup.c @@ -396,7 +396,6 @@ const char *setup_git_directory_gently(int *nongit_ok) check_repository_format_gently(nongit_ok); return NULL; } - chdir(".."); do { if (!offset) { if (nongit_ok) { @@ -408,6 +407,7 @@ const char *setup_git_directory_gently(int *nongit_ok) die("Not a git repository"); } } while (cwd[--offset] != '/'); + chdir(".."); } inside_git_dir = 0; -- 1.5.4 >From 1543c81e75e7890475331bbae1de98ead45e5245 Mon Sep 17 00:00:00 2001 From: David Reiss <dreiss@xxxxxxxxxxxx> Date: Sun, 20 Apr 2008 22:54:11 -0700 Subject: [PATCH] Add support for GIT_CEILING_DIRS Make git recognize a new environment variable that prevents it from chdir'ing up into specified directories when looking for a GIT_DIR. Useful for avoiding slow network directories. Signed-off-by: David Reiss <dreiss@xxxxxxxxxxxx> --- Just a bit of context about the motivation for this. I use git in an environment where homedirs are automounted and "ls /home/nonexistent" takes about 9 seconds. I use the git bash completion, which runs "git help -a". As of Git 1.5.5, this calls setup_git_directory_gently, which searches /home/.git and /home/objects, which takes forever, delaying all of my logins. This patch lets me set "GIT_CEILING_DIRS=/home", which makes this operation fast. As a bonus, the "git symbolic-ref HEAD" in my shell prompt now works quickly when I'm in a non-git directory. Documentation/git.txt | 8 ++++ cache.h | 1 + setup.c | 67 +++++++++++++++++++++++++----- t/t1503-ceiling-dirs.sh | 104 +++++++++++++++++++++++++++++++++++++++++++++++ t/test-lib.sh | 1 + 5 files changed, 170 insertions(+), 11 deletions(-) create mode 100755 t/t1503-ceiling-dirs.sh diff --git a/Documentation/git.txt b/Documentation/git.txt index 336fe99..cc52f6e 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -406,6 +406,14 @@ 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_DIRS':: + This should be a colon-separated list of absolute paths. + If set, it is a list of directories that git should not chdir + up into while looking for a repository directory. + It will not exclude the current working directory or + a GIT_DIR set on the command line or in the environment. + (Useful for excluding slow-loading network directories.) + git Commits ~~~~~~~~~~~ 'GIT_AUTHOR_NAME':: diff --git a/cache.h b/cache.h index 50b28fa..e5e3dba 100644 --- a/cache.h +++ b/cache.h @@ -295,6 +295,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_DIRS_ENVIRONMENT "GIT_CEILING_DIRS" #define GITATTRIBUTES_FILE ".gitattributes" #define INFOATTRIBUTES_FILE "info/attributes" #define ATTRIBUTE_MACRO_PREFIX "[attr]" diff --git a/setup.c b/setup.c index a7b7f56..2f95d68 100644 --- a/setup.c +++ b/setup.c @@ -321,9 +321,10 @@ static int check_repository_format_gently(int *nongit_ok) const char *setup_git_directory_gently(int *nongit_ok) { const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); + const char *env_ceiling_dirs = getenv(CEILING_DIRS_ENVIRONMENT); static char cwd[PATH_MAX+1]; const char *gitdirenv; - int len, offset; + int len, offset, max_offset = -1; /* * Let's assume that we are in a git repository. @@ -375,6 +376,37 @@ const char *setup_git_directory_gently(int *nongit_ok) if (!getcwd(cwd, sizeof(cwd)-1)) die("Unable to read current working directory"); + // Compute max_offset based on GIT_CEILING_DIRS. + if (env_ceiling_dirs) { + char *ceils, *ceil, *colon; + ceil = ceils = xstrdup(env_ceiling_dirs); + for (;;) { + int len; + + if ((colon = strchr(ceil, ':'))) + *colon = '\0'; + len = strlen(ceil); + + // "" would otherwise be treated like "/". + if (len) { + // Trim trailing slashes. + while (len && ceil[len-1] == '/') + ceil[--len] = '\0'; + + if (!strncmp(cwd, ceil, len) && + cwd[len] == '/' && + len > max_offset) { + max_offset = len; + } + } + + if (!colon) + break; + ceil = colon + 1; + } + free(ceils); + } + /* * Test in the following order (relative to the cwd): * - .git/ @@ -386,6 +418,7 @@ const char *setup_git_directory_gently(int *nongit_ok) */ offset = len = strlen(cwd); for (;;) { + // Check the current directory (.git first). if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) break; if (is_git_directory(".")) { @@ -396,17 +429,29 @@ const char *setup_git_directory_gently(int *nongit_ok) check_repository_format_gently(nongit_ok); return NULL; } - do { - if (!offset) { - if (nongit_ok) { - if (chdir(cwd)) - die("Cannot come back to cwd"); - *nongit_ok = 1; - return NULL; - } - die("Not a git repository"); + + // Did we just check the root dir? + if (!offset) { + not_a_repo: + if (nongit_ok) { + if (chdir(cwd)) + die("Cannot come back to cwd"); + *nongit_ok = 1; + return NULL; } - } while (cwd[--offset] != '/'); + die("Not a git repository"); + } + + while (cwd[--offset] != '/') { + assert(offset > 0); + } + + // Don't chdir into the ceiling. + if (offset <= max_offset) { + assert(offset == max_offset); + goto not_a_repo; + } + chdir(".."); } diff --git a/t/t1503-ceiling-dirs.sh b/t/t1503-ceiling-dirs.sh new file mode 100755 index 0000000..d4eaa13 --- /dev/null +++ b/t/t1503-ceiling-dirs.sh @@ -0,0 +1,104 @@ +#!/bin/sh + +test_description='test GIT_CEILING_DIRS' +. ./test-lib.sh + +test_prefix() { + test_expect_success "$1" \ + "test '$2' = \"\$(git rev-parse --show-prefix)\"" + shift + [ $# -eq 0 ] && return +} + +test_fail() { + test_expect_code 128 "$1: prefix" \ + "git rev-parse --show-prefix" + shift + [ $# -eq 0 ] && return +} + +TRASH_ROOT="$(pwd)" +ROOT_PARENT=$(dirname "$TRASH_ROOT") + + +unset GIT_CEILING_DIRS +test_prefix no_ceil "" + +export GIT_CEILING_DIRS="" +test_prefix ceil_empty "" + +export GIT_CEILING_DIRS="$ROOT_PARENT" +test_prefix ceil_at_parent "" + +export GIT_CEILING_DIRS="$ROOT_PARENT/" +test_prefix ceil_at_parent_slash "" + +export GIT_CEILING_DIRS="$TRASH_ROOT" +test_prefix ceil_at_trash "" + +export GIT_CEILING_DIRS="$TRASH_ROOT/" +test_prefix ceil_at_trash_slash "" + +export GIT_CEILING_DIRS="$TRASH_ROOT/sub" +test_prefix ceil_at_sub "" + +export GIT_CEILING_DIRS="$TRASH_ROOT/sub/" +test_prefix ceil_at_sub_slash "" + + +mkdir -p sub/dir || exit 1 +cd sub/dir || exit 1 + +unset GIT_CEILING_DIRS +test_prefix subdir_no_ceil "sub/dir/" + +export GIT_CEILING_DIRS="" +test_prefix subdir_ceil_empty "sub/dir/" + +export GIT_CEILING_DIRS="$TRASH_ROOT" +test_fail subdir_ceil_at_trash + +export GIT_CEILING_DIRS="$TRASH_ROOT/" +test_fail subdir_ceil_at_trash_slash + +export GIT_CEILING_DIRS="$TRASH_ROOT/sub" +test_fail subdir_ceil_at_sub + +export GIT_CEILING_DIRS="$TRASH_ROOT/sub/" +test_fail subdir_ceil_at_sub_slash + +export GIT_CEILING_DIRS="$TRASH_ROOT/sub/dir" +test_prefix subdir_ceil_at_subdir "sub/dir/" + +export GIT_CEILING_DIRS="$TRASH_ROOT/sub/dir/" +test_prefix subdir_ceil_at_subdir_slash "sub/dir/" + + +export GIT_CEILING_DIRS="$TRASH_ROOT/su" +test_prefix subdir_ceil_at_su "sub/dir/" + +export GIT_CEILING_DIRS="$TRASH_ROOT/su/" +test_prefix subdir_ceil_at_su_slash "sub/dir/" + +export GIT_CEILING_DIRS="$TRASH_ROOT/subdi" +test_prefix subdir_ceil_at_subdi "sub/dir/" + +export GIT_CEILING_DIRS="$TRASH_ROOT/subdi" +test_prefix subdir_ceil_at_subdi_slash "sub/dir/" + + +export GIT_CEILING_DIRS="foo:$TRASH_ROOT/sub" +test_fail second_of_two + +export GIT_CEILING_DIRS="$TRASH_ROOT/sub:bar" +test_fail first_of_two + +export GIT_CEILING_DIRS="foo:$TRASH_ROOT/sub:bar" +test_fail second_of_three + + +export GIT_CEILING_DIRS="$TRASH_ROOT/sub" +export GIT_DIR=../../.git +test_prefix git_dir_specified "" + +test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index 7c2a8ba..22899c1 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_DIRS unset SHA1_FILE_DIRECTORIES unset SHA1_FILE_DIRECTORY GIT_MERGE_VERBOSITY=5 -- 1.5.4 -- 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