summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: d553e73)
raw | patch | inline | side by side (parent: d553e73)
author | David Reiss <dreiss@facebook.com> | |
Tue, 20 May 2008 06:49:26 +0000 (23:49 -0700) | ||
committer | Junio C Hamano <gitster@pobox.com> | |
Fri, 23 May 2008 21:15:01 +0000 (14:15 -0700) |
Make git recognize a new environment variable that prevents it from
chdir'ing up into specified directories when looking for a GIT_DIR.
Useful for avoiding slow network directories.
For example, I use git in an environment where homedirs are automounted
and "ls /home/nonexistent" takes about 9 seconds. Setting
GIT_CEILING_DIRS="/home" allows "git help -a" (for bash completion) and
"git symbolic-ref" (for my shell prompt) to run in a reasonable time.
Signed-off-by: David Reiss <dreiss@facebook.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
chdir'ing up into specified directories when looking for a GIT_DIR.
Useful for avoiding slow network directories.
For example, I use git in an environment where homedirs are automounted
and "ls /home/nonexistent" takes about 9 seconds. Setting
GIT_CEILING_DIRS="/home" allows "git help -a" (for bash completion) and
"git symbolic-ref" (for my shell prompt) to run in a reasonable time.
Signed-off-by: David Reiss <dreiss@facebook.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git.txt | patch | blob | history | |
cache.h | patch | blob | history | |
path.c | patch | blob | history | |
setup.c | patch | blob | history | |
t/t0060-path-utils.sh | patch | blob | history | |
t/t1504-ceiling-dirs.sh | [new file with mode: 0755] | patch | blob |
t/test-lib.sh | patch | blob | history | |
test-path-utils.c | patch | blob | history |
diff --git a/Documentation/git.txt b/Documentation/git.txt
index adcd3e00b201af6182bbc441aa0b288a5d2db8a5..4ffe802371dc4ee95635c98beb38029fc0545184 100644 (file)
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
This can also be controlled by the '--work-tree' command line
option and the core.worktree configuration variable.
+'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.)
+
git Commits
~~~~~~~~~~~
'GIT_AUTHOR_NAME'::
index 88c390d9f17d3fd053dde60fbe0c7bd9d299270a..833f4cd982e419902c0376b0e5917e272640336e 100644 (file)
--- a/cache.h
+++ b/cache.h
#define CONFIG_ENVIRONMENT "GIT_CONFIG"
#define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
+#define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
#define GITATTRIBUTES_FILE ".gitattributes"
#define INFOATTRIBUTES_FILE "info/attributes"
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
}
const char *make_absolute_path(const char *path);
int normalize_absolute_path(char *buf, const char *path);
+int longest_ancestor_length(const char *path, const char *prefix_list);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int sha1_object_info(const unsigned char *, unsigned long *);
index 7175a06ed8924acc32cf982cf24cd1b2f1779163..0c4330486bb83b38c14a356cebb488f4419b93fb 100644 (file)
--- a/path.c
+++ b/path.c
*dst = '\0';
return dst - buf;
}
+
+/*
+ * path = Canonical absolute path
+ * prefix_list = Colon-separated list of absolute paths
+ *
+ * Determines, for each path in parent_list, whether the "prefix" really
+ * is an ancestor directory of path. Returns the length of the longest
+ * ancestor directory, excluding any trailing slashes, or -1 if no prefix
+ * is an ancestor. (Note that this means 0 is returned if prefix_list is
+ * "/".) "/foo" is not considered an ancestor of "/foobar". Directories
+ * are not considered to be their own ancestors. path must be in a
+ * canonical form: empty components, or "." or ".." components are not
+ * allowed. prefix_list may be null, which is like "".
+ */
+int longest_ancestor_length(const char *path, const char *prefix_list)
+{
+ char buf[PATH_MAX+1];
+ const char *ceil, *colon;
+ int len, max_len = -1;
+
+ if (prefix_list == NULL || !strcmp(path, "/"))
+ return -1;
+
+ for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
+ for (colon = ceil; *colon && *colon != ':'; colon++);
+ len = colon - ceil;
+ if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
+ continue;
+ strlcpy(buf, ceil, len+1);
+ len = normalize_absolute_path(buf, buf);
+ /* Strip "trailing slashes" from "/". */
+ if (len == 1)
+ len = 0;
+
+ if (!strncmp(path, buf, len) &&
+ path[len] == '/' &&
+ len > max_len) {
+ max_len = len;
+ }
+ }
+
+ return max_len;
+}
index b8fd476395db782bf942a548b87242b3a9f990f4..c14b10636faed05a47b4fb755768c4650a1ead76 100644 (file)
--- a/setup.c
+++ b/setup.c
const char *setup_git_directory_gently(int *nongit_ok)
{
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
+ const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
static char cwd[PATH_MAX+1];
const char *gitdirenv;
const char *gitfile_dir;
- int len, offset;
+ int len, offset, ceil_offset;
/*
* Let's assume that we are in a git repository.
if (!getcwd(cwd, sizeof(cwd)-1))
die("Unable to read current working directory");
+ ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
+
/*
* Test in the following order (relative to the cwd):
* - .git (file containing "gitdir: <path>")
return NULL;
}
chdir("..");
- do {
- if (!offset) {
- if (nongit_ok) {
- if (chdir(cwd))
- die("Cannot come back to cwd");
- *nongit_ok = 1;
- return NULL;
- }
- die("Not a git repository");
+ while (--offset > ceil_offset && cwd[offset] != '/');
+ if (offset <= ceil_offset) {
+ if (nongit_ok) {
+ if (chdir(cwd))
+ die("Cannot come back to cwd");
+ *nongit_ok = 1;
+ return NULL;
}
- } while (cwd[--offset] != '/');
+ die("Not a git repository");
+ }
}
inside_git_dir = 0;
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 9076b3bd81b45e95376e67f265ba9308d7f03de9..6e7501f352ee97636280357da54c50d73ceb0138 100755 (executable)
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
"test \$(test-path-utils normalize_absolute_path '$1') = '$2'"
}
+ancestor() {
+ test_expect_success "longest ancestor" \
+ "test \$(test-path-utils longest_ancestor_length '$1' '$2') = '$3'"
+}
+
norm_abs "" /
norm_abs / /
norm_abs // /
norm_abs /d1/.../d2 /d1/.../d2
norm_abs /d1/..././../d2 /d1/d2
+ancestor / "" -1
+ancestor / / -1
+ancestor /foo "" -1
+ancestor /foo : -1
+ancestor /foo ::. -1
+ancestor /foo ::..:: -1
+ancestor /foo / 0
+ancestor /foo /fo -1
+ancestor /foo /foo -1
+ancestor /foo /foo/ -1
+ancestor /foo /bar -1
+ancestor /foo /bar/ -1
+ancestor /foo /foo/bar -1
+ancestor /foo /foo:/bar/ -1
+ancestor /foo /foo/:/bar/ -1
+ancestor /foo /foo::/bar/ -1
+ancestor /foo /:/foo:/bar/ 0
+ancestor /foo /foo:/:/bar/ 0
+ancestor /foo /:/bar/:/foo 0
+ancestor /foo/bar "" -1
+ancestor /foo/bar / 0
+ancestor /foo/bar /fo -1
+ancestor /foo/bar foo -1
+ancestor /foo/bar /foo 4
+ancestor /foo/bar /foo/ 4
+ancestor /foo/bar /foo/ba -1
+ancestor /foo/bar /:/fo 0
+ancestor /foo/bar /foo:/foo/ba 4
+ancestor /foo/bar /bar -1
+ancestor /foo/bar /bar/ -1
+ancestor /foo/bar /fo: -1
+ancestor /foo/bar :/fo -1
+ancestor /foo/bar /foo:/bar/ 4
+ancestor /foo/bar /:/foo:/bar/ 4
+ancestor /foo/bar /foo:/:/bar/ 4
+ancestor /foo/bar /:/bar/:/fo 0
+ancestor /foo/bar /:/bar/ 0
+ancestor /foo/bar :://foo/. 4
+ancestor /foo/bar :://foo/.:: 4
+ancestor /foo/bar //foo/./::/bar 4
+ancestor /foo/bar ::/bar -1
+
test_done
diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh
--- /dev/null
+++ b/t/t1504-ceiling-dirs.sh
@@ -0,0 +1,163 @@
+#!/bin/sh
+
+test_description='test GIT_CEILING_DIRECTORIES'
+. ./test-lib.sh
+
+test_prefix() {
+ test_expect_success "$1" \
+ "test '$2' = \"\$(git rev-parse --show-prefix)\""
+}
+
+test_fail() {
+ test_expect_code 128 "$1: prefix" \
+ "git rev-parse --show-prefix"
+}
+
+TRASH_ROOT="$(pwd)"
+ROOT_PARENT=$(dirname "$TRASH_ROOT")
+
+
+unset GIT_CEILING_DIRECTORIES
+test_prefix no_ceil ""
+
+export GIT_CEILING_DIRECTORIES
+
+GIT_CEILING_DIRECTORIES=""
+test_prefix ceil_empty ""
+
+GIT_CEILING_DIRECTORIES="$ROOT_PARENT"
+test_prefix ceil_at_parent ""
+
+GIT_CEILING_DIRECTORIES="$ROOT_PARENT/"
+test_prefix ceil_at_parent_slash ""
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT"
+test_prefix ceil_at_trash ""
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/"
+test_prefix ceil_at_trash_slash ""
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub"
+test_prefix ceil_at_sub ""
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/"
+test_prefix ceil_at_sub_slash ""
+
+
+mkdir -p sub/dir || exit 1
+cd sub/dir || exit 1
+
+unset GIT_CEILING_DIRECTORIES
+test_prefix subdir_no_ceil "sub/dir/"
+
+export GIT_CEILING_DIRECTORIES
+
+GIT_CEILING_DIRECTORIES=""
+test_prefix subdir_ceil_empty "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT"
+test_fail subdir_ceil_at_trash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/"
+test_fail subdir_ceil_at_trash_slash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub"
+test_fail subdir_ceil_at_sub
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/"
+test_fail subdir_ceil_at_sub_slash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/dir"
+test_prefix subdir_ceil_at_subdir "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/dir/"
+test_prefix subdir_ceil_at_subdir_slash "sub/dir/"
+
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/su"
+test_prefix subdir_ceil_at_su "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/su/"
+test_prefix subdir_ceil_at_su_slash "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/di"
+test_prefix subdir_ceil_at_sub_di "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/di"
+test_prefix subdir_ceil_at_sub_di_slash "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/subdi"
+test_prefix subdir_ceil_at_subdi "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/subdi"
+test_prefix subdir_ceil_at_subdi_slash "sub/dir/"
+
+
+GIT_CEILING_DIRECTORIES="foo:$TRASH_ROOT/sub"
+test_fail second_of_two
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub:bar"
+test_fail first_of_two
+
+GIT_CEILING_DIRECTORIES="foo:$TRASH_ROOT/sub:bar"
+test_fail second_of_three
+
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub"
+GIT_DIR=../../.git
+export GIT_DIR
+test_prefix git_dir_specified ""
+unset GIT_DIR
+
+
+cd ../.. || exit 1
+mkdir -p s/d || exit 1
+cd s/d || exit 1
+
+unset GIT_CEILING_DIRECTORIES
+test_prefix sd_no_ceil "s/d/"
+
+export GIT_CEILING_DIRECTORIES
+
+GIT_CEILING_DIRECTORIES=""
+test_prefix sd_ceil_empty "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT"
+test_fail sd_ceil_at_trash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/"
+test_fail sd_ceil_at_trash_slash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s"
+test_fail sd_ceil_at_s
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s/"
+test_fail sd_ceil_at_s_slash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s/d"
+test_prefix sd_ceil_at_sd "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s/d/"
+test_prefix sd_ceil_at_sd_slash "s/d/"
+
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/su"
+test_prefix sd_ceil_at_su "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/su/"
+test_prefix sd_ceil_at_su_slash "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s/di"
+test_prefix sd_ceil_at_s_di "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s/di"
+test_prefix sd_ceil_at_s_di_slash "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sdi"
+test_prefix sd_ceil_at_sdi "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sdi"
+test_prefix sd_ceil_at_sdi_slash "s/d/"
+
+
+test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 5002fb04b58ea91572b58dcaf7fe92342f4eeab1..c3a3167382778feb7ff8f5789d86e25869805499 100644 (file)
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
unset GIT_EXTERNAL_DIFF
unset GIT_INDEX_FILE
unset GIT_OBJECT_DIRECTORY
+unset GIT_CEILING_DIRECTORIES
unset SHA1_FILE_DIRECTORIES
unset SHA1_FILE_DIRECTORY
GIT_MERGE_VERBOSITY=5
diff --git a/test-path-utils.c b/test-path-utils.c
index 842b58018f7efebf7f92cee117d1a0b50297a12e..a0bcb0e210523124fa977c8bf46667cf25d0335f 100644 (file)
--- a/test-path-utils.c
+++ b/test-path-utils.c
}
}
+ if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
+ int len = longest_ancestor_length(argv[2], argv[3]);
+ printf("%d\n", len);
+ }
+
return 0;
}