{"thread":{"id":"13533","subject":"[PATCH v3] Add support for GIT_CEILING_DIRS","startedAt":"2008-05-15T18:49:44Z","lastAt":"2008-05-19T10:49:55Z","messageCount":20,"participants":["David Reiss","Johannes Schindelin","Junio C Hamano","Johannes Sixt"],"isPatch":true,"patchVersion":3,"patchTotal":null},"messages":[{"id":"77062","messageId":"482C85C8.90804@facebook.com","threadId":"13533","inReplyTo":null,"subject":"[PATCH v3] Add support for GIT_CEILING_DIRS","fromName":"David Reiss","fromEmail":"dreiss@facebook.com","sentAt":"2008-05-15T18:49:44Z","receivedAt":"2008-05-15T18:49:44Z","isPatch":true,"sender":{"key":"dreiss@facebook.com","avatar":null},"body":"Make 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\nFor example, I use git in an environment where homedirs are automounted\nand \"ls /home/nonexistent\" takes about 9 seconds.  Setting\nGIT_CEILING_DIRS=\"/home\" allows \"git help -a\" (for bash completion) and\n\"git symbolic-ref\" (for my shell prompt) to run in a reasonable time.\n\nThis also moves the chdir call to after computing the new cwd.\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 Documentation/git.txt   |    8 +++\n cache.h                 |    1 +\n setup.c                 |  127 ++++++++++++++++++++++++++++++++++----\n t/t1504-ceiling-dirs.sh |  156 +++++++++++++++++++++++++++++++++++++++++++++++\n t/test-lib.sh           |    1 +\n 5 files changed, 281 insertions(+), 12 deletions(-)\n create mode 100755 t/t1504-ceiling-dirs.sh\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 6f445b1..8aea331 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -415,6 +415,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 9cee9a5..8300acc 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -300,6 +300,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 b8fd476..fdcfae1 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -353,16 +353,118 @@ const char *read_gitfile_gently(const char *path)\n }\n \n /*\n+ * path = Canonical absolute path\n+ * prefix_list = Colon-separated list of canonical absolute paths\n+ *\n+ * Determines, for each path in parent_list, whether the \"prefix\" really\n+ * is an ancestor directory of path.  Returns the length of the longest\n+ * ancestor directory, excluding any trailing slashes, or -1 if no prefix\n+ * is an ancestry.  (Note that this means 0 is returned if prefix_list\n+ * contains \"/\".)  \"/foo\" is not considered an ancestor of \"/foobar\".\n+ * Directories are not considered to be their own ancestors.  Paths must\n+ * be in a canonical form: empty components, or \".\" or \"..\" components\n+ * are not allowed.  prefix_list may be null, which is like \"\".\n+ */\n+static int longest_ancestor_length(const char *path, const char *prefix_list)\n+{\n+\tconst char *ceil, *colon;\n+\tint max_len = -1;\n+\n+\tif (prefix_list == NULL)\n+\t\treturn -1;\n+\t/* \"/\" is a tricky edge case.  It should always return -1, though. */\n+\tif (!strcmp(path, \"/\"))\n+\t\treturn -1;\n+\n+\tceil = prefix_list;\n+\tfor (;;) {\n+\t\tint len;\n+\n+\t\t/* Add strchrnul to compat? */\n+\t\tcolon = strchr(ceil, ':');\n+\t\tif (colon)\n+\t\t\tlen = colon - ceil;\n+\t\telse\n+\t\t\tlen = strlen(ceil);\n+\n+\t\t/* \"\" would otherwise be treated like \"/\". */\n+\t\tif (len) {\n+\t\t\t/* Trim trailing slashes. */\n+\t\t\twhile (len && ceil[len-1] == '/')\n+\t\t\t\tlen--;\n+\n+\t\t\tif (!strncmp(path, ceil, len) &&\n+\t\t\t\t\tpath[len] == '/' &&\n+\t\t\t\t\tlen > max_len) {\n+\t\t\t\tmax_len = len;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!colon)\n+\t\t\tbreak;\n+\t\tceil = colon + 1;\n+\t}\n+\n+\treturn max_len;\n+}\n+\n+#if 0\n+static void test_longest_ancestor_length()\n+{\n+\tassert(longest_ancestor_length(\"/\", NULL           ) == -1);\n+\tassert(longest_ancestor_length(\"/\", \"\"             ) == -1);\n+\tassert(longest_ancestor_length(\"/\", \"/\"            ) == -1);\n+\n+\tassert(longest_ancestor_length(\"/foo\", NULL           ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"\"             ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \":\"            ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"/\"            ) ==  0);\n+\tassert(longest_ancestor_length(\"/foo\", \"/fo\"          ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"/foo\"         ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"/foo/\"        ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"/bar\"         ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"/bar/\"        ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"/foo/bar\"     ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"/foo:/bar/\"   ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"/foo/:/bar/\"  ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"/foo::/bar/\"  ) == -1);\n+\tassert(longest_ancestor_length(\"/foo\", \"/:/foo:/bar/\" ) ==  0);\n+\tassert(longest_ancestor_length(\"/foo\", \"/foo:/:/bar/\" ) ==  0);\n+\tassert(longest_ancestor_length(\"/foo\", \"/:/bar/:/foo\" ) ==  0);\n+\n+\tassert(longest_ancestor_length(\"/foo/bar\", NULL           ) == -1);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"\"             ) == -1);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/\"            ) ==  0);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/fo\"          ) == -1);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo\"         ) ==  4);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo/\"        ) ==  4);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo/ba\"      ) == -1);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/:/fo\"        ) ==  0);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo:/foo/ba\" ) ==  4);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/bar\"         ) == -1);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/bar/\"        ) == -1);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/fo:\"         ) == -1);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \":/fo\"         ) == -1);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo:/bar/\"   ) ==  4);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/:/foo:/bar/\" ) ==  4);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo:/:/bar/\" ) ==  4);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/:/bar/:/fo\"  ) ==  0);\n+\tassert(longest_ancestor_length(\"/foo/bar\", \"/:/bar/\"      ) ==  0);\n+}\n+#endif\n+\n+/*\n  * We cannot decide in this function whether we are in the work tree or\n  * not, since the config can only be read _after_ this function was called.\n  */\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 \tconst char *gitfile_dir;\n-\tint len, offset;\n+\tint len, offset, ceil_offset;\n \n \t/*\n \t * Let's assume that we are in a git repository.\n@@ -414,6 +516,8 @@ 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+\tceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);\n+\n \t/*\n \t * Test in the following order (relative to the cwd):\n \t * - .git (file containing \"gitdir: <path>\")\n@@ -443,18 +547,17 @@ 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-\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+\t\twhile (--offset > ceil_offset && cwd[offset] != '/') /* EMPTY */;\n+\t\tif (offset <= ceil_offset) {\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+\t\tchdir(\"..\");\n \t}\n \n \tinside_git_dir = 0;\ndiff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh\nnew file mode 100755\nindex 0000000..091baad\n--- /dev/null\n+++ b/t/t1504-ceiling-dirs.sh\n@@ -0,0 +1,156 @@\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+}\n+\n+test_fail() {\n+\ttest_expect_code 128 \"$1: prefix\" \\\n+\t\"git rev-parse --show-prefix\"\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/sub/di\"\n+test_prefix subdir_ceil_at_sub_di \"sub/dir/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sub/di\"\n+test_prefix subdir_ceil_at_sub_di_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+unset GIT_DIR\n+\n+\n+cd ../.. || exit 1\n+mkdir -p s/d || exit 1\n+cd s/d || exit 1\n+\n+unset GIT_CEILING_DIRS\n+test_prefix sd_no_ceil \"s/d/\"\n+\n+export GIT_CEILING_DIRS=\"\"\n+test_prefix sd_ceil_empty \"s/d/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT\"\n+test_fail sd_ceil_at_trash\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/\"\n+test_fail sd_ceil_at_trash_slash\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/s\"\n+test_fail sd_ceil_at_s\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/s/\"\n+test_fail sd_ceil_at_s_slash\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/s/d\"\n+test_prefix sd_ceil_at_sd \"s/d/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/s/d/\"\n+test_prefix sd_ceil_at_sd_slash \"s/d/\"\n+\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/su\"\n+test_prefix sd_ceil_at_su \"s/d/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/su/\"\n+test_prefix sd_ceil_at_su_slash \"s/d/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/s/di\"\n+test_prefix sd_ceil_at_s_di \"s/d/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/s/di\"\n+test_prefix sd_ceil_at_s_di_slash \"s/d/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sdi\"\n+test_prefix sd_ceil_at_sdi \"s/d/\"\n+\n+export GIT_CEILING_DIRS=\"$TRASH_ROOT/sdi\"\n+test_prefix sd_ceil_at_sdi_slash \"s/d/\"\n+\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"},{"id":"77063","messageId":"alpine.DEB.1.00.0805151958180.30431@racer","threadId":"13533","inReplyTo":"482C85C8.90804@facebook.com","subject":"Re: [PATCH v3] Add support for GIT_CEILING_DIRS","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-05-15T19:03:19Z","receivedAt":"2008-05-15T19:03:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 May 2008, David Reiss wrote:\n\n>  cache.h                 |    1 +\n>  setup.c                 |  127 ++++++++++++++++++++++++++++++++++----\n>  t/t1504-ceiling-dirs.sh |  156 +++++++++++++++++++++++++++++++++++++++++++++++\n\nBy now, I strongly believe that these changes are too large.  I am \nconvinced that what you desire can be expressed much simpler, and thus \nless error-prone.\n\nAlso, I think that your test cases are too extensive.  While it is usually \ngood to have exhaustive tests, running them takes time.  And if it takes \nso much time that hardly anybody bothers with running the test suite, \nthere are _too_ many tests.\n\n> diff --git a/setup.c b/setup.c\n> index b8fd476..fdcfae1 100644\n> --- a/setup.c\n> +++ b/setup.c\n> @@ -353,16 +353,118 @@ const char *read_gitfile_gently(const char *path)\n>  }\n>  \n>  /*\n> + * path = Canonical absolute path\n> + * prefix_list = Colon-separated list of canonical absolute paths\n> + *\n> + * Determines, for each path in parent_list, whether the \"prefix\" really\n> + * is an ancestor directory of path.  Returns the length of the longest\n> + * ancestor directory, excluding any trailing slashes, or -1 if no prefix\n> + * is an ancestry.  (Note that this means 0 is returned if prefix_list\n> + * contains \"/\".)  \"/foo\" is not considered an ancestor of \"/foobar\".\n> + * Directories are not considered to be their own ancestors.  Paths must\n> + * be in a canonical form: empty components, or \".\" or \"..\" components\n> + * are not allowed.  prefix_list may be null, which is like \"\".\n> + */\n> +static int longest_ancestor_length(const char *path, const char *prefix_list)\n> +{\n> +\tconst char *ceil, *colon;\n> +\tint max_len = -1;\n> +\n> +\tif (prefix_list == NULL)\n> +\t\treturn -1;\n> +\t/* \"/\" is a tricky edge case.  It should always return -1, though. */\n> +\tif (!strcmp(path, \"/\"))\n> +\t\treturn -1;\n> +\n> +\tceil = prefix_list;\n> +\tfor (;;) {\n> +\t\tint len;\n> +\n> +\t\t/* Add strchrnul to compat? */\n> +\t\tcolon = strchr(ceil, ':');\n> +\t\tif (colon)\n> +\t\t\tlen = colon - ceil;\n> +\t\telse\n> +\t\t\tlen = strlen(ceil);\n> +\n> +\t\t/* \"\" would otherwise be treated like \"/\". */\n> +\t\tif (len) {\n> +\t\t\t/* Trim trailing slashes. */\n> +\t\t\twhile (len && ceil[len-1] == '/')\n> +\t\t\t\tlen--;\n> +\n> +\t\t\tif (!strncmp(path, ceil, len) &&\n> +\t\t\t\t\tpath[len] == '/' &&\n> +\t\t\t\t\tlen > max_len) {\n> +\t\t\t\tmax_len = len;\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tif (!colon)\n> +\t\t\tbreak;\n> +\t\tceil = colon + 1;\n> +\t}\n> +\n> +\treturn max_len;\n> +}\n\nYou know, I wonder why I even bothered writing those responses, if you \njust ignore them.\n\n> +#if 0\n> +static void test_longest_ancestor_length()\n> +{\n> +\tassert(longest_ancestor_length(\"/\", NULL           ) == -1);\n> +\tassert(longest_ancestor_length(\"/\", \"\"             ) == -1);\n> +\tassert(longest_ancestor_length(\"/\", \"/\"            ) == -1);\n> +\n> +\tassert(longest_ancestor_length(\"/foo\", NULL           ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"\"             ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \":\"            ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/\"            ) ==  0);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/fo\"          ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/foo\"         ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/foo/\"        ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/bar\"         ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/bar/\"        ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/foo/bar\"     ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/foo:/bar/\"   ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/foo/:/bar/\"  ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/foo::/bar/\"  ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/:/foo:/bar/\" ) ==  0);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/foo:/:/bar/\" ) ==  0);\n> +\tassert(longest_ancestor_length(\"/foo\", \"/:/bar/:/foo\" ) ==  0);\n> +\n> +\tassert(longest_ancestor_length(\"/foo/bar\", NULL           ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"\"             ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/\"            ) ==  0);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/fo\"          ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo\"         ) ==  4);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo/\"        ) ==  4);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo/ba\"      ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/:/fo\"        ) ==  0);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo:/foo/ba\" ) ==  4);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/bar\"         ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/bar/\"        ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/fo:\"         ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \":/fo\"         ) == -1);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo:/bar/\"   ) ==  4);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/:/foo:/bar/\" ) ==  4);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/foo:/:/bar/\" ) ==  4);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/:/bar/:/fo\"  ) ==  0);\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/:/bar/\"      ) ==  0);\n> +}\n> +#endif\n\nThis has _no_ place in the Git source code.  Have you looked around, and \nfound similar dead code?  No?  That's because Git's source code is not a \ngraveyard of useless code bits, but it is a collection of elegant code.  \nMostly, at least.\n\nInstead of wasting my time further, I will try to come up with a better \nimplementation, as is the way of Open Source.\n\nCiao,\nDscho\n"},{"id":"77064","messageId":"482C91BD.5070504@facebook.com","threadId":"13533","inReplyTo":"alpine.DEB.1.00.0805151958180.30431@racer","subject":"Re: [PATCH v3] Add support for GIT_CEILING_DIRS","fromName":"David Reiss","fromEmail":"dreiss@facebook.com","sentAt":"2008-05-15T19:40:45Z","receivedAt":"2008-05-15T19:40:45Z","isPatch":true,"sender":{"key":"dreiss@facebook.com","avatar":null},"body":"> By now, I strongly believe that these changes are too large.  I am\n> convinced that what you desire can be expressed much simpler, and thus\n> less error-prone.\nMost of the code is in the one function to parse out the colon-separated\nenvironment variable value and compute the longest directory prefix.\nI'm not convinced this can be made much simpler.  (Using strtok_r could\nhelp, but would require an allocation.)  Most of the rest of the changes\nare test code and indentation.\n \n> Also, I think that your test cases are too extensive.  While it is usually\n> good to have exhaustive tests, running them takes time.  And if it takes\n> so much time that hardly anybody bothers with running the test suite,\n> there are _too_ many tests.\nI am more than happy to remove most of them, leaving only basic sanity\nchecks.  However, I would prefer to leave them in but comment them out,\nso that if I or someone else wants to modify this code later, they would\nbe able to run a more extensive test suite.  I'll submit a modified\npatch with this change.\n\n> You know, I wonder why I even bothered writing those responses, if you\n> just ignore them.\nI must say that I am very confused.  I thought I followed all of your\nresponses to the letter.  Could you please be more specific about the\nones I missed?  I'm happy to make further changes.\n\n> This has _no_ place in the Git source code.  Have you looked around, and\n> found similar dead code?  No?  That's because Git's source code is not a\n> graveyard of useless code bits, but it is a collection of elegant code. \n> Mostly, at least.\nAs I stated in \"PATCH v2\", I was unsure what the convention was for unit\ntests like this.  Most of the git code is (obviously) functional tests,\nbut it is impossible to test how this code would behave with a git\ndirectory under \"/\" using a functional test, unless it was run as root.\nSomeone just pointed out to me that there are some C-based tests (like\ntest-sha1) that are run from \"make test\".  I guess I can move the test\nfunction to a new one of those, but it will require making\nlongest_ancestor_length extern.\n\n> Instead of wasting my time further, I will try to come up with a better\n> implementation, as is the way of Open Source.\nI am sorry if this has wasted your time.  I really have been trying to\nincorporate your feedback into my patch, and the code has definitely\nimproved as a result.  However, my main goal is simply to get this\nfeature working (I have already patched it into my own git build, and it\nhas saved me a lot of time), so if you come up with a better\nimplementation, that would be great!\n\n--David\n"},{"id":"77066","messageId":"7vej83gxjm.fsf@gitster.siamese.dyndns.org","threadId":"13533","inReplyTo":"482C85C8.90804@facebook.com","subject":"Re: [PATCH v3] Add support for GIT_CEILING_DIRS","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-05-15T19:46:53Z","receivedAt":"2008-05-15T19:46:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Reiss <dreiss@facebook.com> writes:\n\n> + * ....  Paths must\n> + * be in a canonical form: empty components, or \".\" or \"..\" components\n> + * are not allowed.  prefix_list may be null, which is like \"\".\n\nThe caller starts from cwd[] and chomps, so you can safely assume that it\nwould not feed anything problematic.  But prefix_list comes from user's\nenvironment, and it is easy to make mistakes like doubled slashes (which\nyou seem to take care) and also is tempting to use \"..\" when specifying\nthe ceiling (e.g. \"CEIL=$HOME/..\").  Perhaps canonicalizing the ceiling\nwould make this easier to use for end users?\n\n\n> +#if 0\n> +static void test_longest_ancestor_length()\n> +{\n> ...\n> +\tassert(longest_ancestor_length(\"/foo/bar\", \"/:/bar/\"      ) ==  0);\n\nNo test for nonsense/invalid input, like \"::/foo\" for prefix_list?\n\n> diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh\n> new file mode 100755\n> index 0000000..091baad\n> --- /dev/null\n> +++ b/t/t1504-ceiling-dirs.sh\n> @@ -0,0 +1,156 @@\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> +}\n> +\n> +test_fail() {\n> +\ttest_expect_code 128 \"$1: prefix\" \\\n> +\t\"git rev-parse --show-prefix\"\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\nPortability.  Instead write this as two separate statements, please.\n\n        VAR=val\n        export VAR\n\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> ...\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\nYou may also check stuff like \"sub//dir\" and \"::sub/dir/\".\n\nHow well would this colon separated list work with msys folks?\n"},{"id":"77072","messageId":"alpine.DEB.1.00.0805152055280.30431@racer","threadId":"13533","inReplyTo":"alpine.DEB.1.00.0805151958180.30431@racer","subject":"[PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-05-15T20:27:54Z","receivedAt":"2008-05-15T20:27:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nIn certain setups, trying to access a non-existing .git/ can take quite\nsome time, for example when the directory is an automount directory.\n\nAllow the user to specify directories where Git should stop looking for\na .git/ directory: GIT_CEILING_DIRECTORIES, if set, is expected to be\na colon delimited list of such barrier directories.\n\nNote: if GIT_CEILING_DIRECTORIES=/a/b and your current working directory \nis /a, Git will _not_ stop looking.\n\nNote2: you must not specify the directories with trailing slashes.\n\nInitial implementation by David Reiss.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tShow me the bugs!\n\n\tI just checked: the \"/\" issue you were referring to is most likely\n\tthe fact that \"git rev-parse --git-dir\" would return \"//.git\"\n\tinstead of \"/.git\" (if that is the appropriate GIT_DIR).\n\n\tThis is the original behavior (without this patch), and IMO a \n\tseparate issue, which might not even need fixing.\n\n Documentation/git.txt          |    6 +++++\n cache.h                        |    2 +\n path.c                         |   19 ++++++++++++++++\n setup.c                        |   11 +++++++-\n t/t1504-ceiling-directories.sh |   46 ++++++++++++++++++++++++++++++++++++++++\n t/test-lib.sh                  |    1 +\n 6 files changed, 83 insertions(+), 2 deletions(-)\n create mode 100644 t/t1504-ceiling-directories.sh\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex adcd3e0..a12d1f8 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -415,6 +415,12 @@ 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+\tIf set (to a colon delimited list of absolute directories), Git\n+\twill refuse to look for the .git/ directory further when hitting\n+\tone of those directories (otherwise it would traverse the parent\n+\tdirectories until hitting the root directory).\n+\n git Commits\n ~~~~~~~~~~~\n 'GIT_AUTHOR_NAME'::\ndiff --git a/cache.h b/cache.h\nindex a8638b1..c31b4c7 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -300,6 +300,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_DIRECTORIES_ENVIRONMENT \"GIT_CEILING_DIRECTORIES\"\n #define GITATTRIBUTES_FILE \".gitattributes\"\n #define INFOATTRIBUTES_FILE \"info/attributes\"\n #define ATTRIBUTE_MACRO_PREFIX \"[attr]\"\n@@ -522,6 +523,7 @@ static inline int is_absolute_path(const char *path)\n \treturn path[0] == '/';\n }\n const char *make_absolute_path(const char *path);\n+int longest_prefix(const char *path, const char *prefix_list);\n \n /* Read and unpack a sha1 file into memory, write memory to a sha1 file */\n extern int sha1_object_info(const unsigned char *, unsigned long *);\ndiff --git a/path.c b/path.c\nindex b7c24a2..c0d7364 100644\n--- a/path.c\n+++ b/path.c\n@@ -357,3 +357,22 @@ const char *make_absolute_path(const char *path)\n \n \treturn buf;\n }\n+\n+int longest_prefix(const char *path, const char *prefix_list)\n+{\n+\tint max_length = 0, length = 0, i;\n+\n+\tfor (i = 0; prefix_list[i]; i++)\n+\t\tif (prefix_list[i] == ':') {\n+\t\t\tif (length > max_length)\n+\t\t\t\tmax_length = length;\n+\t\t\tlength = 0;\n+\t\t}\n+\t\telse if (length >= 0) {\n+\t\t\tif (prefix_list[i] == path[length])\n+\t\t\t\tlength++;\n+\t\t\telse\n+\t\t\t\tlength = -1;\n+\t\t}\n+\treturn max_length > length ? max_length : length;\n+}\ndiff --git a/setup.c b/setup.c\nindex 9e9a2b1..cece3e4 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -365,10 +365,13 @@ const char *read_gitfile_gently(const char *path)\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 *ceiling_directories =\n+\t\tgetenv(CEILING_DIRECTORIES_ENVIRONMENT);\n \tstatic char cwd[PATH_MAX+1];\n \tconst char *gitdirenv;\n \tconst char *gitfile_dir;\n \tint len, offset;\n+\tint min_offset = 0;\n \n \t/*\n \t * Let's assume that we are in a git repository.\n@@ -422,6 +425,9 @@ 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+\tif (ceiling_directories)\n+\t\tmin_offset = longest_prefix(cwd, ceiling_directories);\n+\n \t/*\n \t * Test in the following order (relative to the cwd):\n \t * - .git (file containing \"gitdir: <path>\")\n@@ -453,7 +459,7 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\t}\n \t\tchdir(\"..\");\n \t\tdo {\n-\t\t\tif (!offset) {\n+\t\t\tif (offset <= min_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@@ -462,7 +468,8 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\t\t\t}\n \t\t\t\tdie(\"Not a git repository\");\n \t\t\t}\n-\t\t} while (cwd[--offset] != '/');\n+\t\t} while (offset > min_offset &&\n+\t\t\t\t--offset >=0 && cwd[offset] != '/');\n \t}\n \n \tinside_git_dir = 0;\ndiff --git a/t/t1504-ceiling-directories.sh b/t/t1504-ceiling-directories.sh\nnew file mode 100644\nindex 0000000..1d8ef0b\n--- /dev/null\n+++ b/t/t1504-ceiling-directories.sh\n@@ -0,0 +1,46 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Johannes E. Schindelin\n+#\n+\n+test_description='test limiting with GIT_CEILING_DIRECTORIES'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\n+\tCWD=\"$(pwd -P)\" &&\n+\tmkdir subdir\n+\n+'\n+\n+test_expect_success 'without GIT_CEILING_DIRECTORIES' '\n+\n+\ttest .git = \"$(git rev-parse --git-dir)\" &&\n+\t(cd subdir && git rev-parse --git-dir) &&\n+\techo \"$CWD\" &&\n+\ttest \"$CWD/.git\" = \"$(cd subdir && git rev-parse --git-dir)\"\n+\n+'\n+\n+test_expect_success 'with non-matching ceiling directory' '\n+\n+\ttest \"$(GIT_CEILING_DIRECTORIES=\"$CWD/X\" \\\n+\t\tgit rev-parse --git-dir)\" = .git\n+\n+'\n+\n+test_expect_success 'with matching ceiling directories' '\n+\n+\tGIT_CEILING_DIRECTORIES=\"$CWD/X:$CWD/subdir\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && test_must_fail git rev-parse --git-dir) &&\n+\tgit rev-parse --git-dir &&\n+\tGIT_CEILING_DIRECTORIES=\"$CWD/subdir:$CWD/X\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && test_must_fail git rev-parse --git-dir) &&\n+\tgit rev-parse --git-dir\n+\n+'\n+\n+test_done\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 5002fb0..c3a3167 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_DIRECTORIES\n unset SHA1_FILE_DIRECTORIES\n unset SHA1_FILE_DIRECTORY\n GIT_MERGE_VERBOSITY=5\n-- \n1.5.5.1.425.g5f464\n"},{"id":"77073","messageId":"alpine.DEB.1.00.0805152131390.30431@racer","threadId":"13533","inReplyTo":"7vej83gxjm.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH v3] Add support for GIT_CEILING_DIRS","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-05-15T20:34:24Z","receivedAt":"2008-05-15T20:34:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 May 2008, Junio C Hamano wrote:\n\n> David Reiss <dreiss@facebook.com> writes:\n> \n> > + * ....  Paths must\n> > + * be in a canonical form: empty components, or \".\" or \"..\" components\n> > + * are not allowed.  prefix_list may be null, which is like \"\".\n> \n> The caller starts from cwd[] and chomps, so you can safely assume that \n> it would not feed anything problematic.  But prefix_list comes from \n> user's environment, and it is easy to make mistakes like doubled slashes \n> (which you seem to take care) and also is tempting to use \"..\" when \n> specifying the ceiling (e.g. \"CEIL=$HOME/..\").  Perhaps canonicalizing \n> the ceiling would make this easier to use for end users?\n\nIs this not going too far?  I mean, CEILING_DIRECTORIES is already a very \nspecial case.\n\n> How well would this colon separated list work with msys folks?\n\nNot well at all.  At least for the moment, I think setting this variable \nwould fail (since it would be rewritten into a Windows-PATH-style string).  \nThough honestly, I have no idea what getcwd() does on MinGW.\n\nSteffen is working on that rewriting stuff, so maybe it will be a \nnon-issue, eventually.\n\nCiao,\nDscho\n"},{"id":"77075","messageId":"482CA693.3060602@facebook.com","threadId":"13533","inReplyTo":"alpine.DEB.1.00.0805152055280.30431@racer","subject":"Re: [PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"David Reiss","fromEmail":"dreiss@facebook.com","sentAt":"2008-05-15T21:09:39Z","receivedAt":"2008-05-15T21:09:39Z","isPatch":true,"sender":{"key":"dreiss@facebook.com","avatar":null},"body":"longest_prefix is just a textual check.  It doesn't verify that the prefix\nis actually a full directory component of the cwd.  For example...\n\ndreiss@dreiss-vmware:/tmp$ mkdir test\ndreiss@dreiss-vmware:/tmp$ cd test\ndreiss@dreiss-vmware:/tmp/test$ git init\nInitialized empty Git repository in .git/\ndreiss@dreiss-vmware:master:test$ mkdir abcd\ndreiss@dreiss-vmware:master:test$ cd abcd\ndreiss@dreiss-vmware:master:test/abcd$ git rev-parse --show-prefix\nabcd/\ndreiss@dreiss-vmware:master:test/abcd$ ~/git-ceil/git rev-parse --show-prefix\nabcd/\ndreiss@dreiss-vmware:master:test/abcd$ GIT_CEILING_DIRECTORIES=/tmp/test/ab ~/git-ceil/git rev-parse --show-prefix\nfatal: Not a git repository\ndreiss@dreiss-vmware:master:test/abcd:128$ \n\n\"/tmp/test/ab\" is a textual prefix of the cwd, but it should not prevent\n\"/tmp/test/.git\" from being discovered.  For what it's worth, my test cases\ncheck this behavior.  Ironically, I think this could be fixed by requiring\nthe ceiling directories to have trailing slashes.\n\nAlso, I think it is better to move the 'chdir(\"..\")' after the do loop,\nso that git won't even chdir up into the ceiling directory.  This actually\ndoesn't matter to me, but I figured that it might be nice for someone.\n\nFinally, just a small thing.  The documentation still says \"GIT_CEILING_DIRS\".\n\n--David\n\nJohannes Schindelin wrote:\n> \n> In certain setups, trying to access a non-existing .git/ can take quite\n> some time, for example when the directory is an automount directory.\n> \n> Allow the user to specify directories where Git should stop looking for\n> a .git/ directory: GIT_CEILING_DIRECTORIES, if set, is expected to be\n> a colon delimited list of such barrier directories.\n> \n> Note: if GIT_CEILING_DIRECTORIES=/a/b and your current working directory\n> is /a, Git will _not_ stop looking.\n> \n> Note2: you must not specify the directories with trailing slashes.\n> \n> Initial implementation by David Reiss.\n> \n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n> \n>         Show me the bugs!\n> \n>         I just checked: the \"/\" issue you were referring to is most likely\n>         the fact that \"git rev-parse --git-dir\" would return \"//.git\"\n>         instead of \"/.git\" (if that is the appropriate GIT_DIR).\n> \n>         This is the original behavior (without this patch), and IMO a\n>         separate issue, which might not even need fixing.\n> \n>  Documentation/git.txt          |    6 +++++\n>  cache.h                        |    2 +\n>  path.c                         |   19 ++++++++++++++++\n>  setup.c                        |   11 +++++++-\n>  t/t1504-ceiling-directories.sh |   46\n> ++++++++++++++++++++++++++++++++++++++++\n>  t/test-lib.sh                  |    1 +\n>  6 files changed, 83 insertions(+), 2 deletions(-)\n>  create mode 100644 t/t1504-ceiling-directories.sh\n> \n> diff --git a/Documentation/git.txt b/Documentation/git.txt\n> index adcd3e0..a12d1f8 100644\n> --- a/Documentation/git.txt\n> +++ b/Documentation/git.txt\n> @@ -415,6 +415,12 @@ git so take care if using Cogito etc.\n>         This can also be controlled by the '--work-tree' command line\n>         option and the core.worktree configuration variable.\n> \n> +'GIT_CEILING_DIRS'::\n> +       If set (to a colon delimited list of absolute directories), Git\n> +       will refuse to look for the .git/ directory further when hitting\n> +       one of those directories (otherwise it would traverse the parent\n> +       directories until hitting the root directory).\n> +\n>  git Commits\n>  ~~~~~~~~~~~\n>  'GIT_AUTHOR_NAME'::\n> diff --git a/cache.h b/cache.h\n> index a8638b1..c31b4c7 100644\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -300,6 +300,7 @@ static inline enum object_type object_type(unsigned\n> 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_DIRECTORIES_ENVIRONMENT \"GIT_CEILING_DIRECTORIES\"\n>  #define GITATTRIBUTES_FILE \".gitattributes\"\n>  #define INFOATTRIBUTES_FILE \"info/attributes\"\n>  #define ATTRIBUTE_MACRO_PREFIX \"[attr]\"\n> @@ -522,6 +523,7 @@ static inline int is_absolute_path(const char *path)\n>         return path[0] == '/';\n>  }\n>  const char *make_absolute_path(const char *path);\n> +int longest_prefix(const char *path, const char *prefix_list);\n> \n>  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */\n>  extern int sha1_object_info(const unsigned char *, unsigned long *);\n> diff --git a/path.c b/path.c\n> index b7c24a2..c0d7364 100644\n> --- a/path.c\n> +++ b/path.c\n> @@ -357,3 +357,22 @@ const char *make_absolute_path(const char *path)\n> \n>         return buf;\n>  }\n> +\n> +int longest_prefix(const char *path, const char *prefix_list)\n> +{\n> +       int max_length = 0, length = 0, i;\n> +\n> +       for (i = 0; prefix_list[i]; i++)\n> +               if (prefix_list[i] == ':') {\n> +                       if (length > max_length)\n> +                               max_length = length;\n> +                       length = 0;\n> +               }\n> +               else if (length >= 0) {\n> +                       if (prefix_list[i] == path[length])\n> +                               length++;\n> +                       else\n> +                               length = -1;\n> +               }\n> +       return max_length > length ? max_length : length;\n> +}\n> diff --git a/setup.c b/setup.c\n> index 9e9a2b1..cece3e4 100644\n> --- a/setup.c\n> +++ b/setup.c\n> @@ -365,10 +365,13 @@ const char *read_gitfile_gently(const char *path)\n>  const char *setup_git_directory_gently(int *nongit_ok)\n>  {\n>         const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);\n> +       const char *ceiling_directories =\n> +               getenv(CEILING_DIRECTORIES_ENVIRONMENT);\n>         static char cwd[PATH_MAX+1];\n>         const char *gitdirenv;\n>         const char *gitfile_dir;\n>         int len, offset;\n> +       int min_offset = 0;\n> \n>         /*\n>          * Let's assume that we are in a git repository.\n> @@ -422,6 +425,9 @@ const char *setup_git_directory_gently(int *nongit_ok)\n>         if (!getcwd(cwd, sizeof(cwd)-1))\n>                 die(\"Unable to read current working directory\");\n> \n> +       if (ceiling_directories)\n> +               min_offset = longest_prefix(cwd, ceiling_directories);\n> +\n>         /*\n>          * Test in the following order (relative to the cwd):\n>          * - .git (file containing \"gitdir: <path>\")\n> @@ -453,7 +459,7 @@ const char *setup_git_directory_gently(int *nongit_ok)\n>                 }\n>                 chdir(\"..\");\n>                 do {\n> -                       if (!offset) {\n> +                       if (offset <= min_offset) {\n>                                 if (nongit_ok) {\n>                                         if (chdir(cwd))\n>                                                 die(\"Cannot come back to\n> cwd\");\n> @@ -462,7 +468,8 @@ const char *setup_git_directory_gently(int *nongit_ok)\n>                                 }\n>                                 die(\"Not a git repository\");\n>                         }\n> -               } while (cwd[--offset] != '/');\n> +               } while (offset > min_offset &&\n> +                               --offset >=0 && cwd[offset] != '/');\n>         }\n> \n>         inside_git_dir = 0;\n> diff --git a/t/t1504-ceiling-directories.sh b/t/t1504-ceiling-directories.sh\n> new file mode 100644\n> index 0000000..1d8ef0b\n> --- /dev/null\n> +++ b/t/t1504-ceiling-directories.sh\n> @@ -0,0 +1,46 @@\n> +#!/bin/sh\n> +#\n> +# Copyright (c) 2007 Johannes E. Schindelin\n> +#\n> +\n> +test_description='test limiting with GIT_CEILING_DIRECTORIES'\n> +\n> +. ./test-lib.sh\n> +\n> +test_expect_success 'setup' '\n> +\n> +       CWD=\"$(pwd -P)\" &&\n> +       mkdir subdir\n> +\n> +'\n> +\n> +test_expect_success 'without GIT_CEILING_DIRECTORIES' '\n> +\n> +       test .git = \"$(git rev-parse --git-dir)\" &&\n> +       (cd subdir && git rev-parse --git-dir) &&\n> +       echo \"$CWD\" &&\n> +       test \"$CWD/.git\" = \"$(cd subdir && git rev-parse --git-dir)\"\n> +\n> +'\n> +\n> +test_expect_success 'with non-matching ceiling directory' '\n> +\n> +       test \"$(GIT_CEILING_DIRECTORIES=\"$CWD/X\" \\\n> +               git rev-parse --git-dir)\" = .git\n> +\n> +'\n> +\n> +test_expect_success 'with matching ceiling directories' '\n> +\n> +       GIT_CEILING_DIRECTORIES=\"$CWD/X:$CWD/subdir\" &&\n> +       export GIT_CEILING_DIRECTORIES &&\n> +       (cd subdir && test_must_fail git rev-parse --git-dir) &&\n> +       git rev-parse --git-dir &&\n> +       GIT_CEILING_DIRECTORIES=\"$CWD/subdir:$CWD/X\" &&\n> +       export GIT_CEILING_DIRECTORIES &&\n> +       (cd subdir && test_must_fail git rev-parse --git-dir) &&\n> +       git rev-parse --git-dir\n> +\n> +'\n> +\n> +test_done\n> diff --git a/t/test-lib.sh b/t/test-lib.sh\n> index 5002fb0..c3a3167 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_DIRECTORIES\n>  unset SHA1_FILE_DIRECTORIES\n>  unset SHA1_FILE_DIRECTORY\n>  GIT_MERGE_VERBOSITY=5\n> --\n> 1.5.5.1.425.g5f464\n> \n"},{"id":"77077","messageId":"alpine.DEB.1.00.0805152327440.30431@racer","threadId":"13533","inReplyTo":"482CA693.3060602@facebook.com","subject":"Re: [PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-05-15T22:29:47Z","receivedAt":"2008-05-15T22:29:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 May 2008, David Reiss wrote:\n\n> longest_prefix is just a textual check.  It doesn't verify that the prefix\n> is actually a full directory component of the cwd.\n\nOkay.\n\n> Also, I think it is better to move the 'chdir(\"..\")' after the do loop, \n> so that git won't even chdir up into the ceiling directory.  This \n> actually doesn't matter to me, but I figured that it might be nice for \n> someone.\n\nI'd rather go with the minimal diff, unless there is a good reason to \nchange it.\n\n> Finally, just a small thing.  The documentation still says \n> \"GIT_CEILING_DIRS\".\n\nOkay.\n\nHow about this on top (still pretty simple):\n\n---\n\n Documentation/git.txt          |    2 +-\n path.c                         |   10 ++++++++--\n t/t1504-ceiling-directories.sh |    8 ++++++++\n 3 files changed, 17 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex a12d1f8..e4413bf 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -415,7 +415,7 @@ 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+'GIT_CEILING_DIRECTORIES'::\n \tIf set (to a colon delimited list of absolute directories), Git\n \twill refuse to look for the .git/ directory further when hitting\n \tone of those directories (otherwise it would traverse the parent\ndiff --git a/path.c b/path.c\nindex c0d7364..a097ecc 100644\n--- a/path.c\n+++ b/path.c\n@@ -358,13 +358,18 @@ const char *make_absolute_path(const char *path)\n \treturn buf;\n }\n \n+static int is_separator(char c)\n+{\n+\treturn !c || c == '/';\n+}\n+\n int longest_prefix(const char *path, const char *prefix_list)\n {\n \tint max_length = 0, length = 0, i;\n \n \tfor (i = 0; prefix_list[i]; i++)\n \t\tif (prefix_list[i] == ':') {\n-\t\t\tif (length > max_length)\n+\t\t\tif (length > max_length && is_separator(path[length]))\n \t\t\t\tmax_length = length;\n \t\t\tlength = 0;\n \t\t}\n@@ -374,5 +379,6 @@ int longest_prefix(const char *path, const char *prefix_list)\n \t\t\telse\n \t\t\t\tlength = -1;\n \t\t}\n-\treturn max_length > length ? max_length : length;\n+\treturn max_length > length || !is_separator(path[length]) ?\n+\t\tmax_length : length;\n }\ndiff --git a/t/t1504-ceiling-directories.sh b/t/t1504-ceiling-directories.sh\nindex 1d8ef0b..6c8757d 100644\n--- a/t/t1504-ceiling-directories.sh\n+++ b/t/t1504-ceiling-directories.sh\n@@ -43,4 +43,12 @@ test_expect_success 'with matching ceiling directories' '\n \n '\n \n+test_expect_success 'with non-directory prefix' '\n+\n+\tGIT_CEILING_DIRECTORIES=\"$CWD/sub\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && git rev-parse --git-dir)\n+\n+'\n+\n test_done\n"},{"id":"77080","messageId":"482CBCF2.6030202@facebook.com","threadId":"13533","inReplyTo":"alpine.DEB.1.00.0805152327440.30431@racer","subject":"Re: [PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"David Reiss","fromEmail":"dreiss@facebook.com","sentAt":"2008-05-15T22:45:06Z","receivedAt":"2008-05-15T22:45:06Z","isPatch":true,"sender":{"key":"dreiss@facebook.com","avatar":null},"body":"This meets my needs.\n\nJunio, if you'd like, I can incorporate your suggestions of normalizing paths\ninternally and testing more corner cases.  But if you just want to take this\nversion, I'll be fine.\n\n--David\n\nJohannes Schindelin wrote:\n> Hi,\n> \n> On Thu, 15 May 2008, David Reiss wrote:\n> \n>> longest_prefix is just a textual check.  It doesn't verify that the prefix\n>> is actually a full directory component of the cwd.\n> \n> Okay.\n> \n>> Also, I think it is better to move the 'chdir(\"..\")' after the do loop,\n>> so that git won't even chdir up into the ceiling directory.  This\n>> actually doesn't matter to me, but I figured that it might be nice for\n>> someone.\n> \n> I'd rather go with the minimal diff, unless there is a good reason to\n> change it.\n> \n>> Finally, just a small thing.  The documentation still says\n>> \"GIT_CEILING_DIRS\".\n> \n> Okay.\n> \n> How about this on top (still pretty simple):\n> \n> ---\n> \n>  Documentation/git.txt          |    2 +-\n>  path.c                         |   10 ++++++++--\n>  t/t1504-ceiling-directories.sh |    8 ++++++++\n>  3 files changed, 17 insertions(+), 3 deletions(-)\n> \n> diff --git a/Documentation/git.txt b/Documentation/git.txt\n> index a12d1f8..e4413bf 100644\n> --- a/Documentation/git.txt\n> +++ b/Documentation/git.txt\n> @@ -415,7 +415,7 @@ git so take care if using Cogito etc.\n>         This can also be controlled by the '--work-tree' command line\n>         option and the core.worktree configuration variable.\n> \n> -'GIT_CEILING_DIRS'::\n> +'GIT_CEILING_DIRECTORIES'::\n>         If set (to a colon delimited list of absolute directories), Git\n>         will refuse to look for the .git/ directory further when hitting\n>         one of those directories (otherwise it would traverse the parent\n> diff --git a/path.c b/path.c\n> index c0d7364..a097ecc 100644\n> --- a/path.c\n> +++ b/path.c\n> @@ -358,13 +358,18 @@ const char *make_absolute_path(const char *path)\n>         return buf;\n>  }\n> \n> +static int is_separator(char c)\n> +{\n> +       return !c || c == '/';\n> +}\n> +\n>  int longest_prefix(const char *path, const char *prefix_list)\n>  {\n>         int max_length = 0, length = 0, i;\n> \n>         for (i = 0; prefix_list[i]; i++)\n>                 if (prefix_list[i] == ':') {\n> -                       if (length > max_length)\n> +                       if (length > max_length &&\n> is_separator(path[length]))\n>                                 max_length = length;\n>                         length = 0;\n>                 }\n> @@ -374,5 +379,6 @@ int longest_prefix(const char *path, const char\n> *prefix_list)\n>                         else\n>                                 length = -1;\n>                 }\n> -       return max_length > length ? max_length : length;\n> +       return max_length > length || !is_separator(path[length]) ?\n> +               max_length : length;\n>  }\n> diff --git a/t/t1504-ceiling-directories.sh b/t/t1504-ceiling-directories.sh\n> index 1d8ef0b..6c8757d 100644\n> --- a/t/t1504-ceiling-directories.sh\n> +++ b/t/t1504-ceiling-directories.sh\n> @@ -43,4 +43,12 @@ test_expect_success 'with matching ceiling directories' '\n> \n>  '\n> \n> +test_expect_success 'with non-directory prefix' '\n> +\n> +       GIT_CEILING_DIRECTORIES=\"$CWD/sub\" &&\n> +       export GIT_CEILING_DIRECTORIES &&\n> +       (cd subdir && git rev-parse --git-dir)\n> +\n> +'\n> +\n>  test_done\n> \n"},{"id":"77084","messageId":"alpine.DEB.1.00.0805160012310.30431@racer","threadId":"13533","inReplyTo":"482CBCF2.6030202@facebook.com","subject":"[SQUASHED PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-05-15T23:27:28Z","receivedAt":"2008-05-15T23:27:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nIn certain setups, trying to access a non-existing .git/ can take quite\nsome time, for example when the directory is an automount directory.\n\nAllow the user to specify directories where Git would stop looking for\na .git/ directory: GIT_CEILING_DIRECTORIES, if set, is expected to be\na colon delimited list of such barrier directories.\n\nNote: if GIT_CEILING_DIRECTORIES=/a/b and your current working directory\nis /a, Git will _not_ stop looking.\n\nNote2: you must not specify the directories with trailing slashes.\n\nInitial implementation by David Reiss.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tOn Thu, 15 May 2008, David Reiss wrote:\n\n\t> This meets my needs.\n\t> \n\t> Junio, if you'd like, I can incorporate your suggestions of \n\t> normalizing paths internally and testing more corner cases.  But\n\t> if you just want to take this version, I'll be fine.\n\n\tFor your pleasure, the combined patch of my work (so that it is \n\teasier to hack on normalizing paths, if you still want that --\n\tyou might want to look at get_pathspec() and its history in that \n\tcase).\n\n Documentation/git.txt          |    6 ++++\n cache.h                        |    2 +\n path.c                         |   25 ++++++++++++++++++\n setup.c                        |   11 ++++++-\n t/t1504-ceiling-directories.sh |   54 ++++++++++++++++++++++++++++++++++++++++\n t/test-lib.sh                  |    1 +\n 6 files changed, 97 insertions(+), 2 deletions(-)\n create mode 100644 t/t1504-ceiling-directories.sh\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex adcd3e0..e4413bf 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -415,6 +415,12 @@ 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_DIRECTORIES'::\n+\tIf set (to a colon delimited list of absolute directories), Git\n+\twill refuse to look for the .git/ directory further when hitting\n+\tone of those directories (otherwise it would traverse the parent\n+\tdirectories until hitting the root directory).\n+\n git Commits\n ~~~~~~~~~~~\n 'GIT_AUTHOR_NAME'::\ndiff --git a/cache.h b/cache.h\nindex a8638b1..c31b4c7 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -300,6 +300,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_DIRECTORIES_ENVIRONMENT \"GIT_CEILING_DIRECTORIES\"\n #define GITATTRIBUTES_FILE \".gitattributes\"\n #define INFOATTRIBUTES_FILE \"info/attributes\"\n #define ATTRIBUTE_MACRO_PREFIX \"[attr]\"\n@@ -522,6 +523,7 @@ static inline int is_absolute_path(const char *path)\n \treturn path[0] == '/';\n }\n const char *make_absolute_path(const char *path);\n+int longest_prefix(const char *path, const char *prefix_list);\n \n /* Read and unpack a sha1 file into memory, write memory to a sha1 file */\n extern int sha1_object_info(const unsigned char *, unsigned long *);\ndiff --git a/path.c b/path.c\nindex b7c24a2..a097ecc 100644\n--- a/path.c\n+++ b/path.c\n@@ -357,3 +357,28 @@ const char *make_absolute_path(const char *path)\n \n \treturn buf;\n }\n+\n+static int is_separator(char c)\n+{\n+\treturn !c || c == '/';\n+}\n+\n+int longest_prefix(const char *path, const char *prefix_list)\n+{\n+\tint max_length = 0, length = 0, i;\n+\n+\tfor (i = 0; prefix_list[i]; i++)\n+\t\tif (prefix_list[i] == ':') {\n+\t\t\tif (length > max_length && is_separator(path[length]))\n+\t\t\t\tmax_length = length;\n+\t\t\tlength = 0;\n+\t\t}\n+\t\telse if (length >= 0) {\n+\t\t\tif (prefix_list[i] == path[length])\n+\t\t\t\tlength++;\n+\t\t\telse\n+\t\t\t\tlength = -1;\n+\t\t}\n+\treturn max_length > length || !is_separator(path[length]) ?\n+\t\tmax_length : length;\n+}\ndiff --git a/setup.c b/setup.c\nindex 9e9a2b1..cece3e4 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -365,10 +365,13 @@ const char *read_gitfile_gently(const char *path)\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 *ceiling_directories =\n+\t\tgetenv(CEILING_DIRECTORIES_ENVIRONMENT);\n \tstatic char cwd[PATH_MAX+1];\n \tconst char *gitdirenv;\n \tconst char *gitfile_dir;\n \tint len, offset;\n+\tint min_offset = 0;\n \n \t/*\n \t * Let's assume that we are in a git repository.\n@@ -422,6 +425,9 @@ 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+\tif (ceiling_directories)\n+\t\tmin_offset = longest_prefix(cwd, ceiling_directories);\n+\n \t/*\n \t * Test in the following order (relative to the cwd):\n \t * - .git (file containing \"gitdir: <path>\")\n@@ -453,7 +459,7 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\t}\n \t\tchdir(\"..\");\n \t\tdo {\n-\t\t\tif (!offset) {\n+\t\t\tif (offset <= min_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@@ -462,7 +468,8 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\t\t\t}\n \t\t\t\tdie(\"Not a git repository\");\n \t\t\t}\n-\t\t} while (cwd[--offset] != '/');\n+\t\t} while (offset > min_offset &&\n+\t\t\t\t--offset >=0 && cwd[offset] != '/');\n \t}\n \n \tinside_git_dir = 0;\ndiff --git a/t/t1504-ceiling-directories.sh b/t/t1504-ceiling-directories.sh\nnew file mode 100644\nindex 0000000..6c8757d\n--- /dev/null\n+++ b/t/t1504-ceiling-directories.sh\n@@ -0,0 +1,54 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Johannes E. Schindelin\n+#\n+\n+test_description='test limiting with GIT_CEILING_DIRECTORIES'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\n+\tCWD=\"$(pwd -P)\" &&\n+\tmkdir subdir\n+\n+'\n+\n+test_expect_success 'without GIT_CEILING_DIRECTORIES' '\n+\n+\ttest .git = \"$(git rev-parse --git-dir)\" &&\n+\t(cd subdir && git rev-parse --git-dir) &&\n+\techo \"$CWD\" &&\n+\ttest \"$CWD/.git\" = \"$(cd subdir && git rev-parse --git-dir)\"\n+\n+'\n+\n+test_expect_success 'with non-matching ceiling directory' '\n+\n+\ttest \"$(GIT_CEILING_DIRECTORIES=\"$CWD/X\" \\\n+\t\tgit rev-parse --git-dir)\" = .git\n+\n+'\n+\n+test_expect_success 'with matching ceiling directories' '\n+\n+\tGIT_CEILING_DIRECTORIES=\"$CWD/X:$CWD/subdir\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && test_must_fail git rev-parse --git-dir) &&\n+\tgit rev-parse --git-dir &&\n+\tGIT_CEILING_DIRECTORIES=\"$CWD/subdir:$CWD/X\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && test_must_fail git rev-parse --git-dir) &&\n+\tgit rev-parse --git-dir\n+\n+'\n+\n+test_expect_success 'with non-directory prefix' '\n+\n+\tGIT_CEILING_DIRECTORIES=\"$CWD/sub\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && git rev-parse --git-dir)\n+\n+'\n+\n+test_done\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 5002fb0..c3a3167 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_DIRECTORIES\n unset SHA1_FILE_DIRECTORIES\n unset SHA1_FILE_DIRECTORY\n GIT_MERGE_VERBOSITY=5\n-- \n1.5.5.1.425.g5f464.dirty\n"},{"id":"77101","messageId":"482D2F9D.4080809@viscovery.net","threadId":"13533","inReplyTo":"alpine.DEB.1.00.0805160012310.30431@racer","subject":"Re: [SQUASHED PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2008-05-16T06:54:21Z","receivedAt":"2008-05-16T06:54:21Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Johannes Schindelin schrieb:\n> +'GIT_CEILING_DIRECTORIES'::\n> +\tIf set (to a colon delimited list of absolute directories), Git\n> +\twill refuse to look for the .git/ directory further when hitting\n> +\tone of those directories (otherwise it would traverse the parent\n> +\tdirectories until hitting the root directory).\n\nHmm.\n\nLooking at the current implementation, this should be written as:\n\n\tIf set to a colon delimited list of absolute directories,\n\tand the current directory is in or below one of them, then\n\tthese are the top-most directories in which Git will look for\n\ta .git/ directory (otherwise it would traverse the parent\n\tdirectories until hitting the root directory).\n\nBut from David's initial commit message:\n\nFor example, I use git in an environment where homedirs are automounted\nand \"ls /home/nonexistent\" takes about 9 seconds.  Setting\nGIT_CEILING_DIRS=\"/home\" allows \"git help -a\" (for bash completion) and\n\"git symbolic-ref\" (for my shell prompt) to run in a reasonable time.\n\nThis implementation it will still look for a non-existing /home/.git, and,\nhence, should take a long time to complete.\n\nDavid, does this really meet your needs?\n\n-- Hannes\n"},{"id":"77102","messageId":"482D31B8.8000707@viscovery.net","threadId":"13533","inReplyTo":"alpine.DEB.1.00.0805152131390.30431@racer","subject":"Re: [PATCH v3] Add support for GIT_CEILING_DIRS","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2008-05-16T07:03:20Z","receivedAt":"2008-05-16T07:03:20Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Johannes Schindelin schrieb:\n> On Thu, 15 May 2008, Junio C Hamano wrote:\n> \n>> David Reiss <dreiss@facebook.com> writes:\n>> How well would this colon separated list work with msys folks?\n> \n> Not well at all.  At least for the moment, I think setting this variable \n> would fail (since it would be rewritten into a Windows-PATH-style string).  \n> Though honestly, I have no idea what getcwd() does on MinGW.\n\nWe would have to use ';' instead of ':' in longest_prefix(). Then it will\nwork automatically. Except that it does not ignore case in path names and\ndrive letters. Hence, the user must set this environment variable to the\nprecise path name(s).\n\n> Steffen is working on that rewriting stuff, so maybe it will be a \n> non-issue, eventually.\n\nThis will not matter. When we change the code to use is_path_separator()\ninstead of hard-coded c == ':', then it will work automatically. We do\nthat already for GIT_ALTERNATE_OBJECT_DIRECTORIES.\n\n-- Hannes\n"},{"id":"77107","messageId":"alpine.DEB.1.00.0805161118160.30431@racer","threadId":"13533","inReplyTo":"482D2F9D.4080809@viscovery.net","subject":"Re: [SQUASHED PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-05-16T10:20:09Z","receivedAt":"2008-05-16T10:20:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 16 May 2008, Johannes Sixt wrote:\n\n> Johannes Schindelin schrieb:\n> > +'GIT_CEILING_DIRECTORIES'::\n> > +\tIf set (to a colon delimited list of absolute directories), Git\n> > +\twill refuse to look for the .git/ directory further when hitting\n> > +\tone of those directories (otherwise it would traverse the parent\n> > +\tdirectories until hitting the root directory).\n> \n> Hmm.\n> \n> Looking at the current implementation, this should be written as:\n> \n> \tIf set to a colon delimited list of absolute directories,\n> \tand the current directory is in or below one of them, then\n> \tthese are the top-most directories in which Git will look for\n> \ta .git/ directory (otherwise it would traverse the parent\n> \tdirectories until hitting the root directory).\n\nAccording to the test case\n\n        GIT_CEILING_DIRECTORIES=\"$CWD/X:$CWD/subdir\" &&\n        export GIT_CEILING_DIRECTORIES &&\n        (cd subdir && test_must_fail git rev-parse --git-dir)\n\nthis is not the case.  If you have something like /bla/subdir and you are \nin /bla/subdir, it will not look for .git/.\n\nCiao,\nDscho \"who leaves it as an exercise to the reader to see why the code \ndoes what it does\"\n"},{"id":"77108","messageId":"alpine.DEB.1.00.0805161120500.30431@racer","threadId":"13533","inReplyTo":"482D31B8.8000707@viscovery.net","subject":"Re: [PATCH v3] Add support for GIT_CEILING_DIRS","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-05-16T10:21:11Z","receivedAt":"2008-05-16T10:21:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 16 May 2008, Johannes Sixt wrote:\n\n> Johannes Schindelin schrieb:\n> > On Thu, 15 May 2008, Junio C Hamano wrote:\n> > \n> >> David Reiss <dreiss@facebook.com> writes:\n> >> How well would this colon separated list work with msys folks?\n> > \n> > Not well at all.  At least for the moment, I think setting this variable \n> > would fail (since it would be rewritten into a Windows-PATH-style string).  \n> > Though honestly, I have no idea what getcwd() does on MinGW.\n> \n> We would have to use ';' instead of ':' in longest_prefix(). Then it will\n> work automatically. Except that it does not ignore case in path names and\n> drive letters. Hence, the user must set this environment variable to the\n> precise path name(s).\n\nYes, good point.\n\n> > Steffen is working on that rewriting stuff, so maybe it will be a \n> > non-issue, eventually.\n> \n> This will not matter. When we change the code to use is_path_separator()\n> instead of hard-coded c == ':', then it will work automatically. We do\n> that already for GIT_ALTERNATE_OBJECT_DIRECTORIES.\n\nYou are right, of course.\n\nCiao,\nDscho\n"},{"id":"77113","messageId":"482D66DC.30602@viscovery.net","threadId":"13533","inReplyTo":"alpine.DEB.1.00.0805161118160.30431@racer","subject":"Re: [SQUASHED PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2008-05-16T10:50:04Z","receivedAt":"2008-05-16T10:50:04Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Johannes Schindelin schrieb:\n> Hi,\n> \n> On Fri, 16 May 2008, Johannes Sixt wrote:\n> \n>> Johannes Schindelin schrieb:\n>>> +'GIT_CEILING_DIRECTORIES'::\n>>> +\tIf set (to a colon delimited list of absolute directories), Git\n>>> +\twill refuse to look for the .git/ directory further when hitting\n>>> +\tone of those directories (otherwise it would traverse the parent\n>>> +\tdirectories until hitting the root directory).\n>> Hmm.\n>>\n>> Looking at the current implementation, this should be written as:\n>>\n>> \tIf set to a colon delimited list of absolute directories,\n>> \tand the current directory is in or below one of them, then\n>> \tthese are the top-most directories in which Git will look for\n>> \ta .git/ directory (otherwise it would traverse the parent\n>> \tdirectories until hitting the root directory).\n> \n> According to the test case\n> \n>         GIT_CEILING_DIRECTORIES=\"$CWD/X:$CWD/subdir\" &&\n>         export GIT_CEILING_DIRECTORIES &&\n>         (cd subdir && test_must_fail git rev-parse --git-dir)\n> \n> this is not the case.  If you have something like /bla/subdir and you are \n> in /bla/subdir, it will not look for .git/.\n\nNo, the test just shows that it does not *find* a subdir/.git/, but it\ndoes not show that it doesn't even *look* for it.\n\nLet's first clarify the semantics of GIT_CEILING_DIRECTORIES before we\nstart fixing something: Can a directory that is named in\nGIT_CEILING_DIRECTORIES be a git repository or not?\n\nDavid's original motivation was that it cannot; in your implementation it can.\n\n-- Hannes\n"},{"id":"77153","messageId":"482DC7B6.1080406@facebook.com","threadId":"13533","inReplyTo":"482D66DC.30602@viscovery.net","subject":"Re: [SQUASHED PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"David Reiss","fromEmail":"dreiss@facebook.com","sentAt":"2008-05-16T17:43:18Z","receivedAt":"2008-05-16T17:43:18Z","isPatch":true,"sender":{"key":"dreiss@facebook.com","avatar":null},"body":"Sorry I missed this before.  As you said, in Johannes's version the\nceiling directories are the last directories we look in, whereas in my\nimplementation, they are the first directories we do not look in.  I\nmade this choice because it makes more sense for me to set my ceiling to\n\"/home\", rather than \"/home/dreiss\", so it will work even if I am in\nanother user's homedir.\n\nThere is also a difference in how they handle the case where the cwd is\na ceiling directory, but I think it is worth sorting out the first issue\nfirst.\n\n--David\n\n\nJohannes Sixt wrote:\n> Johannes Schindelin schrieb:\n>> Hi,\n>>\n>> On Fri, 16 May 2008, Johannes Sixt wrote:\n>>\n>>> Johannes Schindelin schrieb:\n>>>> +'GIT_CEILING_DIRECTORIES'::\n>>>> +   If set (to a colon delimited list of absolute directories), Git\n>>>> +   will refuse to look for the .git/ directory further when hitting\n>>>> +   one of those directories (otherwise it would traverse the parent\n>>>> +   directories until hitting the root directory).\n>>> Hmm.\n>>>\n>>> Looking at the current implementation, this should be written as:\n>>>\n>>>      If set to a colon delimited list of absolute directories,\n>>>      and the current directory is in or below one of them, then\n>>>      these are the top-most directories in which Git will look for\n>>>      a .git/ directory (otherwise it would traverse the parent\n>>>      directories until hitting the root directory).\n>>\n>> According to the test case\n>>\n>>         GIT_CEILING_DIRECTORIES=\"$CWD/X:$CWD/subdir\" &&\n>>         export GIT_CEILING_DIRECTORIES &&\n>>         (cd subdir && test_must_fail git rev-parse --git-dir)\n>>\n>> this is not the case.  If you have something like /bla/subdir and you are\n>> in /bla/subdir, it will not look for .git/.\n> \n> No, the test just shows that it does not *find* a subdir/.git/, but it\n> does not show that it doesn't even *look* for it.\n> \n> Let's first clarify the semantics of GIT_CEILING_DIRECTORIES before we\n> start fixing something: Can a directory that is named in\n> GIT_CEILING_DIRECTORIES be a git repository or not?\n> \n> David's original motivation was that it cannot; in your implementation\n> it can.\n> \n> -- Hannes\n> \n"},{"id":"77167","messageId":"alpine.DEB.1.00.0805170117000.30431@racer","threadId":"13533","inReplyTo":"482DC7B6.1080406@facebook.com","subject":"Re: [SQUASHED PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-05-17T00:19:53Z","receivedAt":"2008-05-17T00:19:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 16 May 2008, David Reiss wrote:\n\n> Sorry I missed this before.  As you said, in Johannes's version the \n> ceiling directories are the last directories we look in, whereas in my \n> implementation, they are the first directories we do not look in.  I \n> made this choice because it makes more sense for me to set my ceiling to \n> \"/home\", rather than \"/home/dreiss\", so it will work even if I am in \n> another user's homedir.\n> \n> There is also a difference in how they handle the case where the cwd is \n> a ceiling directory, but I think it is worth sorting out the first issue \n> first.\n\nPlease do not top-post.\n\nThis is the interdiff to the last squashed patch:\n\n-- snip --\ndiff --git a/setup.c b/setup.c\nindex cece3e4..2f7a17a 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -441,6 +441,8 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t */\n \toffset = len = strlen(cwd);\n \tfor (;;) {\n+\t\tif (offset <= min_offset)\n+\t\t\tgoto non_git;\n \t\tgitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n \t\tif (gitfile_dir) {\n \t\t\tif (set_git_dir(gitfile_dir))\n@@ -460,6 +462,7 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\tchdir(\"..\");\n \t\tdo {\n \t\t\tif (offset <= min_offset) {\n+non_git:\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\");\ndiff --git a/t/t1504-ceiling-directories.sh b/t/t1504-ceiling-directories.sh\nindex 6c8757d..edc00be 100644\n--- a/t/t1504-ceiling-directories.sh\n+++ b/t/t1504-ceiling-directories.sh\n@@ -30,6 +30,15 @@ test_expect_success 'with non-matching ceiling directory' '\n \n '\n \n+test_expect_success 'with matching ceiling directory' '\n+\n+\tGIT_CEILING_DIRECTORIES=\"$CWD\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && test_must_fail git rev-parse --git-dir) &&\n+\ttest_must_fail git rev-parse --git-dir\n+\n+'\n+\n test_expect_success 'with matching ceiling directories' '\n \n \tGIT_CEILING_DIRECTORIES=\"$CWD/X:$CWD/subdir\" &&\n-- snap --\n\nI will post the squashed patch as a response to this mail.\n\nCiao,\nDscho\n"},{"id":"77168","messageId":"alpine.DEB.1.00.0805170120080.30431@racer","threadId":"13533","inReplyTo":"alpine.DEB.1.00.0805170117000.30431@racer","subject":"[2nd SQUASHED PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-05-17T00:20:33Z","receivedAt":"2008-05-17T00:20:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nIn certain setups, trying to access a non-existing .git/ can take quite\nsome time, for example when the directory is an automount directory.\n\nAllow the user to specify directories where Git would stop looking for\na .git/ directory: GIT_CEILING_DIRECTORIES, if set, is expected to be\na colon delimited list of such barrier directories.\n\nNote: if GIT_CEILING_DIRECTORIES=/a/b and your current working directory\nis /a, Git will _not_ stop looking.\n\nNote2: you must not specify the directories with trailing slashes.\n\nInitial implementation by David Reiss.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git.txt          |    6 ++++\n cache.h                        |    2 +\n path.c                         |   25 ++++++++++++++++\n setup.c                        |   14 +++++++-\n t/t1504-ceiling-directories.sh |   63 ++++++++++++++++++++++++++++++++++++++++\n t/test-lib.sh                  |    1 +\n 6 files changed, 109 insertions(+), 2 deletions(-)\n create mode 100644 t/t1504-ceiling-directories.sh\n\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex adcd3e0..e4413bf 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -415,6 +415,12 @@ 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_DIRECTORIES'::\n+\tIf set (to a colon delimited list of absolute directories), Git\n+\twill refuse to look for the .git/ directory further when hitting\n+\tone of those directories (otherwise it would traverse the parent\n+\tdirectories until hitting the root directory).\n+\n git Commits\n ~~~~~~~~~~~\n 'GIT_AUTHOR_NAME'::\ndiff --git a/cache.h b/cache.h\nindex a8638b1..c31b4c7 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -300,6 +300,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_DIRECTORIES_ENVIRONMENT \"GIT_CEILING_DIRECTORIES\"\n #define GITATTRIBUTES_FILE \".gitattributes\"\n #define INFOATTRIBUTES_FILE \"info/attributes\"\n #define ATTRIBUTE_MACRO_PREFIX \"[attr]\"\n@@ -522,6 +523,7 @@ static inline int is_absolute_path(const char *path)\n \treturn path[0] == '/';\n }\n const char *make_absolute_path(const char *path);\n+int longest_prefix(const char *path, const char *prefix_list);\n \n /* Read and unpack a sha1 file into memory, write memory to a sha1 file */\n extern int sha1_object_info(const unsigned char *, unsigned long *);\ndiff --git a/path.c b/path.c\nindex b7c24a2..a097ecc 100644\n--- a/path.c\n+++ b/path.c\n@@ -357,3 +357,28 @@ const char *make_absolute_path(const char *path)\n \n \treturn buf;\n }\n+\n+static int is_separator(char c)\n+{\n+\treturn !c || c == '/';\n+}\n+\n+int longest_prefix(const char *path, const char *prefix_list)\n+{\n+\tint max_length = 0, length = 0, i;\n+\n+\tfor (i = 0; prefix_list[i]; i++)\n+\t\tif (prefix_list[i] == ':') {\n+\t\t\tif (length > max_length && is_separator(path[length]))\n+\t\t\t\tmax_length = length;\n+\t\t\tlength = 0;\n+\t\t}\n+\t\telse if (length >= 0) {\n+\t\t\tif (prefix_list[i] == path[length])\n+\t\t\t\tlength++;\n+\t\t\telse\n+\t\t\t\tlength = -1;\n+\t\t}\n+\treturn max_length > length || !is_separator(path[length]) ?\n+\t\tmax_length : length;\n+}\ndiff --git a/setup.c b/setup.c\nindex 9e9a2b1..2f7a17a 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -365,10 +365,13 @@ const char *read_gitfile_gently(const char *path)\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 *ceiling_directories =\n+\t\tgetenv(CEILING_DIRECTORIES_ENVIRONMENT);\n \tstatic char cwd[PATH_MAX+1];\n \tconst char *gitdirenv;\n \tconst char *gitfile_dir;\n \tint len, offset;\n+\tint min_offset = 0;\n \n \t/*\n \t * Let's assume that we are in a git repository.\n@@ -422,6 +425,9 @@ 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+\tif (ceiling_directories)\n+\t\tmin_offset = longest_prefix(cwd, ceiling_directories);\n+\n \t/*\n \t * Test in the following order (relative to the cwd):\n \t * - .git (file containing \"gitdir: <path>\")\n@@ -435,6 +441,8 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t */\n \toffset = len = strlen(cwd);\n \tfor (;;) {\n+\t\tif (offset <= min_offset)\n+\t\t\tgoto non_git;\n \t\tgitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n \t\tif (gitfile_dir) {\n \t\t\tif (set_git_dir(gitfile_dir))\n@@ -453,7 +461,8 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\t}\n \t\tchdir(\"..\");\n \t\tdo {\n-\t\t\tif (!offset) {\n+\t\t\tif (offset <= min_offset) {\n+non_git:\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@@ -462,7 +471,8 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\t\t\t}\n \t\t\t\tdie(\"Not a git repository\");\n \t\t\t}\n-\t\t} while (cwd[--offset] != '/');\n+\t\t} while (offset > min_offset &&\n+\t\t\t\t--offset >=0 && cwd[offset] != '/');\n \t}\n \n \tinside_git_dir = 0;\ndiff --git a/t/t1504-ceiling-directories.sh b/t/t1504-ceiling-directories.sh\nnew file mode 100644\nindex 0000000..edc00be\n--- /dev/null\n+++ b/t/t1504-ceiling-directories.sh\n@@ -0,0 +1,63 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Johannes E. Schindelin\n+#\n+\n+test_description='test limiting with GIT_CEILING_DIRECTORIES'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\n+\tCWD=\"$(pwd -P)\" &&\n+\tmkdir subdir\n+\n+'\n+\n+test_expect_success 'without GIT_CEILING_DIRECTORIES' '\n+\n+\ttest .git = \"$(git rev-parse --git-dir)\" &&\n+\t(cd subdir && git rev-parse --git-dir) &&\n+\techo \"$CWD\" &&\n+\ttest \"$CWD/.git\" = \"$(cd subdir && git rev-parse --git-dir)\"\n+\n+'\n+\n+test_expect_success 'with non-matching ceiling directory' '\n+\n+\ttest \"$(GIT_CEILING_DIRECTORIES=\"$CWD/X\" \\\n+\t\tgit rev-parse --git-dir)\" = .git\n+\n+'\n+\n+test_expect_success 'with matching ceiling directory' '\n+\n+\tGIT_CEILING_DIRECTORIES=\"$CWD\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && test_must_fail git rev-parse --git-dir) &&\n+\ttest_must_fail git rev-parse --git-dir\n+\n+'\n+\n+test_expect_success 'with matching ceiling directories' '\n+\n+\tGIT_CEILING_DIRECTORIES=\"$CWD/X:$CWD/subdir\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && test_must_fail git rev-parse --git-dir) &&\n+\tgit rev-parse --git-dir &&\n+\tGIT_CEILING_DIRECTORIES=\"$CWD/subdir:$CWD/X\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && test_must_fail git rev-parse --git-dir) &&\n+\tgit rev-parse --git-dir\n+\n+'\n+\n+test_expect_success 'with non-directory prefix' '\n+\n+\tGIT_CEILING_DIRECTORIES=\"$CWD/sub\" &&\n+\texport GIT_CEILING_DIRECTORIES &&\n+\t(cd subdir && git rev-parse --git-dir)\n+\n+'\n+\n+test_done\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 5002fb0..c3a3167 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_DIRECTORIES\n unset SHA1_FILE_DIRECTORIES\n unset SHA1_FILE_DIRECTORY\n GIT_MERGE_VERBOSITY=5\n-- \n1.5.5.1.490.g1644c11\n"},{"id":"77261","messageId":"48313258.5010208@viscovery.net","threadId":"13533","inReplyTo":"alpine.DEB.1.00.0805170117000.30431@racer","subject":"Re: [SQUASHED PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2008-05-19T07:55:04Z","receivedAt":"2008-05-19T07:55:04Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Johannes Schindelin schrieb:\n> Hi,\n> \n> On Fri, 16 May 2008, David Reiss wrote:\n> \n>> Sorry I missed this before.  As you said, in Johannes's version the \n>> ceiling directories are the last directories we look in, whereas in my \n>> implementation, they are the first directories we do not look in.  I \n>> made this choice because it makes more sense for me to set my ceiling to \n>> \"/home\", rather than \"/home/dreiss\", so it will work even if I am in \n>> another user's homedir.\n>>\n>> There is also a difference in how they handle the case where the cwd is \n>> a ceiling directory, but I think it is worth sorting out the first issue \n>> first.\n> \n> Please do not top-post.\n> \n> This is the interdiff to the last squashed patch:\n> \n> -- snip --\n> diff --git a/setup.c b/setup.c\n> index cece3e4..2f7a17a 100644\n> --- a/setup.c\n> +++ b/setup.c\n> @@ -441,6 +441,8 @@ const char *setup_git_directory_gently(int *nongit_ok)\n>  \t */\n>  \toffset = len = strlen(cwd);\n>  \tfor (;;) {\n> +\t\tif (offset <= min_offset)\n> +\t\t\tgoto non_git;\n>  \t\tgitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n>  \t\tif (gitfile_dir) {\n>  \t\t\tif (set_git_dir(gitfile_dir))\n> @@ -460,6 +462,7 @@ const char *setup_git_directory_gently(int *nongit_ok)\n>  \t\tchdir(\"..\");\n>  \t\tdo {\n>  \t\t\tif (offset <= min_offset) {\n> +non_git:\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\nHmm... If the implementation needs a 'goto', then I have the strong\nsuspicion that there's already something wrong at the concept level.\n\nI actually like the previous version better because of its clearer semantics:\n\n- The current directory is always checked.\n- GIT_CEILING_DIRECTORIES are checked. Consequently, setting the variable\nto the empty string is equivalent to not setting it at all.\n\n(but it means that David can't have what he wants, i.e. he must set\nGIT_CEILING_DIRECTORIES=/home/dreiss.)\n\nThis implementation:\n\n- Never checks the root directory, even if it is the current directory.\n- Otherwise always checks the current directory, even if it is mentioned\nin GIT_CEILING_DIRECTORIES.\n\n[That said, I'm not in strong support of this feature in general - I'm\njust caring because *if* it goes in, it will have conflicts with the mingw\nbranch.]\n\n-- Hannes\n"},{"id":"77267","messageId":"alpine.DEB.1.00.0805191138210.30431@racer","threadId":"13533","inReplyTo":"48313258.5010208@viscovery.net","subject":"Re: [SQUASHED PATCH] Add support for GIT_CEILING_DIRECTORIES","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-05-19T10:49:55Z","receivedAt":"2008-05-19T10:49:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 19 May 2008, Johannes Sixt wrote:\n\n> Johannes Schindelin schrieb:\n>\n> > diff --git a/setup.c b/setup.c\n> > index cece3e4..2f7a17a 100644\n> > --- a/setup.c\n> > +++ b/setup.c\n> > @@ -441,6 +441,8 @@ const char *setup_git_directory_gently(int *nongit_ok)\n> >  \t */\n> >  \toffset = len = strlen(cwd);\n> >  \tfor (;;) {\n> > +\t\tif (offset <= min_offset)\n> > +\t\t\tgoto non_git;\n> >  \t\tgitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n> >  \t\tif (gitfile_dir) {\n> >  \t\t\tif (set_git_dir(gitfile_dir))\n> > @@ -460,6 +462,7 @@ const char *setup_git_directory_gently(int *nongit_ok)\n> >  \t\tchdir(\"..\");\n> >  \t\tdo {\n> >  \t\t\tif (offset <= min_offset) {\n> > +non_git:\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> \n> Hmm... If the implementation needs a 'goto', then I have the strong\n> suspicion that there's already something wrong at the concept level.\n\nI do not share the notion that \"goto\" = BAD.\n\n> [That said, I'm not in strong support of this feature in general - I'm \n> just caring because *if* it goes in, it will have conflicts with the \n> mingw branch.]\n\nI am not in support of this feature at all, since I do not need it.\n\nHowever, I saw that David needs it, but I did not agree with the way he \nimplemented his patch.  So I tried to show how it is possible to do it in \na way that looks simpler to me.\n\nNow the ball is back in David's field: he can just take my patch, mangle \nit until it does what he wants, and resubmit.  That said, if the result \noffends my eye again, I will complain again.\n\nCiao,\nDscho\n"}]}