{"thread":{"id":"13199","subject":"[PATCH] Add support for GIT_CEILING_DIRS","startedAt":"2008-04-21T17:43:08Z","lastAt":"2008-04-21T17:43:08Z","messageCount":1,"participants":["David Reiss"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"74876","messageId":"480CD22C.4030109@facebook.com","threadId":"13199","inReplyTo":null,"subject":"[PATCH] Add support for GIT_CEILING_DIRS","fromName":"David Reiss","fromEmail":"dreiss@facebook.com","sentAt":"2008-04-21T17:43:08Z","receivedAt":"2008-04-21T17:43:08Z","isPatch":true,"sender":{"key":"dreiss@facebook.com","avatar":null},"body":">From f29e8a524f770596450dbbe338433f0dfc365377 Mon Sep 17 00:00:00 2001\nFrom: David Reiss <dreiss@facebook.com>\nDate: Sun, 20 Apr 2008 12:27:52 -0700\nSubject: [PATCH] chdir after computing new cwd in check_repository_format_gently\n\nIn preparation for adding a check before the chdir.\nThis should be a no-op because the cwd is not read in the interim\nand any nonlocal exits either chdir to an absolute path or die.\n\nSigned-off-by: David Reiss <dreiss@facebook.com>\n---\n setup.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex 3d2d958..a7b7f56 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -396,7 +396,6 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\t\tcheck_repository_format_gently(nongit_ok);\n \t\t\treturn NULL;\n \t\t}\n-\t\tchdir(\"..\");\n \t\tdo {\n \t\t\tif (!offset) {\n \t\t\t\tif (nongit_ok) {\n@@ -408,6 +407,7 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\t\t\tdie(\"Not a git repository\");\n \t\t\t}\n \t\t} while (cwd[--offset] != '/');\n+\t\tchdir(\"..\");\n \t}\n \n \tinside_git_dir = 0;\n-- \n1.5.4\n\n\n>From 1543c81e75e7890475331bbae1de98ead45e5245 Mon Sep 17 00:00:00 2001\nFrom: David Reiss <dreiss@facebook.com>\nDate: Sun, 20 Apr 2008 22:54:11 -0700\nSubject: [PATCH] Add support for GIT_CEILING_DIRS\n\nMake git recognize a new environment variable that prevents it from\nchdir'ing up into specified directories when looking for a GIT_DIR.\nUseful for avoiding slow network directories.\n\nSigned-off-by: David Reiss <dreiss@facebook.com>\n---\nJust a bit of context about the motivation for this.  I use git in an\nenvironment where homedirs are automounted and \"ls /home/nonexistent\"\ntakes about 9 seconds.  I use the git bash completion, which runs\n\"git help -a\".  As of Git 1.5.5, this calls setup_git_directory_gently,\nwhich searches /home/.git and /home/objects, which takes forever,\ndelaying all of my logins.  This patch lets me set\n\"GIT_CEILING_DIRS=/home\", which makes this operation fast.  As a bonus,\nthe \"git symbolic-ref HEAD\" in my shell prompt now works quickly when\nI'm in a non-git directory.\n\n Documentation/git.txt   |    8 ++++\n cache.h                 |    1 +\n setup.c                 |   67 +++++++++++++++++++++++++-----\n t/t1503-ceiling-dirs.sh |  104 +++++++++++++++++++++++++++++++++++++++++++++++\n t/test-lib.sh           |    1 +\n 5 files changed, 170 insertions(+), 11 deletions(-)\n create mode 100755 t/t1503-ceiling-dirs.sh\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 336fe99..cc52f6e 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -406,6 +406,14 @@ git so take care if using Cogito etc.\n \tThis can also be controlled by the '--work-tree' command line\n \toption and the core.worktree configuration variable.\n \n+'GIT_CEILING_DIRS'::\n+\tThis should be a colon-separated list of absolute paths.\n+\tIf set, it is a list of directories that git should not chdir\n+\tup into while looking for a repository directory.\n+\tIt will not exclude the current working directory or\n+\ta GIT_DIR set on the command line or in the environment.\n+\t(Useful for excluding slow-loading network directories.)\n+\n git Commits\n ~~~~~~~~~~~\n 'GIT_AUTHOR_NAME'::\ndiff --git a/cache.h b/cache.h\nindex 50b28fa..e5e3dba 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -295,6 +295,7 @@ static inline enum object_type object_type(unsigned int mode)\n #define CONFIG_ENVIRONMENT \"GIT_CONFIG\"\n #define CONFIG_LOCAL_ENVIRONMENT \"GIT_CONFIG_LOCAL\"\n #define EXEC_PATH_ENVIRONMENT \"GIT_EXEC_PATH\"\n+#define CEILING_DIRS_ENVIRONMENT \"GIT_CEILING_DIRS\"\n #define GITATTRIBUTES_FILE \".gitattributes\"\n #define INFOATTRIBUTES_FILE \"info/attributes\"\n #define ATTRIBUTE_MACRO_PREFIX \"[attr]\"\ndiff --git a/setup.c b/setup.c\nindex a7b7f56..2f95d68 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -321,9 +321,10 @@ static int check_repository_format_gently(int *nongit_ok)\n const char *setup_git_directory_gently(int *nongit_ok)\n {\n \tconst char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);\n+\tconst char *env_ceiling_dirs = getenv(CEILING_DIRS_ENVIRONMENT);\n \tstatic char cwd[PATH_MAX+1];\n \tconst char *gitdirenv;\n-\tint len, offset;\n+\tint len, offset, max_offset = -1;\n \n \t/*\n \t * Let's assume that we are in a git repository.\n@@ -375,6 +376,37 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \tif (!getcwd(cwd, sizeof(cwd)-1))\n \t\tdie(\"Unable to read current working directory\");\n \n+\t// Compute max_offset based on GIT_CEILING_DIRS.\n+\tif (env_ceiling_dirs) {\n+\t\tchar *ceils, *ceil, *colon;\n+\t\tceil = ceils = xstrdup(env_ceiling_dirs);\n+\t\tfor (;;) {\n+\t\t\tint len;\n+\n+\t\t\tif ((colon = strchr(ceil, ':')))\n+\t\t\t\t*colon = '\\0';\n+\t\t\tlen = strlen(ceil);\n+\n+\t\t\t// \"\" would otherwise be treated like \"/\".\n+\t\t\tif (len) {\n+\t\t\t\t// Trim trailing slashes.\n+\t\t\t\twhile (len && ceil[len-1] == '/')\n+\t\t\t\t\tceil[--len] = '\\0';\n+\n+\t\t\t\tif (!strncmp(cwd, ceil, len) &&\n+\t\t\t\t    cwd[len] == '/' &&\n+\t\t\t\t    len > max_offset) {\n+\t\t\t\t\tmax_offset = len;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tif (!colon)\n+\t\t\t\tbreak;\n+\t\t\tceil = colon + 1;\n+\t\t}\n+\t\tfree(ceils);\n+\t}\n+\n \t/*\n \t * Test in the following order (relative to the cwd):\n \t * - .git/\n@@ -386,6 +418,7 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t */\n \toffset = len = strlen(cwd);\n \tfor (;;) {\n+\t\t// Check the current directory (.git first).\n \t\tif (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))\n \t\t\tbreak;\n \t\tif (is_git_directory(\".\")) {\n@@ -396,17 +429,29 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\t\tcheck_repository_format_gently(nongit_ok);\n \t\t\treturn NULL;\n \t\t}\n-\t\tdo {\n-\t\t\tif (!offset) {\n-\t\t\t\tif (nongit_ok) {\n-\t\t\t\t\tif (chdir(cwd))\n-\t\t\t\t\t\tdie(\"Cannot come back to cwd\");\n-\t\t\t\t\t*nongit_ok = 1;\n-\t\t\t\t\treturn NULL;\n-\t\t\t\t}\n-\t\t\t\tdie(\"Not a git repository\");\n+\n+\t\t// Did we just check the root dir?\n+\t\tif (!offset) {\n+\t\tnot_a_repo:\n+\t\t\tif (nongit_ok) {\n+\t\t\t\tif (chdir(cwd))\n+\t\t\t\t\tdie(\"Cannot come back to cwd\");\n+\t\t\t\t*nongit_ok = 1;\n+\t\t\t\treturn NULL;\n \t\t\t}\n-\t\t} while (cwd[--offset] != '/');\n+\t\t\tdie(\"Not a git repository\");\n+\t\t}\n+\n+\t\twhile (cwd[--offset] != '/') {\n+\t\t\tassert(offset > 0);\n+\t\t}\n+\n+\t\t// Don't chdir into the ceiling.\n+\t\tif (offset <= max_offset) {\n+\t\t\tassert(offset == max_offset);\n+\t\t\tgoto not_a_repo;\n+\t\t}\n+\n \t\tchdir(\"..\");\n \t}\n \ndiff --git a/t/t1503-ceiling-dirs.sh b/t/t1503-ceiling-dirs.sh\nnew file mode 100755\nindex 0000000..d4eaa13\n--- /dev/null\n+++ b/t/t1503-ceiling-dirs.sh\n@@ -0,0 +1,104 @@\n+#!/bin/sh\n+\n+test_description='test GIT_CEILING_DIRS'\n+. ./test-lib.sh\n+\n+test_prefix() {\n+\ttest_expect_success \"$1\" \\\n+\t\"test '$2' = \\\"\\$(git rev-parse --show-prefix)\\\"\"\n+\tshift\n+\t[ $# -eq 0 ] && return\n+}\n+\n+test_fail() {\n+\ttest_expect_code 128 \"$1: prefix\" \\\n+\t\"git rev-parse --show-prefix\"\n+\tshift\n+\t[ $# -eq 0 ] && return\n+}\n+\n+TRASH_ROOT=\"$(pwd)\"\n+ROOT_PARENT=$(dirname \"$TRASH_ROOT\")\n+\n+\n+unset GIT_CEILING_DIRS\n+test_prefix no_ceil \"\"\n+\n+export GIT_CEILING_DIRS=\"\"\n+test_prefix ceil_empty \"\"\n+\n+export GIT_CEILING_DIRS=\"$ROOT_PARENT\"\n+test_prefix ceil_at_parent \"\"\n+\n+export GIT_CEILING_DIRS=\"$ROOT_PARENT/\"\n+test_prefix ceil_at_parent_slash \"\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT\"\n+test_prefix ceil_at_trash \"\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/\"\n+test_prefix ceil_at_trash_slash \"\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sub\"\n+test_prefix ceil_at_sub \"\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sub/\"\n+test_prefix ceil_at_sub_slash \"\"\n+\n+\n+mkdir -p sub/dir || exit 1\n+cd sub/dir || exit 1\n+\n+unset GIT_CEILING_DIRS\n+test_prefix subdir_no_ceil \"sub/dir/\"\n+\n+export GIT_CEILING_DIRS=\"\"\n+test_prefix subdir_ceil_empty \"sub/dir/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT\"\n+test_fail subdir_ceil_at_trash\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/\"\n+test_fail subdir_ceil_at_trash_slash\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sub\"\n+test_fail subdir_ceil_at_sub\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sub/\"\n+test_fail subdir_ceil_at_sub_slash\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sub/dir\"\n+test_prefix subdir_ceil_at_subdir \"sub/dir/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sub/dir/\"\n+test_prefix subdir_ceil_at_subdir_slash \"sub/dir/\"\n+\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/su\"\n+test_prefix subdir_ceil_at_su \"sub/dir/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/su/\"\n+test_prefix subdir_ceil_at_su_slash \"sub/dir/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/subdi\"\n+test_prefix subdir_ceil_at_subdi \"sub/dir/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/subdi\"\n+test_prefix subdir_ceil_at_subdi_slash \"sub/dir/\"\n+\n+\n+export GIT_CEILING_DIRS=\"foo:$TRASH_ROOT/sub\"\n+test_fail second_of_two\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sub:bar\"\n+test_fail first_of_two\n+\n+export GIT_CEILING_DIRS=\"foo:$TRASH_ROOT/sub:bar\"\n+test_fail second_of_three\n+\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sub\"\n+export GIT_DIR=../../.git\n+test_prefix git_dir_specified \"\"\n+\n+test_done\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 7c2a8ba..22899c1 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -35,6 +35,7 @@ unset GIT_WORK_TREE\n unset GIT_EXTERNAL_DIFF\n unset GIT_INDEX_FILE\n unset GIT_OBJECT_DIRECTORY\n+unset GIT_CEILING_DIRS\n unset SHA1_FILE_DIRECTORIES\n unset SHA1_FILE_DIRECTORY\n GIT_MERGE_VERBOSITY=5\n-- \n1.5.4\n"}]}