{"thread":{"id":"12163","subject":"[PATCH 3/5] Teach resolve_gitlink_ref() about the .git file","startedAt":"2008-02-17T22:14:11Z","lastAt":"2008-02-18T11:45:58Z","messageCount":17,"participants":["Lars Hjemli","Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"69051","messageId":"1203286456-26033-1-git-send-email-hjemli@gmail.com","threadId":"12163","inReplyTo":null,"subject":"Intoducing the .git file (again)","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-17T22:14:11Z","receivedAt":"2008-02-17T22:14:11Z","isPatch":false,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"These patches enables .git to be a textfile containing the path to the git\ndirectory proper. It passes all the tests so hopefully there are no\nregressions, but there may be bugs and omissions lurking when this feature\nis actually used; I've exercised it in my git, cgit and dayjob repos but git\nis big and has many codepaths so I wouldn't be suprised if there still are\nsome git commands left which fails to obey the .git file.\n\nPS: These patches could certainly be squashed into a single patch, but I've\nleft them as is to make each one easier to review.\n\nPPS: If included, the .git file should probably be used by git-submodule to\nclone submodule repositories into something like $GIT_DIR/submodules/<name>,\nas that would make local submodule changes more resistant to dataloss due to\ncheckout/reset in the containing repository.\n\nShortlog:\n Add platform-independent .git \"symlink\"\n Fix setup of $GIT_DIR in git-sh-setup.sh\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                       |   38 ++++++++++++++++++\n git-sh-setup.sh                     |   12 ++---\n git-submodule.sh                    |    4 +-\n refs.c                              |   17 +++++++-\n setup.c                             |    9 ++++\n t/t0002-gitfile.sh                  |   74 +++++++++++++++++++++++++++++++++++\n 9 files changed, 148 insertions(+), 14 deletions(-)\n"},{"id":"69049","messageId":"1203286456-26033-2-git-send-email-hjemli@gmail.com","threadId":"12163","inReplyTo":"1203286456-26033-1-git-send-email-hjemli@gmail.com","subject":"[PATCH 1/5] Add platform-independent .git \"symlink\"","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-17T22:14:12Z","receivedAt":"2008-02-17T22:14:12Z","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 (formatted like \"gitdir: <path>\\n\"), which is\nuseful on platforms 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                       |   38 ++++++++++++++++++\n setup.c                             |    9 ++++\n t/t0002-gitfile.sh                  |   74 +++++++++++++++++++++++++++++++++++\n 5 files changed, 126 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..e9db3a1 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>\\n`, 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..d120e8f 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -45,10 +45,48 @@ static const char *work_tree;\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+\tstatic char buf[PATH_MAX + 9];  /* \"gitdir: \" + \"\\n\" */\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) || st.st_size >= sizeof(buf))\n+\t\treturn NULL;\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\treturn NULL;\n+\tlen = read_in_full(fd, buf, sizeof(buf));\n+\tclose(fd);\n+\tif (len != st.st_size)\n+\t\treturn NULL;\n+\tif (!len || buf[len - 1] != '\\n')\n+\t\treturn NULL;\n+\tbuf[len - 1] = '\\0';\n+\tif (prefixcmp(buf, \"gitdir: \"))\n+\t\treturn NULL;\n+/*\n+\tif (!is_git_directory(buf + 8))\n+\t\treturn NULL;\n+*/\n+\treturn make_absolute_path(buf + 8);\n+}\n+\n 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..ebdf64b 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -247,6 +247,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 +294,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\")\n \t * - .git/\n \t * - ./ (bare)\n+\t * - ../.git (file containing \"gitdir: <path>\\n\")\n \t * - ../.git/\n \t * - ../ (bare)\n \t * - ../../.git/\n@@ -302,6 +305,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 && is_git_directory(gitfile_dir)) {\n+\t\t\tif (set_git_dir(gitfile_dir))\n+\t\t\t\treturn NULL;\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.gdfa6c\n"},{"id":"69052","messageId":"1203286456-26033-3-git-send-email-hjemli@gmail.com","threadId":"12163","inReplyTo":"1203286456-26033-2-git-send-email-hjemli@gmail.com","subject":"[PATCH 2/5] Fix setup of $GIT_DIR in git-sh-setup.sh","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-17T22:14:13Z","receivedAt":"2008-02-17T22:14:13Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"Since .git can be a file refering to the real GIT_DIR, git-sh-setup needs\nto use 'git rev-parse --git-dir' to obtain the location of the git\nrepository.\n\nSigned-off-by: Lars Hjemli <hjemli@gmail.com>\n---\n git-sh-setup.sh |   12 +++++-------\n 1 files changed, 5 insertions(+), 7 deletions(-)\n\ndiff --git a/git-sh-setup.sh b/git-sh-setup.sh\nindex f388275..a7dbce2 100755\n--- a/git-sh-setup.sh\n+++ b/git-sh-setup.sh\n@@ -127,20 +127,18 @@ 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) || {\n+\t\texit=$?\n+\t\techo >&2 \"Failed to find a valid git directory.\"\n+\t\texit $exit\n+\t}\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.gdfa6c\n"},{"id":"69047","messageId":"1203286456-26033-4-git-send-email-hjemli@gmail.com","threadId":"12163","inReplyTo":"1203286456-26033-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-17T22:14:14Z","receivedAt":"2008-02-17T22:14:14Z","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.gdfa6c\n"},{"id":"69050","messageId":"1203286456-26033-5-git-send-email-hjemli@gmail.com","threadId":"12163","inReplyTo":"1203286456-26033-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-17T22:14:15Z","receivedAt":"2008-02-17T22:14:15Z","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.gdfa6c\n"},{"id":"69048","messageId":"1203286456-26033-6-git-send-email-hjemli@gmail.com","threadId":"12163","inReplyTo":"1203286456-26033-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-17T22:14:16Z","receivedAt":"2008-02-17T22:14:16Z","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.gdfa6c\n"},{"id":"69054","messageId":"alpine.LSU.1.00.0802172219190.30505@racer.site","threadId":"12163","inReplyTo":"1203286456-26033-1-git-send-email-hjemli@gmail.com","subject":"Re: Intoducing the .git file (again)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-17T22:20:55Z","receivedAt":"2008-02-17T22:20:55Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 17 Feb 2008, Lars Hjemli wrote:\n\n> PPS: If included, the .git file should probably be used by git-submodule \n> to clone submodule repositories into something like \n> $GIT_DIR/submodules/<name>, as that would make local submodule changes \n> more resistant to dataloss due to checkout/reset in the containing \n> repository.\n\nI don't buy that argument.  For the moment, the submodules are \nself-contained repositories.  The superproject does not even have to have \na single object contained in a submodule.  I'd try to keep that \nseparation.\n\nAs for data loss, again, as a submodule is self-contained, the same rules \napply to it as for any repository.\n\nCiao,\nDscho\n"},{"id":"69055","messageId":"alpine.LSU.1.00.0802172224550.30505@racer.site","threadId":"12163","inReplyTo":"1203286456-26033-2-git-send-email-hjemli@gmail.com","subject":"Re: [PATCH 1/5] Add platform-independent .git \"symlink\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-17T22:25:27Z","receivedAt":"2008-02-17T22:25:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 17 Feb 2008, Lars Hjemli wrote:\n\n> @@ -302,6 +305,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 && is_git_directory(gitfile_dir)) {\n> +\t\t\tif (set_git_dir(gitfile_dir))\n> +\t\t\t\treturn NULL;\n\nShould this not complain loudly?  Really loudly, as in die()?\n\nCiao,\nDscho\n"},{"id":"69056","messageId":"8c5c35580802171429m1091492dgd65e13c94de08bd4@mail.gmail.com","threadId":"12163","inReplyTo":"alpine.LSU.1.00.0802172219190.30505@racer.site","subject":"Re: Intoducing the .git file (again)","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-17T22:29:31Z","receivedAt":"2008-02-17T22:29:31Z","isPatch":false,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"On Feb 17, 2008 11:20 PM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> Hi,\n>\n> On Sun, 17 Feb 2008, Lars Hjemli wrote:\n>\n> > PPS: If included, the .git file should probably be used by git-submodule\n> > to clone submodule repositories into something like\n> > $GIT_DIR/submodules/<name>, as that would make local submodule changes\n> > more resistant to dataloss due to checkout/reset in the containing\n> > repository.\n>\n> I don't buy that argument.  For the moment, the submodules are\n> self-contained repositories.  The superproject does not even have to have\n> a single object contained in a submodule.  I'd try to keep that\n> separation.\n\nThey would still be separated and self-contained repositories, but\nwith my suggestion the submodule repository would be moved out of the\nworking tree of the containing repository. Which I believe is a good\nthing, especially when you switch from one branch in the containing\nrepository which had the submodule in './foo' to another branch where\nthe submodule is located in './lib/foo'.\n\n--\nlarsh\n"},{"id":"69058","messageId":"8c5c35580802171437x5c952c32we38f9a62288d6c84@mail.gmail.com","threadId":"12163","inReplyTo":"alpine.LSU.1.00.0802172224550.30505@racer.site","subject":"Re: [PATCH 1/5] Add platform-independent .git \"symlink\"","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-17T22:37:20Z","receivedAt":"2008-02-17T22:37:20Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"On Feb 17, 2008 11:25 PM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> On Sun, 17 Feb 2008, Lars Hjemli wrote:\n>\n> > @@ -302,6 +305,12 @@ const char *setup_git_directory_gently(int *nongit_ok)\n> >        */\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 (set_git_dir(gitfile_dir))\n> > +                             return NULL;\n>\n> Should this not complain loudly?  Really loudly, as in die()?\n\nYes, I'll resend. Thanks.\n\n--\nlarsh\n"},{"id":"69059","messageId":"1203288610-5924-1-git-send-email-hjemli@gmail.com","threadId":"12163","inReplyTo":"8c5c35580802171437x5c952c32we38f9a62288d6c84@mail.gmail.com","subject":"[PATCH 1/5] Add platform-independent .git \"symlink\"","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-17T22:50:10Z","receivedAt":"2008-02-17T22:50:10Z","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 (formatted like \"gitdir: <path>\\n\"), which is\nuseful on platforms 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                       |   38 ++++++++++++++++++\n setup.c                             |    9 ++++\n t/t0002-gitfile.sh                  |   74 +++++++++++++++++++++++++++++++++++\n 5 files changed, 126 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..e9db3a1 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>\\n`, 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..d120e8f 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -45,10 +45,48 @@ static const char *work_tree;\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+\tstatic char buf[PATH_MAX + 9];  /* \"gitdir: \" + \"\\n\" */\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) || st.st_size >= sizeof(buf))\n+\t\treturn NULL;\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\treturn NULL;\n+\tlen = read_in_full(fd, buf, sizeof(buf));\n+\tclose(fd);\n+\tif (len != st.st_size)\n+\t\treturn NULL;\n+\tif (!len || buf[len - 1] != '\\n')\n+\t\treturn NULL;\n+\tbuf[len - 1] = '\\0';\n+\tif (prefixcmp(buf, \"gitdir: \"))\n+\t\treturn NULL;\n+/*\n+\tif (!is_git_directory(buf + 8))\n+\t\treturn NULL;\n+*/\n+\treturn make_absolute_path(buf + 8);\n+}\n+\n 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..f8ebaf5 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -247,6 +247,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 +294,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\")\n \t * - .git/\n \t * - ./ (bare)\n+\t * - ../.git (file containing \"gitdir: <path>\\n\")\n \t * - ../.git/\n \t * - ../ (bare)\n \t * - ../../.git/\n@@ -302,6 +305,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 && is_git_directory(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.gfde78\n"},{"id":"69061","messageId":"alpine.LSU.1.00.0802172324520.30505@racer.site","threadId":"12163","inReplyTo":"8c5c35580802171429m1091492dgd65e13c94de08bd4@mail.gmail.com","subject":"Re: Intoducing the .git file (again)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-17T23:27:04Z","receivedAt":"2008-02-17T23:27:04Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 17 Feb 2008, Lars Hjemli wrote:\n\n> On Feb 17, 2008 11:20 PM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n>\n> > On Sun, 17 Feb 2008, Lars Hjemli wrote:\n> >\n> > > PPS: If included, the .git file should probably be used by \n> > > git-submodule to clone submodule repositories into something like \n> > > $GIT_DIR/submodules/<name>, as that would make local submodule \n> > > changes more resistant to dataloss due to checkout/reset in the \n> > > containing repository.\n> >\n> > I don't buy that argument.  For the moment, the submodules are \n> > self-contained repositories.  The superproject does not even have to \n> > have a single object contained in a submodule.  I'd try to keep that \n> > separation.\n> \n> They would still be separated and self-contained repositories, but with \n> my suggestion the submodule repository would be moved out of the working \n> tree of the containing repository. Which I believe is a good thing, \n> especially when you switch from one branch in the containing repository \n> which had the submodule in './foo' to another branch where the submodule \n> is located in './lib/foo'.\n\nThat is a good argument.\n\nBut what about the argument when you stop being interested in the \nsuperproject, and move the submodule out of it (then deleting the \nsuperproject)?\n\nHowever, when a submodule is deleted, wasn't the plan all along to put the \nsubmodule's .git/ into the superproject's .git/submodules-deleted/ or some \nsuch?\n\nCiao,\nDscho\n"},{"id":"69088","messageId":"7v7ih2u8e1.fsf@gitster.siamese.dyndns.org","threadId":"12163","inReplyTo":"1203286456-26033-2-git-send-email-hjemli@gmail.com","subject":"Re: [PATCH 1/5] Add platform-independent .git \"symlink\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-18T05:43:34Z","receivedAt":"2008-02-18T05:43:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lars Hjemli <hjemli@gmail.com> writes:\n\n> This patch allows .git to be a regular textfile containing the path of\n> the real git directory (formatted like \"gitdir: <path>\\n\"), which is\n> useful on platforms lacking support for real symlinks.\n\nHmmmm.  I have suspected all along that these \"platforms lacking\nsupport for real symlinks\" are the ones whose native line\ntermination convention is CRLF.  How do you envision this file\nis initialized?  By users editing the file by hand?  Or some git\nspecific tool?  If the former, it might make sense to define the\nformat as \"a single line, terminated with platform line\nterminator\" and open and read it in text mode.  If the latter,\nwe do not have to care, and \"terminated with LF\" is good enough.\n\nI think a sane simplification is to allow the file to have\nany number of optional \\r or \\n at the end.  We may care about\nallowing arbitrary and possibly crazy filenames in the tracked\ncontents, but we can say \"Sorry, you cannot create a directory\n'ab\\nc\\r' and use it as the .git directory substitute.\"\n\n> +const char *read_gitfile_gently(const char *path)\n> +{\n> +\tstatic char buf[PATH_MAX + 9];  /* \"gitdir: \" + \"\\n\" */\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) || st.st_size >= sizeof(buf))\n> +\t\treturn NULL;\n> +\tfd = open(path, O_RDONLY);\n> +\tif (fd < 0)\n> +\t\treturn NULL;\n\nUp to this point it is fine \"gently\" semantics.\n\n> +\tlen = read_in_full(fd, buf, sizeof(buf));\n> +\tclose(fd);\n> +\tif (len != st.st_size)\n> +\t\treturn NULL;\n> +\tif (!len || buf[len - 1] != '\\n')\n> +\t\treturn NULL;\n> +\tbuf[len - 1] = '\\0';\n> +\tif (prefixcmp(buf, \"gitdir: \"))\n> +\t\treturn NULL;\n\nBut I am not sure about this part.  We found what claims to be\nthe \".git\" fake symlink but it is ill-formed.  Don't we want to\ndiagnose the possible breakage for the user?\n\n> +/*\n> +\tif (!is_git_directory(buf + 8))\n> +\t\treturn NULL;\n> +*/\n\nLikewise.\n"},{"id":"69089","messageId":"7v1w7au8d4.fsf@gitster.siamese.dyndns.org","threadId":"12163","inReplyTo":"1203286456-26033-3-git-send-email-hjemli@gmail.com","subject":"Re: [PATCH 2/5] Fix setup of $GIT_DIR in git-sh-setup.sh","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-02-18T05:44:07Z","receivedAt":"2008-02-18T05:44:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lars Hjemli <hjemli@gmail.com> writes:\n\n> Since .git can be a file refering to the real GIT_DIR, git-sh-setup needs\n> to use 'git rev-parse --git-dir' to obtain the location of the git\n> repository.\n\nI wonder if this depend on your [1/5].  Isn't this actually a\nsimplification (removing 7 adding 5 lines) that applies to the\nmainline already?  IOW, is there a downside of doing this\nwithout any of the rest of the series?\n\n> @@ -127,20 +127,18 @@ 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) || {\n> +\t\texit=$?\n> +\t\techo >&2 \"Failed to find a valid git directory.\"\n> +\t\texit $exit\n\nrev-parse --git-dir would have said \"fatal: Not a git\nrepository\" already.  Do we still need to say \"Failed to\nfind...\"?\n"},{"id":"69099","messageId":"8c5c35580802180035q3d914720x54aa7e32d6e82108@mail.gmail.com","threadId":"12163","inReplyTo":"7v7ih2u8e1.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 1/5] Add platform-independent .git \"symlink\"","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T08:35:27Z","receivedAt":"2008-02-18T08:35:27Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"On Feb 18, 2008 6:43 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Lars Hjemli <hjemli@gmail.com> writes:\n>\n> > This patch allows .git to be a regular textfile containing the path of\n> > the real git directory (formatted like \"gitdir: <path>\\n\"), which is\n> > useful on platforms lacking support for real symlinks.\n>\n> I think a sane simplification is to allow the file to have\n> any number of optional \\r or \\n at the end.\n\nAgreed.\n\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> But I am not sure about this part.  We found what claims to be\n> the \".git\" fake symlink but it is ill-formed.  Don't we want to\n> diagnose the possible breakage for the user?\n\nYes, I think I got to eager in my 'gentleness'. It's probably better\nto die() with an appropriate errormessage.\n\n>\n> > +/*\n> > +     if (!is_git_directory(buf + 8))\n> > +             return NULL;\n> > +*/\n>\n> Likewise.\n\nTrue, I'll uncomment and die().\n\nThanks for the review.\n\n--\nlarsh\n"},{"id":"69106","messageId":"8c5c35580802180041g5a193c31qeac1b9c920e41abf@mail.gmail.com","threadId":"12163","inReplyTo":"7v1w7au8d4.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/5] Fix setup of $GIT_DIR in git-sh-setup.sh","fromName":"Lars Hjemli","fromEmail":"hjemli@gmail.com","sentAt":"2008-02-18T08:41:12Z","receivedAt":"2008-02-18T08:41:12Z","isPatch":true,"sender":{"key":"hjemli@gmail.com","avatar":null},"body":"On Feb 18, 2008 6:44 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Lars Hjemli <hjemli@gmail.com> writes:\n>\n> > Since .git can be a file refering to the real GIT_DIR, git-sh-setup needs\n> > to use 'git rev-parse --git-dir' to obtain the location of the git\n> > repository.\n>\n> I wonder if this depend on your [1/5].  Isn't this actually a\n> simplification (removing 7 adding 5 lines) that applies to the\n> mainline already?\n\nTrue. I'll resend as [1/5] with a fixed up commit message.\n\n> > @@ -127,20 +127,18 @@ get_author_ident_from_commit () {\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> rev-parse --git-dir would have said \"fatal: Not a git\n> repository\" already.  Do we still need to say \"Failed to\n> find...\"?\n>\n\nAbsolutely not. Thanks.\n\n-- \nlarsh\n"},{"id":"69124","messageId":"alpine.LSU.1.00.0802181144270.30505@racer.site","threadId":"12163","inReplyTo":"8c5c35580802180035q3d914720x54aa7e32d6e82108@mail.gmail.com","subject":"Re: [PATCH 1/5] Add platform-independent .git \"symlink\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-18T11:45:58Z","receivedAt":"2008-02-18T11:45:58Z","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 6:43 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> > Lars Hjemli <hjemli@gmail.com> writes:\n> >\n> > > +/*\n> > > +     if (!is_git_directory(buf + 8))\n> > > +             return NULL;\n> > > +*/\n> >\n> > Likewise.\n> \n> True, I'll uncomment and die().\n\nHmm.  From check_repository_format_gently():\n\n\t\tif (!nongit_ok)\n                        die (\"...\")\n\t\twarning(\"Expected git repo version <= %d...\");\n\nI think we want that, too.  (die() when !nongit_ok, but warn otherwise)\n\nCiao,\nDscho\n"}]}