{"thread":{"id":"12172","subject":"[PATCH 2/5] Add platform-independent .git \"symlink\"","startedAt":"2008-02-18T10:44:18Z","lastAt":"2008-02-18T14:54:13Z","messageCount":18,"participants":["Lars Hjemli","Johannes Schindelin","Jakub Narebski"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"69115","messageId":"1203331463-13857-1-git-send-email-hjemli@gmail.com","threadId":"12172","inReplyTo":null,"subject":"[PATCH 0/5 v2] Introduce the .gitfile","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T10:44:18Z","receivedAt":"2008-02-18T10:44:18Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"The patch-series has been reordered and updated according to comments on\nthe earlier series.\n\nShortlog:\n Simplify setup of $GIT_DIR in git-sh-setup.sh\n Add platform-independent .git \"symlink\"\n Teach resolve_gitlink_ref() about the .git file\n git-submodule: prepare for the .git-file\n Teach GIT-VERSION-GEN about the .git file\n\nDiffstat:\n Documentation/repository-layout.txt |    5 ++-\n GIT-VERSION-GEN                     |    2 +-\n cache.h                             |    1 +\n environment.c                       |    2 +\n git-sh-setup.sh                     |    8 +---\n git-submodule.sh                    |    4 +-\n refs.c                              |   17 +++++++-\n setup.c                             |   47 ++++++++++++++++++++++\n t/t0002-gitfile.sh                  |   74 +++++++++++++++++++++++++++++++++++\n 9 files changed, 146 insertions(+), 14 deletions(-)\n"},{"id":"69119","messageId":"1203331463-13857-2-git-send-email-hjemli@gmail.com","threadId":"12172","inReplyTo":"1203331463-13857-1-git-send-email-hjemli@gmail.com","subject":"[PATCH 1/5] Simplify setup of $GIT_DIR in git-sh-setup.sh","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T10:44:19Z","receivedAt":"2008-02-18T10:44:19Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"Using 'git rev-parse --git-dir' makes the code shorter and more future-\nproof.\n\nSigned-off-by: Lars Hjemli <hjemli@gmail.com>\n---\n git-sh-setup.sh |    8 +-------\n 1 files changed, 1 insertions(+), 7 deletions(-)\n\ndiff --git a/git-sh-setup.sh b/git-sh-setup.sh\nindex f388275..a44b1c7 100755\n--- a/git-sh-setup.sh\n+++ b/git-sh-setup.sh\n@@ -127,20 +127,14 @@ get_author_ident_from_commit () {\n # if we require to be in a git repository.\n if test -z \"$NONGIT_OK\"\n then\n+\tGIT_DIR=$(git rev-parse --git-dir) || exit\n \tif [ -z \"$SUBDIRECTORY_OK\" ]\n \tthen\n-\t\t: ${GIT_DIR=.git}\n \t\ttest -z \"$(git rev-parse --show-cdup)\" || {\n \t\t\texit=$?\n \t\t\techo >&2 \"You need to run this command from the toplevel of the working tree.\"\n \t\t\texit $exit\n \t\t}\n-\telse\n-\t\tGIT_DIR=$(git rev-parse --git-dir) || {\n-\t\t    exit=$?\n-\t\t    echo >&2 \"Failed to find a valid git directory.\"\n-\t\t    exit $exit\n-\t\t}\n \tfi\n \ttest -n \"$GIT_DIR\" && GIT_DIR=$(cd \"$GIT_DIR\" && pwd) || {\n \t\techo >&2 \"Unable to determine absolute path of git directory\"\n-- \n1.5.4.1.188.g3ea1f5\n"},{"id":"69114","messageId":"1203331463-13857-3-git-send-email-hjemli@gmail.com","threadId":"12172","inReplyTo":"1203331463-13857-2-git-send-email-hjemli@gmail.com","subject":"[PATCH 2/5] Add platform-independent .git \"symlink\"","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T10:44:20Z","receivedAt":"2008-02-18T10:44:20Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"This patch allows .git to be a regular textfile containing the path of\nthe real git directory (prefixed with \"gitdir: \"), which can be useful on\nplatforms lacking support for real symlinks.\n\nSigned-off-by: Lars Hjemli <hjemli@gmail.com>\n---\n Documentation/repository-layout.txt |    5 ++-\n cache.h                             |    1 +\n environment.c                       |    2 +\n setup.c                             |   47 ++++++++++++++++++++++\n t/t0002-gitfile.sh                  |   74 +++++++++++++++++++++++++++++++++++\n 5 files changed, 128 insertions(+), 1 deletions(-)\n create mode 100755 t/t0002-gitfile.sh\n\ndiff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt\nindex 6939130..bbaed2e 100644\n--- a/Documentation/repository-layout.txt\n+++ b/Documentation/repository-layout.txt\n@@ -3,7 +3,10 @@ git repository layout\n \n You may find these things in your git repository (`.git`\n directory for a repository associated with your working tree, or\n-`'project'.git` directory for a public 'bare' repository).\n+`'project'.git` directory for a public 'bare' repository. It is\n+also possible to have a working tree where `.git` is a plain\n+ascii file containing `gitdir: <path>`, i.e. the path to the\n+real git repository).\n \n objects::\n \tObject store associated with this repository.  Usually\ndiff --git a/cache.h b/cache.h\nindex e1000bc..1ad822a 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -277,6 +277,7 @@ extern char *get_index_file(void);\n extern char *get_graft_file(void);\n extern int set_git_dir(const char *path);\n extern const char *get_git_work_tree(void);\n+extern const char *read_gitfile_gently(const char *path);\n \n #define ALTERNATE_DB_ENVIRONMENT \"GIT_ALTERNATE_OBJECT_DIRECTORIES\"\n \ndiff --git a/environment.c b/environment.c\nindex 3527f16..8058e7b 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -49,6 +49,8 @@ static void setup_git_env(void)\n {\n \tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n \tif (!git_dir)\n+\t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n+\tif (!git_dir)\n \t\tgit_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n \tgit_object_dir = getenv(DB_ENVIRONMENT);\n \tif (!git_object_dir) {\ndiff --git a/setup.c b/setup.c\nindex 4509598..20502be 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -239,6 +239,44 @@ static int check_repository_format_gently(int *nongit_ok)\n }\n \n /*\n+ * Try to read the location of the git directory from the .git file,\n+ * return path to git directory if found.\n+ */\n+const char *read_gitfile_gently(const char *path)\n+{\n+\tchar *buf;\n+\tstruct stat st;\n+\tint fd;\n+\tsize_t len;\n+\n+\tif (stat(path, &st))\n+\t\treturn NULL;\n+\tif (!S_ISREG(st.st_mode))\n+\t\treturn NULL;\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie(\"Error opening %s: %s\", path, strerror(errno));\n+\tbuf = xmalloc(st.st_size + 1);\n+\tlen = read_in_full(fd, buf, st.st_size);\n+\tclose(fd);\n+\tif (len != st.st_size)\n+\t\tdie(\"Error reading %s\", path);\n+\tbuf[len] = '\\0';\n+\tif (prefixcmp(buf, \"gitdir: \"))\n+\t\tdie(\"Invalid gitfile format: %s\", path);\n+\twhile (buf[len - 1] == '\\n' || buf[len - 1] == '\\r')\n+\t\tlen--;\n+\tif (len < 9)\n+\t\tdie(\"No path in gitfile: %s\", path);\n+\tbuf[len] = '\\0';\n+\tif (!is_git_directory(buf + 8))\n+\t\tdie(\"Not a git repository: %s\", buf + 8);\n+\tpath = make_absolute_path(buf + 8);\n+\tfree(buf);\n+\treturn path;\n+}\n+\n+/*\n  * We cannot decide in this function whether we are in the work tree or\n  * not, since the config can only be read _after_ this function was called.\n  */\n@@ -247,6 +285,7 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \tconst char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);\n \tstatic char cwd[PATH_MAX+1];\n \tconst char *gitdirenv;\n+\tconst char *gitfile_dir;\n \tint len, offset;\n \n \t/*\n@@ -293,8 +332,10 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \n \t/*\n \t * Test in the following order (relative to the cwd):\n+\t * - .git (file containing \"gitdir: <path>\")\n \t * - .git/\n \t * - ./ (bare)\n+\t * - ../.git\n \t * - ../.git/\n \t * - ../ (bare)\n \t * - ../../.git/\n@@ -302,6 +343,12 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t */\n \toffset = len = strlen(cwd);\n \tfor (;;) {\n+\t\tgitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n+\t\tif (gitfile_dir) {\n+\t\t\tif (set_git_dir(gitfile_dir))\n+\t\t\t\tdie(\"Repository setup failed\");\n+\t\t\tbreak;\n+\t\t}\n \t\tif (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))\n \t\t\tbreak;\n \t\tif (is_git_directory(\".\")) {\ndiff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh\nnew file mode 100755\nindex 0000000..d280663\n--- /dev/null\n+++ b/t/t0002-gitfile.sh\n@@ -0,0 +1,74 @@\n+#!/bin/sh\n+\n+test_description='.git file\n+\n+Verify that plumbing commands work when .git is a file\n+'\n+. ./test-lib.sh\n+\n+objpath() {\n+    echo \"$1\" | sed -e 's|\\(..\\)|\\1/|'\n+}\n+\n+objck() {\n+\tp=$(objpath \"$1\")\n+\tif test ! -f \"$REAL/objects/$p\"\n+\tthen\n+\t\techo \"Object not found: $REAL/objects/$p\"\n+\t\tfalse\n+\tfi\n+}\n+\n+test_expect_success 'setup' '\n+\tREAL=\"$(pwd)/.real\" &&\n+\tmv .git \"$REAL\" &&\n+\techo \"gitdir: $REAL\" >.git\n+'\n+\n+test_expect_success 'check rev-parse --git-dir' '\n+\ttest \"$REAL\" = \"$(git rev-parse --git-dir)\"\n+'\n+\n+test_expect_success 'check hash-object' '\n+\techo \"foo\" >bar &&\n+\tSHA=$(cat bar | git hash-object -w --stdin) &&\n+\tobjck $SHA\n+'\n+\n+test_expect_success 'check cat-file' '\n+\tgit cat-file blob $SHA >actual &&\n+\tdiff -u bar actual\n+'\n+\n+test_expect_success 'check update-index' '\n+\tif test -f \"$REAL/index\"\n+\tthen\n+\t\techo \"Hmm, $REAL/index exists?\"\n+\t\tfalse\n+\tfi &&\n+\trm -f \"$REAL/objects/$(objpath $SHA)\" &&\n+\tgit update-index --add bar &&\n+\tif ! test -f \"$REAL/index\"\n+\tthen\n+\t\techo \"$REAL/index not found\"\n+\t\tfalse\n+\tfi &&\n+\tobjck $SHA\n+'\n+\n+test_expect_success 'check write-tree' '\n+\tSHA=$(git write-tree) &&\n+\tobjck $SHA\n+'\n+\n+test_expect_success 'check commit-tree' '\n+\tSHA=$(echo \"commit bar\" | git commit-tree $SHA) &&\n+\tobjck $SHA\n+'\n+\n+test_expect_success 'check rev-list' '\n+\techo $SHA >\"$REAL/HEAD\" &&\n+\ttest \"$SHA\" = \"$(git rev-list HEAD)\"\n+'\n+\n+test_done\n-- \n1.5.4.1.188.g3ea1f5\n"},{"id":"69117","messageId":"1203331463-13857-4-git-send-email-hjemli@gmail.com","threadId":"12172","inReplyTo":"1203331463-13857-3-git-send-email-hjemli@gmail.com","subject":"[PATCH 3/5] Teach resolve_gitlink_ref() about the .git file","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T10:44:21Z","receivedAt":"2008-02-18T10:44:21Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"When .git in a submodule is a file, resolve_gitlink_ref() needs to pick up\nthe real GIT_DIR of the submodule from that file.\n\nSigned-off-by: Lars Hjemli <hjemli@gmail.com>\n---\n refs.c |   17 ++++++++++++++---\n 1 files changed, 14 insertions(+), 3 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 67d2a50..56de5cf 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -351,6 +351,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re\n {\n \tint len = strlen(path), retval;\n \tchar *gitdir;\n+\tconst char *tmp;\n \n \twhile (len && path[len-1] == '/')\n \t\tlen--;\n@@ -358,9 +359,19 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re\n \t\treturn -1;\n \tgitdir = xmalloc(len + MAXREFLEN + 8);\n \tmemcpy(gitdir, path, len);\n-\tmemcpy(gitdir + len, \"/.git/\", 7);\n-\n-\tretval = resolve_gitlink_ref_recursive(gitdir, len+6, refname, result, 0);\n+\tmemcpy(gitdir + len, \"/.git\", 6);\n+\tlen += 5;\n+\n+\ttmp = read_gitfile_gently(gitdir);\n+\tif (tmp) {\n+\t\tfree(gitdir);\n+\t\tlen = strlen(tmp);\n+\t\tgitdir = xmalloc(len + MAXREFLEN + 3);\n+\t\tmemcpy(gitdir, tmp, len);\n+\t}\n+\tgitdir[len] = '/';\n+\tgitdir[++len] = '\\0';\n+\tretval = resolve_gitlink_ref_recursive(gitdir, len, refname, result, 0);\n \tfree(gitdir);\n \treturn retval;\n }\n-- \n1.5.4.1.188.g3ea1f5\n"},{"id":"69118","messageId":"1203331463-13857-5-git-send-email-hjemli@gmail.com","threadId":"12172","inReplyTo":"1203331463-13857-4-git-send-email-hjemli@gmail.com","subject":"[PATCH 4/5] git-submodule: prepare for the .git-file","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T10:44:22Z","receivedAt":"2008-02-18T10:44:22Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"When git-submodule tries to detect 'active' submodules, it checks for the\nexistence of a directory named '.git'. This isn't good enough now that .git\ncan be a file pointing to the real $GIT_DIR so the tests are changed to\nreflect this.\n\nSigned-off-by: Lars Hjemli <hjemli@gmail.com>\n---\n git-submodule.sh |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/git-submodule.sh b/git-submodule.sh\nindex a6aaf40..e7c08b5 100755\n--- a/git-submodule.sh\n+++ b/git-submodule.sh\n@@ -288,7 +288,7 @@ cmd_update()\n \t\t\tcontinue\n \t\tfi\n \n-\t\tif ! test -d \"$path\"/.git\n+\t\tif ! test -d \"$path\"/.git -o -f \"$path\"/.git\n \t\tthen\n \t\t\tmodule_clone \"$path\" \"$url\" || exit\n \t\t\tsubsha1=\n@@ -362,7 +362,7 @@ cmd_status()\n \tdo\n \t\tname=$(module_name \"$path\") || exit\n \t\turl=$(git config submodule.\"$name\".url)\n-\t\tif test -z \"url\" || ! test -d \"$path\"/.git\n+\t\tif test -z \"url\" || ! test -d \"$path\"/.git -o -f \"$path\"/.git\n \t\tthen\n \t\t\tsay \"-$sha1 $path\"\n \t\t\tcontinue;\n-- \n1.5.4.1.188.g3ea1f5\n"},{"id":"69116","messageId":"1203331463-13857-6-git-send-email-hjemli@gmail.com","threadId":"12172","inReplyTo":"1203331463-13857-5-git-send-email-hjemli@gmail.com","subject":"[PATCH 5/5] Teach GIT-VERSION-GEN about the .git file","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T10:44:23Z","receivedAt":"2008-02-18T10:44:23Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"Signed-off-by: Lars Hjemli <hjemli@gmail.com>\n---\n GIT-VERSION-GEN |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN\nindex 38a3273..2432a4f 100755\n--- a/GIT-VERSION-GEN\n+++ b/GIT-VERSION-GEN\n@@ -11,7 +11,7 @@ LF='\n if test -f version\n then\n \tVN=$(cat version) || VN=\"$DEF_VER\"\n-elif test -d .git &&\n+elif test -d .git -o -f .git &&\n \tVN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&\n \tcase \"$VN\" in\n \t*$LF*) (exit 1) ;;\n-- \n1.5.4.1.188.g3ea1f5\n"},{"id":"69130","messageId":"alpine.LSU.1.00.0802181214400.30505@racer.site","threadId":"12172","inReplyTo":"1203331463-13857-1-git-send-email-hjemli@gmail.com","subject":"Re: [PATCH 0/5 v2] Introduce the .gitfile","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-18T12:17:20Z","receivedAt":"2008-02-18T12:17:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 18 Feb 2008, Lars Hjemli wrote:\n\n> The patch-series has been reordered and updated according to comments on \n> the earlier series.\n\nIt would be easier on a stupid reviewer like me, if you summarised what \nyou changed.  In the case of patch \"1/5 => 2/5\", I would even have \nappreciated an interdiff...\n\nOh, and I was kind of surprised to learn that it is the \".gitfile\", not \nthe \".git file\"... I ran to your patch straight away, and was relieved \nwhen I found out that you still call it \".git\".\n\nCiao,\nDscho\n"},{"id":"69131","messageId":"alpine.LSU.1.00.0802181217580.30505@racer.site","threadId":"12172","inReplyTo":"1203331463-13857-3-git-send-email-hjemli@gmail.com","subject":"Re: [PATCH 2/5] Add platform-independent .git \"symlink\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-18T12:34:36Z","receivedAt":"2008-02-18T12:34:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 18 Feb 2008, Lars Hjemli wrote:\n\n> diff --git a/environment.c b/environment.c\n> index 3527f16..8058e7b 100644\n> --- a/environment.c\n> +++ b/environment.c\n> @@ -49,6 +49,8 @@ static void setup_git_env(void)\n>  {\n>  \tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n>  \tif (!git_dir)\n> +\t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n> +\tif (!git_dir)\n>  \t\tgit_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n\nI still maintain that the code (maybe not the diff) is easier to read like \nthis:\n\n  \tif (!git_dir) {\n\t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n\t\tif (!git_dir)\n  \t\t\tgit_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n\t}\n\n> diff --git a/setup.c b/setup.c\n> index 4509598..20502be 100644\n> --- a/setup.c\n> +++ b/setup.c\n> @@ -239,6 +239,44 @@ static int check_repository_format_gently(int *nongit_ok)\n>  }\n>  \n>  /*\n> + * Try to read the location of the git directory from the .git file,\n> + * return path to git directory if found.\n> + */\n> +const char *read_gitfile_gently(const char *path)\n> +{\n> +\tchar *buf;\n> +\tstruct stat st;\n> +\tint fd;\n> +\tsize_t len;\n> +\n> +\tif (stat(path, &st))\n> +\t\treturn NULL;\n> +\tif (!S_ISREG(st.st_mode))\n> +\t\treturn NULL;\n> +\tfd = open(path, O_RDONLY);\n> +\tif (fd < 0)\n> +\t\tdie(\"Error opening %s: %s\", path, strerror(errno));\n\nHmm.  Like I said, in the \"gently\" case, we might want to just print a \nwarning and return NULL.\n\nHowever, since you have 5 die()s in this function that would clutter the \ncode tremendously.  I briefly considered (shut your eyes now if you do \nnot like ugly code):\n\n\tint (*show_error)(const char *format, ...) = nongit_ok ?\n\t\terror : (int (*)(const char *format, ...))die;\n\nbut now I think a better method would be\n\nstatic int show_error(int die_on_error, const char *format, ...)\n{\n        va_list params;\n\n        va_start(params, err);\n\tif (die_on_error)\n\t\tdie_routine(err, params);\n\telse\n\t        error_routine(err, params);\n        va_end(params);\n        return -1;\n}\n\nThis would even be a candidate for a global function die_or_error().\n\nThen you could use it like this:\n\n\tif (fd < 0 && die(\"Error opening %s: %s\", path, strerror(errno))\n\t\treturn NULL;\n\nHmm.  Seeing what I wrote, it does not really feel elegant.\n\nSo maybe we can just scratch all that, and I agree that an invalid .git \nfile means \"no repository\" (as opposed to \"no valid repository\").\n\nIn that case, you might want to test for that, too...\n\nSpeaking about tests:\n\n> +test_expect_success 'setup' '\n> +       REAL=\"$(pwd)/.real\" &&\n> +       mv .git \"$REAL\" &&\n> +       echo \"gitdir: $REAL\" >.git\n> +'\n\nLet's not do this.  It would clutter the t/ directory unnecessarily.  \nInstead, do something like this:\n\ntest_expect_success setup '\n\n\tREAL=\"$(pwd)/.real\" &&\n\tmkdir test &&\n\tcd test &&\n\techo \"gitdir: $REAL\" > .git\n\n'\n\nHmm?\n\nCiao,\nDscho \"who likes to write 'Hmm' three times in a mail\"\n"},{"id":"69135","messageId":"8c5c35580802180456s75c9f857of1f727aca4673374@mail.gmail.com","threadId":"12172","inReplyTo":"alpine.LSU.1.00.0802181214400.30505@racer.site","subject":"Re: [PATCH 0/5 v2] Introduce the .gitfile","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T12:56:37Z","receivedAt":"2008-02-18T12:56:37Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"On Feb 18, 2008 1:17 PM, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> On Mon, 18 Feb 2008, Lars Hjemli wrote:\n>\n> > The patch-series has been reordered and updated according to comments on\n> > the earlier series.\n>\n> It would be easier on a stupid reviewer like me, if you summarised what\n> you changed.\n\nOk, I'll keep that in mind next time, and here's a quick summary (from\nthe top of my head) of this series vs. the previous:\n* patch 1 and 2 are swapped, since the changes to git-sh-setup.sh can\nbe applied onto master regardless of the rest of the patch series\n(while the other patches still rely on 1/5).\n* git-sh-setup.sh is simplified even further, and relies on\ngit-rev-parse to produce the error message when no gitdir can be\nfound.\n* read_gitfile_gently() is moved from environment.c into setup.c since\nit now invokes is_git_directory().\n* read_gitfile_gently() will gently ignore a 'missing' .git or a .git\nwhich isn't a regular file, but die()s on all other errors.\n\n\n> In the case of patch \"1/5 => 2/5\", I would even have\n> appreciated an interdiff...\n\nSorry, but I don't think I understand what you mean by interdiff.\n\n\n>\n> Oh, and I was kind of surprised to learn that it is the \".gitfile\", not\n> the \".git file\"... I ran to your patch straight away, and was relieved\n> when I found out that you still call it \".git\".\n\nYeah, I suck really bad on consistency ;-)\n\n\n--\nlh\n"},{"id":"69137","messageId":"8c5c35580802180518x5c392bddya21c48ea4db9a7cd@mail.gmail.com","threadId":"12172","inReplyTo":"alpine.LSU.1.00.0802181217580.30505@racer.site","subject":"Re: [PATCH 2/5] Add platform-independent .git \"symlink\"","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T13:18:32Z","receivedAt":"2008-02-18T13:18:32Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"On Feb 18, 2008 1:34 PM, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> Hi,\n>\n> On Mon, 18 Feb 2008, Lars Hjemli wrote:\n>\n> > diff --git a/environment.c b/environment.c\n> > index 3527f16..8058e7b 100644\n> > --- a/environment.c\n> > +++ b/environment.c\n> > @@ -49,6 +49,8 @@ static void setup_git_env(void)\n> >  {\n> >       git_dir = getenv(GIT_DIR_ENVIRONMENT);\n> >       if (!git_dir)\n> > +             git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n> > +     if (!git_dir)\n> >               git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n>\n> I still maintain that the code (maybe not the diff) is easier to read like\n> this:\n>\n>         if (!git_dir) {\n>                 git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n>                 if (!git_dir)\n>                         git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n>         }\n\nIMHO such constructs are butt ugly, but if there's consensus for your\nway, I'll abide...\n\n\n> [* snip *]\n>\n> So maybe we can just scratch all that, and I agree that an invalid .git\n> file means \"no repository\" (as opposed to \"no valid repository\").\n>\n> In that case, you might want to test for that, too...\n\nI think I do, did you find a loophole in the testing?\n\n\n>\n> Speaking about tests:\n>\n> > +test_expect_success 'setup' '\n> > +       REAL=\"$(pwd)/.real\" &&\n> > +       mv .git \"$REAL\" &&\n> > +       echo \"gitdir: $REAL\" >.git\n> > +'\n>\n> Let's not do this.  It would clutter the t/ directory unnecessarily.\n\nWhat do you mean? The test just moves t/trash/.git to t/trash/.real...\n\n--\nlarsh\n"},{"id":"69142","messageId":"alpine.LSU.1.00.0802181330030.30505@racer.site","threadId":"12172","inReplyTo":"8c5c35580802180456s75c9f857of1f727aca4673374@mail.gmail.com","subject":"Re: [PATCH 0/5 v2] Introduce the .gitfile","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-18T13:31:45Z","receivedAt":"2008-02-18T13:31:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 18 Feb 2008, Lars Hjemli wrote:\n\n> On Feb 18, 2008 1:17 PM, Johannes Schindelin \n> <Johannes.Schindelin@gmx.de> wrote:\n> > On Mon, 18 Feb 2008, Lars Hjemli wrote:\n> >\n> > > The patch-series has been reordered and updated according to \n> > > comments on the earlier series.\n> >\n> > It would be easier on a stupid reviewer like me, if you summarised \n> > what you changed.\n> \n> Ok, I'll keep that in mind next time, and here's a quick summary (from\n> the top of my head) of this series vs. the previous:\n> * patch 1 and 2 are swapped, since the changes to git-sh-setup.sh can\n> be applied onto master regardless of the rest of the patch series\n> (while the other patches still rely on 1/5).\n> * git-sh-setup.sh is simplified even further, and relies on\n> git-rev-parse to produce the error message when no gitdir can be\n> found.\n> * read_gitfile_gently() is moved from environment.c into setup.c since\n> it now invokes is_git_directory().\n> * read_gitfile_gently() will gently ignore a 'missing' .git or a .git\n> which isn't a regular file, but die()s on all other errors.\n\nThanks.\n\n> > In the case of patch \"1/5 => 2/5\", I would even have appreciated an \n> > interdiff...\n> \n> Sorry, but I don't think I understand what you mean by interdiff.\n\nThe tool interdiff of patchutils is really nice: you can visualise what \nwould be the diff between the state after applying the first patch, and \nthe state after applying the second patch, without applying anything at \nall:\n\n\t$ interdiff <patch1> <patch2>\n\nCiao,\nDscho\n"},{"id":"69143","messageId":"fpc1hd$ror$1@ger.gmane.org","threadId":"12172","inReplyTo":"8c5c35580802180518x5c392bddya21c48ea4db9a7cd@mail.gmail.com","subject":"Re: [PATCH 2/5] Add platform-independent .git \"symlink\"","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2008-02-18T13:34:36Z","receivedAt":"2008-02-18T13:34:36Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Lars Hjemli wrote:\n> On Feb 18, 2008 1:34 PM, Johannes Schindelin wrote:\n>> On Mon, 18 Feb 2008, Lars Hjemli wrote:\n>>\n>> > diff --git a/environment.c b/environment.c\n>> > index 3527f16..8058e7b 100644\n>> > --- a/environment.c\n>> > +++ b/environment.c\n>> > @@ -49,6 +49,8 @@ static void setup_git_env(void)\n>> >  {\n>> >       git_dir = getenv(GIT_DIR_ENVIRONMENT);\n>> >       if (!git_dir)\n>> > +             git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n>> > +     if (!git_dir)\n>> >               git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n>>\n>> I still maintain that the code (maybe not the diff) is easier to read like\n>> this:\n>>\n>>         if (!git_dir) {\n>>                 git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n>>                 if (!git_dir)\n>>                         git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n>>         }\n> \n> IMHO such constructs are butt ugly, but if there's consensus for your\n> way, I'll abide...\n\nI prefer the original variant. It is IMHO no less, and perhaps even\nmore readable, and it is not soo deeply nested.\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"69144","messageId":"alpine.LSU.1.00.0802181334010.30505@racer.site","threadId":"12172","inReplyTo":"8c5c35580802180518x5c392bddya21c48ea4db9a7cd@mail.gmail.com","subject":"Re: [PATCH 2/5] Add platform-independent .git \"symlink\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-18T13:35:18Z","receivedAt":"2008-02-18T13:35:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 18 Feb 2008, Lars Hjemli wrote:\n\n> On Feb 18, 2008 1:34 PM, Johannes Schindelin \n> <Johannes.Schindelin@gmx.de> wrote:\n> >\n> > [* snip *]\n> >\n> > So maybe we can just scratch all that, and I agree that an invalid \n> > .git file means \"no repository\" (as opposed to \"no valid repository\").\n> >\n> > In that case, you might want to test for that, too...\n> \n> I think I do, did you find a loophole in the testing?\n\nI meant writing a readable, but invalid .git file.  Then testing that Git \ndie()s with the correct message.\n\n> > Speaking about tests:\n> >\n> > > +test_expect_success 'setup' '\n> > > +       REAL=\"$(pwd)/.real\" &&\n> > > +       mv .git \"$REAL\" &&\n> > > +       echo \"gitdir: $REAL\" >.git\n> > > +'\n> >\n> > Let's not do this.  It would clutter the t/ directory unnecessarily.\n> \n> What do you mean? The test just moves t/trash/.git to t/trash/.real...\n\nOh, well.  I'll go and buy me some new glasses.\n\nThanks,\nDscho\n"},{"id":"69148","messageId":"8c5c35580802180601h5cf01c5h583458fee61caa7a@mail.gmail.com","threadId":"12172","inReplyTo":"alpine.LSU.1.00.0802181330030.30505@racer.site","subject":"Re: [PATCH 0/5 v2] Introduce the .gitfile","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T14:01:07Z","receivedAt":"2008-02-18T14:01:07Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"On Feb 18, 2008 2:31 PM, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> Hi,\n>\n> On Mon, 18 Feb 2008, Lars Hjemli wrote:\n>\n> > On Feb 18, 2008 1:17 PM, Johannes Schindelin wrote:\n> > > In the case of patch \"1/5 => 2/5\", I would even have appreciated an\n> > > interdiff...\n> >\n> > Sorry, but I don't think I understand what you mean by interdiff.\n>\n> The tool interdiff of patchutils is really nice: you can visualise what\n> would be the diff between the state after applying the first patch, and\n> the state after applying the second patch, without applying anything at\n> all:\n>\n>         $ interdiff <patch1> <patch2>\n\nOk, that sounds useful (I was kind of confused since 'man interdiff'\ngave me nothing: being on slackware I'm so spoiled with preinstalled\ndev-tools that I see no point in consulting google ;).\n\nSomething like this (possibly mangled by gmail)?\n\n$ interdiff prev-patch-2 curr-patch-1\ndiff -u b/git-sh-setup.sh b/git-sh-setup.sh\n--- b/git-sh-setup.sh\n+++ b/git-sh-setup.sh\n@@ -127,11 +127,7 @@\n # if we require to be in a git repository.\n if test -z \"$NONGIT_OK\"\n then\n-       GIT_DIR=$(git rev-parse --git-dir) || {\n-               exit=$?\n-               echo >&2 \"Failed to find a valid git directory.\"\n-               exit $exit\n-       }\n+       GIT_DIR=$(git rev-parse --git-dir) || exit\n        if [ -z \"$SUBDIRECTORY_OK\" ]\n        then\n                test -z \"$(git rev-parse --show-cdup)\" || {\n\n$ interdiff prev-patch-1 curr-patch-2\ndiff -u b/Documentation/repository-layout.txt\nb/Documentation/repository-layout.txt\n--- b/Documentation/repository-layout.txt\n+++ b/Documentation/repository-layout.txt\n@@ -5,7 +5,7 @@\n directory for a repository associated with your working tree, or\n `'project'.git` directory for a public 'bare' repository. It is\n also possible to have a working tree where `.git` is a plain\n-ascii file containing `gitdir: <path>\\n`, i.e. the path to the\n+ascii file containing `gitdir: <path>`, i.e. the path to the\n real git repository).\n\n objects::\ndiff -u b/environment.c b/environment.c\n--- b/environment.c\n+++ b/environment.c\n@@ -45,42 +45,6 @@\n static const char *git_dir;\n static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;\n\n-/*\n- * Try to read the location of the git directory from the .git file,\n- * return path to git directory if found.\n- * Format of the .git file is\n- *    gitdir: <path>\\n\n- */\n-const char *read_gitfile_gently(const char *path)\n-{\n-       static char buf[PATH_MAX + 9];  /* \"gitdir: \" + \"\\n\" */\n-       struct stat st;\n-       int fd;\n-       size_t len;\n-\n-       if (stat(path, &st))\n-               return NULL;\n-       if (!S_ISREG(st.st_mode) || st.st_size >= sizeof(buf))\n-               return NULL;\n-       fd = open(path, O_RDONLY);\n-       if (fd < 0)\n-               return NULL;\n-       len = read_in_full(fd, buf, sizeof(buf));\n-       close(fd);\n-       if (len != st.st_size)\n-               return NULL;\n-       if (!len || buf[len - 1] != '\\n')\n-               return NULL;\n-       buf[len - 1] = '\\0';\n-       if (prefixcmp(buf, \"gitdir: \"))\n-               return NULL;\n-/*\n-       if (!is_git_directory(buf + 8))\n-               return NULL;\n-*/\n-       return make_absolute_path(buf + 8);\n-}\n-\n static void setup_git_env(void)\n {\n        git_dir = getenv(GIT_DIR_ENVIRONMENT);\ndiff -u b/setup.c b/setup.c\n--- b/setup.c\n+++ b/setup.c\n@@ -239,6 +239,44 @@\n }\n\n /*\n+ * Try to read the location of the git directory from the .git file,\n+ * return path to git directory if found.\n+ */\n+const char *read_gitfile_gently(const char *path)\n+{\n+       char *buf;\n+       struct stat st;\n+       int fd;\n+       size_t len;\n+\n+       if (stat(path, &st))\n+               return NULL;\n+       if (!S_ISREG(st.st_mode))\n+               return NULL;\n+       fd = open(path, O_RDONLY);\n+       if (fd < 0)\n+               die(\"Error opening %s: %s\", path, strerror(errno));\n+       buf = xmalloc(st.st_size + 1);\n+       len = read_in_full(fd, buf, st.st_size);\n+       close(fd);\n+       if (len != st.st_size)\n+               die(\"Error reading %s\", path);\n+       buf[len] = '\\0';\n+       if (prefixcmp(buf, \"gitdir: \"))\n+               die(\"Invalid gitfile format: %s\", path);\n+       while (buf[len - 1] == '\\n' || buf[len - 1] == '\\r')\n+               len--;\n+       if (len < 9)\n+               die(\"No path in gitfile: %s\", path);\n+       buf[len] = '\\0';\n+       if (!is_git_directory(buf + 8))\n+               die(\"Not a git repository: %s\", buf + 8);\n+       path = make_absolute_path(buf + 8);\n+       free(buf);\n+       return path;\n+}\n+\n+/*\n  * We cannot decide in this function whether we are in the work tree or\n  * not, since the config can only be read _after_ this function was called.\n  */\n@@ -294,10 +332,10 @@\n\n        /*\n         * Test in the following order (relative to the cwd):\n-        * - .git (file containing \"gitdir: <path>\\n\")\n+        * - .git (file containing \"gitdir: <path>\")\n         * - .git/\n         * - ./ (bare)\n-        * - ../.git (file containing \"gitdir: <path>\\n\")\n+        * - ../.git\n         * - ../.git/\n         * - ../ (bare)\n         * - ../../.git/\n@@ -306,9 +344,9 @@\n        offset = len = strlen(cwd);\n        for (;;) {\n                gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n-               if (gitfile_dir && is_git_directory(gitfile_dir)) {\n+               if (gitfile_dir) {\n                        if (set_git_dir(gitfile_dir))\n-                               return NULL;\n+                               die(\"Repository setup failed\");\n                        break;\n                }\n                if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))\n\n\n--\nlarsh\n"},{"id":"69149","messageId":"8c5c35580802180604w13e28cfdqce7aaad6676346e6@mail.gmail.com","threadId":"12172","inReplyTo":"alpine.LSU.1.00.0802181334010.30505@racer.site","subject":"Re: [PATCH 2/5] Add platform-independent .git \"symlink\"","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T14:04:27Z","receivedAt":"2008-02-18T14:04:27Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"On Feb 18, 2008 2:35 PM, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> Hi,\n>\n> On Mon, 18 Feb 2008, Lars Hjemli wrote:\n>\n> > On Feb 18, 2008 1:34 PM, Johannes Schindelin wrote:\n> > >\n> > > [* snip *]\n> > >\n> > > So maybe we can just scratch all that, and I agree that an invalid\n> > > .git file means \"no repository\" (as opposed to \"no valid repository\").\n> > >\n> > > In that case, you might want to test for that, too...\n> >\n> > I think I do, did you find a loophole in the testing?\n>\n> I meant writing a readable, but invalid .git file.  Then testing that Git\n> die()s with the correct message.\n\nAh, sorry for being slow, that's an excellent suggestion. I'll update\nthe test-script and resend 2/5.\n\n--\nlarsh\n"},{"id":"69152","messageId":"alpine.LSU.1.00.0802181411300.30505@racer.site","threadId":"12172","inReplyTo":"8c5c35580802180601h5cf01c5h583458fee61caa7a@mail.gmail.com","subject":"Re: [PATCH 0/5 v2] Introduce the .gitfile","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-18T14:12:05Z","receivedAt":"2008-02-18T14:12:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 18 Feb 2008, Lars Hjemli wrote:\n\n> On Feb 18, 2008 2:31 PM, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n>\n> > On Mon, 18 Feb 2008, Lars Hjemli wrote:\n> >\n> > > On Feb 18, 2008 1:17 PM, Johannes Schindelin wrote:\n> > > > In the case of patch \"1/5 => 2/5\", I would even have appreciated \n> > > > an interdiff...\n> > >\n> > > Sorry, but I don't think I understand what you mean by interdiff.\n> >\n> > The tool interdiff of patchutils is really nice: you can visualise \n> > what would be the diff between the state after applying the first \n> > patch, and the state after applying the second patch, without applying \n> > anything at all:\n> >\n> >         $ interdiff <patch1> <patch2>\n> \n> Ok, that sounds useful (I was kind of confused since 'man interdiff' \n> gave me nothing: being on slackware I'm so spoiled with preinstalled \n> dev-tools that I see no point in consulting google ;).\n> \n> Something like this (possibly mangled by gmail)?\n\nHeh, thank you!  The mangling does not matter, since this is purely for \nreviewing pleasure.\n\nThanks,\nDscho\n"},{"id":"69160","messageId":"1203345866-4881-1-git-send-email-hjemli@gmail.com","threadId":"12172","inReplyTo":"alpine.LSU.1.00.0802181334010.30505@racer.site","subject":"[PATCH 2/5] Add platform-independent .git \"symlink\"","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T14:44:26Z","receivedAt":"2008-02-18T14:44:26Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"This patch allows .git to be a regular textfile containing the path of\nthe real git directory (prefixed with \"gitdir: \"), which can be useful on\nplatforms lacking support for real symlinks.\n\nSigned-off-by: Lars Hjemli <hjemli@gmail.com>\n---\n\nOn Feb 18, 2008 2:35 PM, Johannes Schindelin wrote:\n> I meant writing a readable, but invalid .git file.  Then testing that Git\n> die()s with the correct message.\n\nSomething like this?\n\n\n Documentation/repository-layout.txt |    5 ++-\n cache.h                             |    1 +\n environment.c                       |    2 +\n setup.c                             |   47 ++++++++++++++++\n t/t0002-gitfile.sh                  |  103 +++++++++++++++++++++++++++++++++++\n 5 files changed, 157 insertions(+), 1 deletions(-)\n create mode 100755 t/t0002-gitfile.sh\n\ndiff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt\nindex 6939130..bbaed2e 100644\n--- a/Documentation/repository-layout.txt\n+++ b/Documentation/repository-layout.txt\n@@ -3,7 +3,10 @@ git repository layout\n \n You may find these things in your git repository (`.git`\n directory for a repository associated with your working tree, or\n-`'project'.git` directory for a public 'bare' repository).\n+`'project'.git` directory for a public 'bare' repository. It is\n+also possible to have a working tree where `.git` is a plain\n+ascii file containing `gitdir: <path>`, i.e. the path to the\n+real git repository).\n \n objects::\n \tObject store associated with this repository.  Usually\ndiff --git a/cache.h b/cache.h\nindex e1000bc..1ad822a 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -277,6 +277,7 @@ extern char *get_index_file(void);\n extern char *get_graft_file(void);\n extern int set_git_dir(const char *path);\n extern const char *get_git_work_tree(void);\n+extern const char *read_gitfile_gently(const char *path);\n \n #define ALTERNATE_DB_ENVIRONMENT \"GIT_ALTERNATE_OBJECT_DIRECTORIES\"\n \ndiff --git a/environment.c b/environment.c\nindex 3527f16..8058e7b 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -49,6 +49,8 @@ static void setup_git_env(void)\n {\n \tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n \tif (!git_dir)\n+\t\tgit_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n+\tif (!git_dir)\n \t\tgit_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n \tgit_object_dir = getenv(DB_ENVIRONMENT);\n \tif (!git_object_dir) {\ndiff --git a/setup.c b/setup.c\nindex 4509598..20502be 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -239,6 +239,44 @@ static int check_repository_format_gently(int *nongit_ok)\n }\n \n /*\n+ * Try to read the location of the git directory from the .git file,\n+ * return path to git directory if found.\n+ */\n+const char *read_gitfile_gently(const char *path)\n+{\n+\tchar *buf;\n+\tstruct stat st;\n+\tint fd;\n+\tsize_t len;\n+\n+\tif (stat(path, &st))\n+\t\treturn NULL;\n+\tif (!S_ISREG(st.st_mode))\n+\t\treturn NULL;\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie(\"Error opening %s: %s\", path, strerror(errno));\n+\tbuf = xmalloc(st.st_size + 1);\n+\tlen = read_in_full(fd, buf, st.st_size);\n+\tclose(fd);\n+\tif (len != st.st_size)\n+\t\tdie(\"Error reading %s\", path);\n+\tbuf[len] = '\\0';\n+\tif (prefixcmp(buf, \"gitdir: \"))\n+\t\tdie(\"Invalid gitfile format: %s\", path);\n+\twhile (buf[len - 1] == '\\n' || buf[len - 1] == '\\r')\n+\t\tlen--;\n+\tif (len < 9)\n+\t\tdie(\"No path in gitfile: %s\", path);\n+\tbuf[len] = '\\0';\n+\tif (!is_git_directory(buf + 8))\n+\t\tdie(\"Not a git repository: %s\", buf + 8);\n+\tpath = make_absolute_path(buf + 8);\n+\tfree(buf);\n+\treturn path;\n+}\n+\n+/*\n  * We cannot decide in this function whether we are in the work tree or\n  * not, since the config can only be read _after_ this function was called.\n  */\n@@ -247,6 +285,7 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \tconst char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);\n \tstatic char cwd[PATH_MAX+1];\n \tconst char *gitdirenv;\n+\tconst char *gitfile_dir;\n \tint len, offset;\n \n \t/*\n@@ -293,8 +332,10 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \n \t/*\n \t * Test in the following order (relative to the cwd):\n+\t * - .git (file containing \"gitdir: <path>\")\n \t * - .git/\n \t * - ./ (bare)\n+\t * - ../.git\n \t * - ../.git/\n \t * - ../ (bare)\n \t * - ../../.git/\n@@ -302,6 +343,12 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t */\n \toffset = len = strlen(cwd);\n \tfor (;;) {\n+\t\tgitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);\n+\t\tif (gitfile_dir) {\n+\t\t\tif (set_git_dir(gitfile_dir))\n+\t\t\t\tdie(\"Repository setup failed\");\n+\t\t\tbreak;\n+\t\t}\n \t\tif (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))\n \t\t\tbreak;\n \t\tif (is_git_directory(\".\")) {\ndiff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh\nnew file mode 100755\nindex 0000000..c5dbc72\n--- /dev/null\n+++ b/t/t0002-gitfile.sh\n@@ -0,0 +1,103 @@\n+#!/bin/sh\n+\n+test_description='.git file\n+\n+Verify that plumbing commands work when .git is a file\n+'\n+. ./test-lib.sh\n+\n+objpath() {\n+    echo \"$1\" | sed -e 's|\\(..\\)|\\1/|'\n+}\n+\n+objck() {\n+\tp=$(objpath \"$1\")\n+\tif test ! -f \"$REAL/objects/$p\"\n+\tthen\n+\t\techo \"Object not found: $REAL/objects/$p\"\n+\t\tfalse\n+\tfi\n+}\n+\n+\n+test_expect_success 'initial setup' '\n+\tREAL=\"$(pwd)/.real\" &&\n+\tmv .git \"$REAL\"\n+'\n+\n+test_expect_success 'bad setup: invalid .git file format' '\n+\techo \"gitdir $REAL\" >.git &&\n+\tif git rev-parse 2>.err\n+\tthen\n+\t\techo \"git rev-parse accepted an invalid .git file\"\n+\t\tfalse\n+\tfi &&\n+\tif ! grep -qe \"Invalid gitfile format\" .err\n+\tthen\n+\t\techo \"git rev-parse returned wrong error\"\n+\t\tfalse\n+\tfi\n+'\n+\n+test_expect_success 'bad setup: invalid .git file path' '\n+\techo \"gitdir: $REAL.not\" >.git &&\n+\tif git rev-parse 2>.err\n+\tthen\n+\t\techo \"git rev-parse accepted an invalid .git file path\"\n+\t\tfalse\n+\tfi &&\n+\tif ! grep -qe \"Not a git repository\" .err\n+\tthen\n+\t\techo \"git rev-parse returned wrong error\"\n+\t\tfalse\n+\tfi\n+'\n+\n+test_expect_success 'final setup + check rev-parse --git-dir' '\n+\techo \"gitdir: $REAL\" >.git &&\n+\ttest \"$REAL\" = \"$(git rev-parse --git-dir)\"\n+'\n+\n+test_expect_success 'check hash-object' '\n+\techo \"foo\" >bar &&\n+\tSHA=$(cat bar | git hash-object -w --stdin) &&\n+\tobjck $SHA\n+'\n+\n+test_expect_success 'check cat-file' '\n+\tgit cat-file blob $SHA >actual &&\n+\tdiff -u bar actual\n+'\n+\n+test_expect_success 'check update-index' '\n+\tif test -f \"$REAL/index\"\n+\tthen\n+\t\techo \"Hmm, $REAL/index exists?\"\n+\t\tfalse\n+\tfi &&\n+\trm -f \"$REAL/objects/$(objpath $SHA)\" &&\n+\tgit update-index --add bar &&\n+\tif ! test -f \"$REAL/index\"\n+\tthen\n+\t\techo \"$REAL/index not found\"\n+\t\tfalse\n+\tfi &&\n+\tobjck $SHA\n+'\n+\n+test_expect_success 'check write-tree' '\n+\tSHA=$(git write-tree) &&\n+\tobjck $SHA\n+'\n+\n+test_expect_success 'check commit-tree' '\n+\tSHA=$(echo \"commit bar\" | git commit-tree $SHA) &&\n+\tobjck $SHA\n+'\n+\n+test_expect_success 'check rev-list' '\n+\techo $SHA >\"$REAL/HEAD\" &&\n+\ttest \"$SHA\" = \"$(git rev-list HEAD)\"\n+'\n+\n+test_done\n-- \n1.5.4.1.188.g3ea1f5\n"},{"id":"69157","messageId":"alpine.LSU.1.00.0802181454000.30505@racer.site","threadId":"12172","inReplyTo":"1203345866-4881-1-git-send-email-hjemli@gmail.com","subject":"Re: [PATCH 2/5] Add platform-independent .git \"symlink\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-18T14:54:13Z","receivedAt":"2008-02-18T14:54:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 18 Feb 2008, Lars Hjemli wrote:\n\n> On Feb 18, 2008 2:35 PM, Johannes Schindelin wrote:\n> > I meant writing a readable, but invalid .git file.  Then testing that \n> > Git die()s with the correct message.\n> \n> Something like this?\n\nSure.\n\nCiao,\nDscho\n"}]}