This patch allows .git to be a regular textfile containing the path of the real git directory (formatted like "gitdir: <path>\n"), which is useful on platforms lacking support for real symlinks. Signed-off-by: Lars Hjemli <hjemli@xxxxxxxxx> --- Documentation/repository-layout.txt | 5 ++- cache.h | 1 + environment.c | 38 ++++++++++++++++++ setup.c | 9 ++++ t/t0002-gitfile.sh | 74 +++++++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 1 deletions(-) create mode 100755 t/t0002-gitfile.sh diff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt index 6939130..e9db3a1 100644 --- a/Documentation/repository-layout.txt +++ b/Documentation/repository-layout.txt @@ -3,7 +3,10 @@ git repository layout You may find these things in your git repository (`.git` directory for a repository associated with your working tree, or -`'project'.git` directory for a public 'bare' repository). +`'project'.git` directory for a public 'bare' repository. It is +also possible to have a working tree where `.git` is a plain +ascii file containing `gitdir: <path>\n`, i.e. the path to the +real git repository). objects:: Object store associated with this repository. Usually diff --git a/cache.h b/cache.h index e1000bc..1ad822a 100644 --- a/cache.h +++ b/cache.h @@ -277,6 +277,7 @@ extern char *get_index_file(void); extern char *get_graft_file(void); extern int set_git_dir(const char *path); extern const char *get_git_work_tree(void); +extern const char *read_gitfile_gently(const char *path); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" diff --git a/environment.c b/environment.c index 3527f16..d120e8f 100644 --- a/environment.c +++ b/environment.c @@ -45,10 +45,48 @@ static const char *work_tree; static const char *git_dir; static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file; +/* + * Try to read the location of the git directory from the .git file, + * return path to git directory if found. + * Format of the .git file is + * gitdir: <path>\n + */ +const char *read_gitfile_gently(const char *path) +{ + static char buf[PATH_MAX + 9]; /* "gitdir: " + "\n" */ + struct stat st; + int fd; + size_t len; + + if (stat(path, &st)) + return NULL; + if (!S_ISREG(st.st_mode) || st.st_size >= sizeof(buf)) + return NULL; + fd = open(path, O_RDONLY); + if (fd < 0) + return NULL; + len = read_in_full(fd, buf, sizeof(buf)); + close(fd); + if (len != st.st_size) + return NULL; + if (!len || buf[len - 1] != '\n') + return NULL; + buf[len - 1] = '\0'; + if (prefixcmp(buf, "gitdir: ")) + return NULL; +/* + if (!is_git_directory(buf + 8)) + return NULL; +*/ + return make_absolute_path(buf + 8); +} + static void setup_git_env(void) { git_dir = getenv(GIT_DIR_ENVIRONMENT); if (!git_dir) + git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT); + if (!git_dir) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; git_object_dir = getenv(DB_ENVIRONMENT); if (!git_object_dir) { diff --git a/setup.c b/setup.c index 4509598..ebdf64b 100644 --- a/setup.c +++ b/setup.c @@ -247,6 +247,7 @@ const char *setup_git_directory_gently(int *nongit_ok) const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); static char cwd[PATH_MAX+1]; const char *gitdirenv; + const char *gitfile_dir; int len, offset; /* @@ -293,8 +294,10 @@ const char *setup_git_directory_gently(int *nongit_ok) /* * Test in the following order (relative to the cwd): + * - .git (file containing "gitdir: <path>\n") * - .git/ * - ./ (bare) + * - ../.git (file containing "gitdir: <path>\n") * - ../.git/ * - ../ (bare) * - ../../.git/ @@ -302,6 +305,12 @@ const char *setup_git_directory_gently(int *nongit_ok) */ offset = len = strlen(cwd); for (;;) { + gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT); + if (gitfile_dir && is_git_directory(gitfile_dir)) { + if (set_git_dir(gitfile_dir)) + return NULL; + break; + } if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) break; if (is_git_directory(".")) { diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh new file mode 100755 index 0000000..d280663 --- /dev/null +++ b/t/t0002-gitfile.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +test_description='.git file + +Verify that plumbing commands work when .git is a file +' +. ./test-lib.sh + +objpath() { + echo "$1" | sed -e 's|\(..\)|\1/|' +} + +objck() { + p=$(objpath "$1") + if test ! -f "$REAL/objects/$p" + then + echo "Object not found: $REAL/objects/$p" + false + fi +} + +test_expect_success 'setup' ' + REAL="$(pwd)/.real" && + mv .git "$REAL" && + echo "gitdir: $REAL" >.git +' + +test_expect_success 'check rev-parse --git-dir' ' + test "$REAL" = "$(git rev-parse --git-dir)" +' + +test_expect_success 'check hash-object' ' + echo "foo" >bar && + SHA=$(cat bar | git hash-object -w --stdin) && + objck $SHA +' + +test_expect_success 'check cat-file' ' + git cat-file blob $SHA >actual && + diff -u bar actual +' + +test_expect_success 'check update-index' ' + if test -f "$REAL/index" + then + echo "Hmm, $REAL/index exists?" + false + fi && + rm -f "$REAL/objects/$(objpath $SHA)" && + git update-index --add bar && + if ! test -f "$REAL/index" + then + echo "$REAL/index not found" + false + fi && + objck $SHA +' + +test_expect_success 'check write-tree' ' + SHA=$(git write-tree) && + objck $SHA +' + +test_expect_success 'check commit-tree' ' + SHA=$(echo "commit bar" | git commit-tree $SHA) && + objck $SHA +' + +test_expect_success 'check rev-list' ' + echo $SHA >"$REAL/HEAD" && + test "$SHA" = "$(git rev-list HEAD)" +' + +test_done -- 1.5.4.1.188.gdfa6c - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html