summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/git.txt19
-rw-r--r--setup.c32
-rwxr-xr-xt/t1504-ceiling-dirs.sh17
3 files changed, 52 insertions, 16 deletions
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 0847cdcc68..79aa8cd149 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -678,12 +678,19 @@ Git so take care if using Cogito etc.
The '--namespace' command-line option also sets this value.
'GIT_CEILING_DIRECTORIES'::
- This should be a colon-separated list of absolute paths.
- If set, it is a list of directories that Git should not chdir
- up into while looking for a repository directory.
- It will not exclude the current working directory or
- a GIT_DIR set on the command line or in the environment.
- (Useful for excluding slow-loading network directories.)
+ This should be a colon-separated list of absolute paths. If
+ set, it is a list of directories that Git should not chdir up
+ into while looking for a repository directory (useful for
+ excluding slow-loading network directories). It will not
+ exclude the current working directory or a GIT_DIR set on the
+ command line or in the environment. Normally, Git has to read
+ the entries in this list and resolve any symlink that
+ might be present in order to compare them with the current
+ directory. However, if even this access is slow, you
+ can add an empty entry to the list to tell Git that the
+ subsequent entries are not symlinks and needn't be resolved;
+ e.g.,
+ 'GIT_CEILING_DIRECTORIES=/maybe/symlink::/very/slow/non/symlink'.
'GIT_DISCOVERY_ACROSS_FILESYSTEM'::
When run in a directory that does not have ".git" repository
diff --git a/setup.c b/setup.c
index 2e1521b09e..1dee47e085 100644
--- a/setup.c
+++ b/setup.c
@@ -650,22 +650,32 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_
/*
* A "string_list_each_func_t" function that canonicalizes an entry
* from GIT_CEILING_DIRECTORIES using real_path_if_valid(), or
- * discards it if unusable.
+ * discards it if unusable. The presence of an empty entry in
+ * GIT_CEILING_DIRECTORIES turns off canonicalization for all
+ * subsequent entries.
*/
static int canonicalize_ceiling_entry(struct string_list_item *item,
- void *unused)
+ void *cb_data)
{
+ int *empty_entry_found = cb_data;
char *ceil = item->string;
- const char *real_path;
- if (!*ceil || !is_absolute_path(ceil))
+ if (!*ceil) {
+ *empty_entry_found = 1;
return 0;
- real_path = real_path_if_valid(ceil);
- if (!real_path)
+ } else if (!is_absolute_path(ceil)) {
return 0;
- free(item->string);
- item->string = xstrdup(real_path);
- return 1;
+ } else if (*empty_entry_found) {
+ /* Keep entry but do not canonicalize it */
+ return 1;
+ } else {
+ const char *real_path = real_path_if_valid(ceil);
+ if (!real_path)
+ return 0;
+ free(item->string);
+ item->string = xstrdup(real_path);
+ return 1;
+ }
}
/*
@@ -705,9 +715,11 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok);
if (env_ceiling_dirs) {
+ int empty_entry_found = 0;
+
string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
filter_string_list(&ceiling_dirs, 0,
- canonicalize_ceiling_entry, NULL);
+ canonicalize_ceiling_entry, &empty_entry_found);
ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs);
string_list_clear(&ceiling_dirs, 0);
}
diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh
index cce87a5ab5..3d51615e42 100755
--- a/t/t1504-ceiling-dirs.sh
+++ b/t/t1504-ceiling-dirs.sh
@@ -44,6 +44,10 @@ test_prefix ceil_at_sub ""
GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/"
test_prefix ceil_at_sub_slash ""
+if test_have_prereq SYMLINKS
+then
+ ln -s sub top
+fi
mkdir -p sub/dir || exit 1
cd sub/dir || exit 1
@@ -68,6 +72,19 @@ test_fail subdir_ceil_at_sub
GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/"
test_fail subdir_ceil_at_sub_slash
+if test_have_prereq SYMLINKS
+then
+ GIT_CEILING_DIRECTORIES="$TRASH_ROOT/top"
+ test_fail subdir_ceil_at_top
+ GIT_CEILING_DIRECTORIES="$TRASH_ROOT/top/"
+ test_fail subdir_ceil_at_top_slash
+
+ GIT_CEILING_DIRECTORIES=":$TRASH_ROOT/top"
+ test_prefix subdir_ceil_at_top_no_resolve "sub/dir/"
+ GIT_CEILING_DIRECTORIES=":$TRASH_ROOT/top/"
+ test_prefix subdir_ceil_at_top_slash_no_resolve "sub/dir/"
+fi
+
GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/dir"
test_prefix subdir_ceil_at_subdir "sub/dir/"