From ed0cb46ebb020234da94a843ca341dde8e9e3911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 14 Feb 2010 22:44:41 +0700 Subject: make_absolute_path(): Do not append redundant slash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When concatenating two paths, if the first one already have '/', do not put another '/' in between the two paths. Usually this is not the case as getcwd() won't return '/foo/bar/', except when you are standing at root, then it will return '/'. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- abspath.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/abspath.c b/abspath.c index b88122cbe7..c91a29cb29 100644 --- a/abspath.c +++ b/abspath.c @@ -54,8 +54,9 @@ const char *make_absolute_path(const char *path) if (len + strlen(last_elem) + 2 > PATH_MAX) die ("Too long path name: '%s/%s'", buf, last_elem); - buf[len] = '/'; - strcpy(buf + len + 1, last_elem); + if (len && buf[len-1] != '/') + buf[len++] = '/'; + strcpy(buf + len, last_elem); free(last_elem); last_elem = NULL; } -- cgit v1.2.3 From d06f15d9c0c2b072e5eebf87fa93ee9a899ed642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 14 Feb 2010 22:44:42 +0700 Subject: init-db, rev-parse --git-dir: do not append redundant slash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If git_dir already has the trailing slash, don't put another one before .git. This only happens when git_dir is '/' or 'C:/' Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin-init-db.c | 9 ++++++--- builtin-rev-parse.c | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/builtin-init-db.c b/builtin-init-db.c index dd84caecbc..aae7a4d7ee 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -331,11 +331,14 @@ int init_db(const char *template_dir, unsigned int flags) git_config_set("receive.denyNonFastforwards", "true"); } - if (!(flags & INIT_DB_QUIET)) - printf("%s%s Git repository in %s/\n", + if (!(flags & INIT_DB_QUIET)) { + const char *git_dir = get_git_dir(); + int len = strlen(git_dir); + printf("%s%s Git repository in %s%s\n", reinit ? "Reinitialized existing" : "Initialized empty", shared_repository ? " shared" : "", - get_git_dir()); + git_dir, len && git_dir[len-1] != '/' ? "/" : ""); + } return 0; } diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 37d0233521..d0ccb4c968 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -608,6 +608,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) if (!strcmp(arg, "--git-dir")) { const char *gitdir = getenv(GIT_DIR_ENVIRONMENT); static char cwd[PATH_MAX]; + int len; if (gitdir) { puts(gitdir); continue; @@ -618,7 +619,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (!getcwd(cwd, PATH_MAX)) die_errno("unable to get current working directory"); - printf("%s/.git\n", cwd); + len = strlen(cwd); + printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : ""); continue; } if (!strcmp(arg, "--is-inside-git-dir")) { -- cgit v1.2.3 From 4bb43de25977063187dedf054122985bc5a2660e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Tue, 16 Feb 2010 12:22:08 +0700 Subject: Move offset_1st_component() to path.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation is also lightly modified to use is_dir_sep() instead of hardcoding '/'. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- cache.h | 1 + path.c | 7 +++++++ sha1_file.c | 7 ------- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cache.h b/cache.h index 04ae824d64..caf4192b88 100644 --- a/cache.h +++ b/cache.h @@ -664,6 +664,7 @@ int normalize_path_copy(char *dst, const char *src); int longest_ancestor_length(const char *path, const char *prefix_list); char *strip_path_suffix(const char *path, const char *suffix); int daemon_avoid_alias(const char *path); +int offset_1st_component(const char *path); /* Read and unpack a sha1 file into memory, write memory to a sha1 file */ extern int sha1_object_info(const unsigned char *, unsigned long *); diff --git a/path.c b/path.c index 79aa104712..06fd9e0577 100644 --- a/path.c +++ b/path.c @@ -649,3 +649,10 @@ int daemon_avoid_alias(const char *p) } } } + +int offset_1st_component(const char *path) +{ + if (has_dos_drive_prefix(path)) + return 2 + is_dir_sep(path[2]); + return is_dir_sep(path[0]); +} diff --git a/sha1_file.c b/sha1_file.c index 23d347c45f..923d9d1cd1 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -83,13 +83,6 @@ int get_sha1_hex(const char *hex, unsigned char *sha1) return 0; } -static inline int offset_1st_component(const char *path) -{ - if (has_dos_drive_prefix(path)) - return 2 + (path[2] == '/'); - return *path == '/'; -} - int safe_create_leading_directories(char *path) { char *pos = path + offset_1st_component(path); -- cgit v1.2.3 From 72ec8ba6dddbe2c53500d35d7ed343c153874fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 14 Feb 2010 22:44:44 +0700 Subject: Support working directory located at root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Git should work regardless where the working directory is located, even at root. This patch fixes two places where it assumes working directory always have parent directory. In setup_git_directory_gently(), when Git goes up to root and finds .git there, it happily sets worktree to "" instead of "/". In prefix_path(), loosen the outside repo check a little bit. Usually when a path XXX is inside worktree /foo, it must be either "/foo", or "/foo/...". When worktree is simply "/", we can safely ignore the check: we have a slash at the beginning already. Not related to worktree, but also set gitdir correctly if a bare repo is placed (insanely?) at root. Thanks João Carlos Mendes Luís for pointing out this problem. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- setup.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/setup.c b/setup.c index 2cf0f19937..3b9efdbaea 100644 --- a/setup.c +++ b/setup.c @@ -18,14 +18,15 @@ const char *prefix_path(const char *prefix, int len, const char *path) if (normalize_path_copy(sanitized, sanitized)) goto error_out; if (is_absolute_path(orig)) { - size_t len, total; + size_t root_len, len, total; const char *work_tree = get_git_work_tree(); if (!work_tree) goto error_out; len = strlen(work_tree); + root_len = offset_1st_component(work_tree); total = strlen(sanitized) + 1; if (strncmp(sanitized, work_tree, len) || - (sanitized[len] != '\0' && sanitized[len] != '/')) { + (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) { error_out: die("'%s' is outside repository", orig); } @@ -294,7 +295,7 @@ const char *setup_git_directory_gently(int *nongit_ok) static char cwd[PATH_MAX+1]; const char *gitdirenv; const char *gitfile_dir; - int len, offset, ceil_offset; + int len, offset, ceil_offset, root_len; /* * Let's assume that we are in a git repository. @@ -376,10 +377,11 @@ const char *setup_git_directory_gently(int *nongit_ok) if (!work_tree_env) inside_work_tree = 0; if (offset != len) { - cwd[offset] = '\0'; - setenv(GIT_DIR_ENVIRONMENT, cwd, 1); + root_len = offset_1st_component(cwd); + cwd[offset > root_len ? offset : root_len] = '\0'; + set_git_dir(cwd); } else - setenv(GIT_DIR_ENVIRONMENT, ".", 1); + set_git_dir("."); check_repository_format_gently(nongit_ok); return NULL; } @@ -400,7 +402,8 @@ const char *setup_git_directory_gently(int *nongit_ok) inside_git_dir = 0; if (!work_tree_env) inside_work_tree = 1; - git_work_tree_cfg = xstrndup(cwd, offset); + root_len = offset_1st_component(cwd); + git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len); if (check_repository_format_gently(nongit_ok)) return NULL; if (offset == len) -- cgit v1.2.3 From 3719b2fe554adc2f7a34a16b90f6894f299aab3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 14 Feb 2010 22:44:45 +0700 Subject: Add test for using Git at root of file system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This kind of test requires a throw-away root filesystem so that it can play on. If you have such a system, go ahead, "chmod 777 /" and run this test manually. Because this is a dangerous test, you are required to set an env variable, and not to use root to run it. Script prepare-root.sh may help you set up a chroot environment with Git test suite inside. You will need Linux, static linked busybox, rsync and root permission to use it. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- t/t1509-root-worktree.sh | 249 ++++++++++++++++++++++++++++++++++++++++++++++ t/t1509/excludes | 14 +++ t/t1509/prepare-chroot.sh | 38 +++++++ 3 files changed, 301 insertions(+) create mode 100755 t/t1509-root-worktree.sh create mode 100644 t/t1509/excludes create mode 100755 t/t1509/prepare-chroot.sh diff --git a/t/t1509-root-worktree.sh b/t/t1509-root-worktree.sh new file mode 100755 index 0000000000..5322a3bf97 --- /dev/null +++ b/t/t1509-root-worktree.sh @@ -0,0 +1,249 @@ +#!/bin/sh + +test_description='Test Git when git repository is located at root + +This test requires write access in root. Do not bother if you do not +have a throwaway chroot or VM. + +Script t1509/prepare-chroot.sh may help you setup chroot, then you +can chroot in and execute this test from there. +' + +. ./test-lib.sh + +test_cmp_val() { + echo "$1" > expected + echo "$2" > result + test_cmp expected result +} + +test_vars() { + test_expect_success "$1: gitdir" ' + test_cmp_val "'"$2"'" "$(git rev-parse --git-dir)" + ' + + test_expect_success "$1: worktree" ' + test_cmp_val "'"$3"'" "$(git rev-parse --show-toplevel)" + ' + + test_expect_success "$1: prefix" ' + test_cmp_val "'"$4"'" "$(git rev-parse --show-prefix)" + ' +} + +test_foobar_root() { + test_expect_success 'add relative' ' + test -z "$(cd / && git ls-files)" && + git add foo/foome && + git add foo/bar/barme && + git add me && + ( cd / && git ls-files --stage ) > result && + test_cmp /ls.expected result && + rm "$(git rev-parse --git-dir)/index" + ' + + test_expect_success 'add absolute' ' + test -z "$(cd / && git ls-files)" && + git add /foo/foome && + git add /foo/bar/barme && + git add /me && + ( cd / && git ls-files --stage ) > result && + test_cmp /ls.expected result && + rm "$(git rev-parse --git-dir)/index" + ' + +} + +test_foobar_foo() { + test_expect_success 'add relative' ' + test -z "$(cd / && git ls-files)" && + git add foome && + git add bar/barme && + git add ../me && + ( cd / && git ls-files --stage ) > result && + test_cmp /ls.expected result && + rm "$(git rev-parse --git-dir)/index" + ' + + test_expect_success 'add absolute' ' + test -z "$(cd / && git ls-files)" && + git add /foo/foome && + git add /foo/bar/barme && + git add /me && + ( cd / && git ls-files --stage ) > result && + test_cmp /ls.expected result && + rm "$(git rev-parse --git-dir)/index" + ' +} + +test_foobar_foobar() { + test_expect_success 'add relative' ' + test -z "$(cd / && git ls-files)" && + git add ../foome && + git add barme && + git add ../../me && + ( cd / && git ls-files --stage ) > result && + test_cmp /ls.expected result && + rm "$(git rev-parse --git-dir)/index" + ' + + test_expect_success 'add absolute' ' + test -z "$(cd / && git ls-files)" && + git add /foo/foome && + git add /foo/bar/barme && + git add /me && + ( cd / && git ls-files --stage ) > result && + test_cmp /ls.expected result && + rm "$(git rev-parse --git-dir)/index" + ' +} + +if ! test_have_prereq POSIXPERM || ! [ -w / ]; then + say "Dangerous test skipped. Read this test if you want to execute it" + test_done +fi + +if [ "$IKNOWWHATIAMDOING" != "YES" ]; then + say "You must set env var IKNOWWHATIAMDOING=YES in order to run this test" + test_done +fi + +if [ "$UID" = 0 ]; then + say "No you can't run this with root" + test_done +fi + +ONE_SHA1=d00491fd7e5bb6fa28c517a0bb32b8b506539d4d + +test_expect_success 'setup' ' + rm -rf /foo + mkdir /foo && + mkdir /foo/bar && + echo 1 > /foo/foome && + echo 1 > /foo/bar/barme && + echo 1 > /me +' + +say "GIT_DIR absolute, GIT_WORK_TREE set" + +test_expect_success 'go to /' 'cd /' + +cat >ls.expected < expected && + git init > result && + test_cmp expected result +' + +test_vars 'auto gitdir, root' ".git" "/" "" +test_foobar_root + +test_expect_success 'go to /foo' 'cd /foo' +test_vars 'auto gitdir, foo' "/.git" "/" "foo/" +test_foobar_foo + +test_expect_success 'go to /foo/bar' 'cd /foo/bar' +test_vars 'auto gitdir, foo/bar' "/.git" "/" "foo/bar/" +test_foobar_foobar + +test_expect_success 'cleanup' 'rm -rf /.git' + +say "auto bare gitdir" + +# DESTROYYYYY!!!!! +test_expect_success 'setup' ' + rm -rf /refs /objects /info /hooks + rm /* + cd / && + echo "Initialized empty Git repository in /" > expected && + git init --bare > result && + test_cmp expected result +' + +test_vars 'auto gitdir, root' "." "" "" + +test_expect_success 'go to /foo' 'cd /foo' + +test_vars 'auto gitdir, root' "/" "" "" + +test_done diff --git a/t/t1509/excludes b/t/t1509/excludes new file mode 100644 index 0000000000..d4d21d31a9 --- /dev/null +++ b/t/t1509/excludes @@ -0,0 +1,14 @@ +*.o +*~ +*.bak +*.c +*.h +.git +contrib +Documentation +git-gui +gitk-git +gitweb +t/t4013 +t/t5100 +t/t5515 diff --git a/t/t1509/prepare-chroot.sh b/t/t1509/prepare-chroot.sh new file mode 100755 index 0000000000..c5334a8fa4 --- /dev/null +++ b/t/t1509/prepare-chroot.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +die() { + echo >&2 "$@" + exit 1 +} + +xmkdir() { + while [ -n "$1" ]; do + [ -d "$1" ] || mkdir "$1" || die "Unable to mkdir $1" + shift + done +} + +R="$1" + +[ -n "$R" ] || die "Usage: prepare-chroot.sh " +[ -x git ] || die "This script needs to be executed at git source code's top directory" +[ -x /bin/busybox ] || die "You need busybox" + +xmkdir "$R" "$R/bin" "$R/etc" "$R/lib" "$R/dev" +[ -c "$R/dev/null" ] || die "/dev/null is missing. Do mknod $R/dev/null c 1 3 && chmod 666 $R/dev/null" +echo "root:x:0:0:root:/:/bin/sh" > "$R/etc/passwd" +echo "$(id -nu):x:$(id -u):$(id -g)::$(pwd)/t:/bin/sh" >> "$R/etc/passwd" +echo "root::0:root" > "$R/etc/group" +echo "$(id -ng)::$(id -g):$(id -nu)" >> "$R/etc/group" + +[ -x "$R/bin/busybox" ] || cp /bin/busybox "$R/bin/busybox" +[ -x "$R/bin/sh" ] || ln -s /bin/busybox "$R/bin/sh" +[ -x "$R/bin/su" ] || ln -s /bin/busybox "$R/bin/su" + +mkdir -p "$R$(pwd)" +rsync --exclude-from t/t1509/excludes -Ha . "$R$(pwd)" +ldd git | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do + mkdir -p "$R$(dirname $i)" + cp "$i" "$R/$i" +done +echo "Execute this in root: 'chroot $R /bin/su - $(id -nu)'" -- cgit v1.2.3