Provide a mechanism to turn off symlink resolution in ceiling paths
Commit 1b77d83cab
'setup_git_directory_gently_1(): resolve symlinks
in ceiling paths' changed the setup code to resolve symlinks in the
entries in GIT_CEILING_DIRECTORIES. Because those entries are
compared textually to the symlink-resolved current directory, an
entry in GIT_CEILING_DIRECTORIES that contained a symlink would have
no effect. It was known that this could cause performance problems
if the symlink resolution *itself* touched slow filesystems, but it
was thought that such use cases would be unlikely. The intention of
the earlier change was to deal with a case when the user has this:
GIT_CEILING_DIRECTORIES=/home/gitster
but in reality, /home/gitster is a symbolic link to somewhere else,
e.g. /net/machine/home4/gitster. A textual comparison between the
specified value /home/gitster and the location getcwd(3) returns
would not help us, but readlink("/home/gitster") would still be
fast.
After this change was released, Anders Kaseorg <andersk@mit.edu>
reported:
> [...] my computer has been acting so slow when I’m not connected to
> the network. I put various network filesystem paths in
> $GIT_CEILING_DIRECTORIES, such as
> /afs/athena.mit.edu/user/a/n/andersk (to avoid hitting its parents
> /afs/athena.mit.edu, /afs/athena.mit.edu/user/a, and
> /afs/athena.mit.edu/user/a/n which all live in different AFS
> volumes). Now when I’m not connected to the network, every
> invocation of Git, including the __git_ps1 in my shell prompt, waits
> for AFS to timeout.
To allow users to work around this problem, give them a mechanism to
turn off symlink resolution in GIT_CEILING_DIRECTORIES entries. All
the entries that follow an empty entry will not be checked for symbolic
links and used literally in comparison. E.g. with these:
GIT_CEILING_DIRECTORIES=:/foo/bar:/xyzzy or
GIT_CEILING_DIRECTORIES=/foo/bar::/xyzzy
we will not readlink("/xyzzy") because it comes after an empty entry.
With the former (but not with the latter), "/foo/bar" comes after an
empty entry, and we will not readlink it, either.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
1b77d83cab
commit
7ec30aaa5b
@ -653,12 +653,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
|
||||
|
24
setup.c
24
setup.c
@ -624,22 +624,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);
|
||||
} else if (!is_absolute_path(ceil)) {
|
||||
return 0;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -679,9 +689,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);
|
||||
}
|
||||
|
@ -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/"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user