{"thread":{"id":"9300","subject":"[PATCH 0/9] work-tree clean ups","startedAt":"2007-07-29T23:23:35Z","lastAt":"2007-08-02T07:04:52Z","messageCount":30,"participants":["Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"49021","messageId":"Pine.LNX.4.64.0707300016470.14781@racer.site","threadId":"9300","inReplyTo":null,"subject":"[PATCH 0/9] work-tree clean ups","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:23:35Z","receivedAt":"2007-07-29T23:23:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nthis is the 3rd revision of the work-tree clean up series.  Unlike the 1st \nrevision, this passes all the tests.  Unlike the 2nd revision, it has a \nconcise and precise logic:\n\n--work-tree=bla overrides GIT_WORK_TREE, which overrides core.bare = true,\nwhich overrides core.worktree, which overrides GIT_DIR/.. when GIT_DIR\nends in /.git, which overrides the directory in which .git/ was found.\n\nThe distinction between git_dir and work_tree is much clearer now: you can \nhave a work_tree which is inside a git_dir, and programs needing a \nwork_tree will no longer complain.\n\nThe work tree information is no longer just thrown away.  Instead, you can \nrun git from the git_dir and it will work on the work tree without you \nhaving to cd back and forth.\n\nThe wrong distinction between a non-bare repository and a repository with \na work_tree is no longer there.  A repository is either bare, or it has a \nworking directory.  There is no third option.\n\nCiao,\nDscho\n"},{"id":"49022","messageId":"Pine.LNX.4.64.0707300023540.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"[PATCH 1/9] Add is_absolute_path() and make_absolute_path()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:24:06Z","receivedAt":"2007-07-29T23:24:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThis patch adds convenience functions to work with absolute paths.\nThe function is_absolute_path() should help the efforts to integrate\nthe MinGW fork.\n\nNote that make_absolute_path() returns a pointer to a static buffer.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile             |    2 +-\n cache.h              |    5 ++++\n path.c               |   65 ++++++++++++++++++++++++++++++++++++++++++++++++++\n t/t0000-basic.sh     |   16 ++++++++++++\n test-absolute-path.c |   11 ++++++++\n 5 files changed, 98 insertions(+), 1 deletions(-)\n create mode 100644 test-absolute-path.c\n\ndiff --git a/Makefile b/Makefile\nindex d8100ad..546e008 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -936,7 +936,7 @@ endif\n \n ### Testing rules\n \n-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X\n+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X\n \n all:: $(TEST_PROGRAMS)\n \ndiff --git a/cache.h b/cache.h\nindex 53801b8..98af530 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -358,6 +358,11 @@ int git_config_perm(const char *var, const char *value);\n int adjust_shared_perm(const char *path);\n int safe_create_leading_directories(char *path);\n char *enter_repo(char *path, int strict);\n+static inline int is_absolute_path(const char *path)\n+{\n+\treturn path[0] == '/';\n+}\n+const char *make_absolute_path(const char *path);\n \n /* Read and unpack a sha1 file into memory, write memory to a sha1 file */\n extern int sha1_object_info(const unsigned char *, unsigned long *);\ndiff --git a/path.c b/path.c\nindex dfff41f..dfa6ce0 100644\n--- a/path.c\n+++ b/path.c\n@@ -288,3 +288,68 @@ int adjust_shared_perm(const char *path)\n \t\treturn -2;\n \treturn 0;\n }\n+\n+/* We allow \"recursive\" symbolic links. Only within reason, though. */\n+#define MAXDEPTH 5\n+\n+const char *make_absolute_path(const char *path)\n+{\n+\tstatic char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];\n+\tchar cwd[1024] = \"\";\n+\tint buf_index = 1, len;\n+\n+\tint depth = MAXDEPTH;\n+\tchar *last_elem = NULL;\n+\tstruct stat st;\n+\n+\tif (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)\n+\t\tdie (\"Too long path: %.*s\", 60, path);\n+\n+\twhile (depth--) {\n+\t\tif (stat(buf, &st) || !S_ISDIR(st.st_mode)) {\n+\t\t\tchar *last_slash = strrchr(buf, '/');\n+\t\t\tif (last_slash) {\n+\t\t\t\t*last_slash = '\\0';\n+\t\t\t\tlast_elem = xstrdup(last_slash + 1);\n+\t\t\t} else\n+\t\t\t\tlast_elem = xstrdup(buf);\n+\t\t}\n+\n+\t\tif (*buf) {\n+\t\t\tif (!*cwd && getcwd(cwd, sizeof(cwd)) < 0)\n+\t\t\t\tdie (\"Could not get current working directory\");\n+\n+\t\t\tif (chdir(buf))\n+\t\t\t\tdie (\"Could not switch to '%s'\", buf);\n+\t\t}\n+\t\tif (getcwd(buf, PATH_MAX) < 0)\n+\t\t\tdie (\"Could not get current working directory\");\n+\n+\t\tif (last_elem) {\n+\t\t\tint len = strlen(buf);\n+\t\t\tif (len + strlen(last_elem) + 2 > PATH_MAX)\n+\t\t\t\tdie (\"Too long path name: '%s/%s'\",\n+\t\t\t\t\t\tbuf, last_elem);\n+\t\t\tbuf[len] = '/';\n+\t\t\tstrcpy(buf + len + 1, last_elem);\n+\t\t\tfree(last_elem);\n+\t\t\tlast_elem = NULL;\n+\t\t}\n+\n+\t\tif (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {\n+\t\t\tlen = readlink(buf, next_buf, PATH_MAX);\n+\t\t\tif (len < 0)\n+\t\t\t\tdie (\"Invalid symlink: %s\", buf);\n+\t\t\tnext_buf[len] = '\\0';\n+\t\t\tbuf = next_buf;\n+\t\t\tbuf_index = 1 - buf_index;\n+\t\t\tnext_buf = bufs[buf_index];\n+\t\t} else\n+\t\t\tbreak;\n+\t}\n+\n+\tif (*cwd && chdir(cwd))\n+\t\tdie (\"Could not change back to '%s'\", cwd);\n+\n+\treturn buf;\n+}\ndiff --git a/t/t0000-basic.sh b/t/t0000-basic.sh\nindex 4bba9c0..4e49d59 100755\n--- a/t/t0000-basic.sh\n+++ b/t/t0000-basic.sh\n@@ -281,4 +281,20 @@ test_expect_success 'update-index D/F conflict' '\n \ttest $numpath0 = 1\n '\n \n+test_expect_success 'absolute path works as expected' '\n+\tmkdir first &&\n+\tln -s ../.git first/.git &&\n+\tmkdir second &&\n+\tln -s ../first second/other &&\n+\tmkdir third &&\n+\tdir=\"$(cd .git; pwd -P)\" &&\n+\tdir2=third/../second/other/.git &&\n+\ttest \"$dir\" = \"$(test-absolute-path $dir2)\" &&\n+\tfile=\"$dir\"/index &&\n+\ttest \"$file\" = \"$(test-absolute-path $dir2/index)\" &&\n+\tln -s ../first/file .git/syml &&\n+\tsym=\"$(cd first; pwd -P)\"/file &&\n+\ttest \"$sym\" = \"$(test-absolute-path $dir2/syml)\"\n+'\n+\n test_done\ndiff --git a/test-absolute-path.c b/test-absolute-path.c\nnew file mode 100644\nindex 0000000..c959ea2\n--- /dev/null\n+++ b/test-absolute-path.c\n@@ -0,0 +1,11 @@\n+#include \"cache.h\"\n+\n+int main(int argc, char **argv)\n+{\n+\twhile (argc > 1) {\n+\t\tputs(make_absolute_path(argv[1]));\n+\t\targc--;\n+\t\targv++;\n+\t}\n+\treturn 0;\n+}\n-- \n1.5.3.rc3.28.g1406\n"},{"id":"49023","messageId":"Pine.LNX.4.64.0707300024150.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"[PATCH 2/9] Add functions get_relative_cwd() and is_inside_dir()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:24:24Z","receivedAt":"2007-07-29T23:24:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThe function get_relative_cwd() works just as getcwd(), only that it\ntakes an absolute path as additional parameter, returning the prefix\nof the current working directory relative to the given path.  If the\ncwd is no subdirectory of the given path, it returns NULL.\n\nis_inside_dir() is just a trivial wrapper over get_relative_cwd().\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n dir.c |   30 ++++++++++++++++++++++++++++++\n dir.h |    3 +++\n 2 files changed, 33 insertions(+), 0 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 8d8faf5..ef67d6f 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -642,3 +642,33 @@ file_exists(const char *f)\n   struct stat sb;\n   return stat(f, &sb) == 0;\n }\n+\n+char *get_relative_cwd(char *buffer, int size, const char *dir)\n+{\n+\tchar *cwd = buffer;\n+\n+\tif (!dir)\n+\t\treturn 0;\n+\n+\tif (!getcwd(buffer, size))\n+\t\treturn 0;\n+\n+\tif (!is_absolute_path(dir))\n+\t\tdir = make_absolute_path(dir);\n+\n+\twhile (*dir && *dir == *cwd) {\n+\t\tdir++;\n+\t\tcwd++;\n+\t}\n+\tif (*dir)\n+\t\treturn NULL;\n+\tif (*cwd == '/')\n+\t\treturn cwd + 1;\n+\treturn cwd;\n+}\n+\n+int is_inside_dir(const char *dir)\n+{\n+\tchar buffer[PATH_MAX];\n+\treturn get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;\n+}\ndiff --git a/dir.h b/dir.h\nindex ec0e8ab..f55a87b 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -61,4 +61,7 @@ extern void add_exclude(const char *string, const char *base,\n extern int file_exists(const char *);\n extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len);\n \n+extern char *get_relative_cwd(char *buffer, int size, const char *dir);\n+extern int is_inside_dir(const char *dir);\n+\n #endif\n-- \n1.5.3.rc3.28.g1406\n"},{"id":"49024","messageId":"Pine.LNX.4.64.0707300024310.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"[PATCH 3/9] white space fixes in setup.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:24:38Z","receivedAt":"2007-07-29T23:24:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nSome lines were not indented by tabs but by spaces.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tJust to make the next patch look nicer.\n\n setup.c |    6 +++---\n 1 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex 7b07144..b54d65f 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -382,11 +382,11 @@ int git_config_perm(const char *var, const char *value)\n \n int check_repository_format_version(const char *var, const char *value)\n {\n-       if (strcmp(var, \"core.repositoryformatversion\") == 0)\n-               repository_format_version = git_config_int(var, value);\n+\tif (strcmp(var, \"core.repositoryformatversion\") == 0)\n+\t\trepository_format_version = git_config_int(var, value);\n \telse if (strcmp(var, \"core.sharedrepository\") == 0)\n \t\tshared_repository = git_config_perm(var, value);\n-       return 0;\n+\treturn 0;\n }\n \n int check_repository_format(void)\n-- \n1.5.3.rc3.28.g1406\n"},{"id":"49025","messageId":"Pine.LNX.4.64.0707300024450.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"[PATCH 4/9] Clean up work-tree handling","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:25:02Z","receivedAt":"2007-07-29T23:25:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThe old version of work-tree support was an unholy mess, barely readable,\nand not to the point.\n\nFor example, why do you have to provide a worktree, when it is not used?\nAs in \"git status\".  Now it works.\n\nAnother riddle was: if you can have work trees inside the git dir, why\nare some programs complaining that they need a work tree?\n\nIOW when inside repo.git/work, where GIT_DIR points to repo.git\nand GIT_WORK_TREE to work, and cwd is work, --is-inside-git-dir _must_\nreturn true, because it is _in the git dir_, but scripts _must_ test\nfor the right thing.\n\nAlso, GIT_DIR=../.git should behave the same as if no GIT_DIR was\nspecified, unless there is a repository in the current working directory.\nIt does now.\n\nThe logic to determine if a repository is bare, or has a work tree\n(tertium non datur), is this:\n\n--work-tree=bla overrides GIT_WORK_TREE, which overrides core.bare = true,\nwhich overrides core.worktree, which overrides GIT_DIR/.. when GIT_DIR\nends in /.git, which overrides the directory in which .git/ was found.\n\nIn related news, a long standing bug was fixed: when in .git/bla/x.git/,\nwhich is a bare repository, git formerly assumed ../.. to be the\nappropriate git dir.  This problem was reported by Shawn Pearce to have\ncaused much pain, where a colleague mistakenly ran \"git init\" in \"/\" a\nlong time ago, and bare repositories just would not work.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tThis version no longer removes more lines than it adds, but it\n\tadds a lot more documentation than it removes.  IMHO it is also\n\teasier to follow, implements a sane logic, and stands a chance\n\tto be fixed by somebody else than the original author.\n\n builtin-rev-parse.c |    7 ++\n cache.h             |    2 +\n environment.c       |   35 +++++--\n setup.c             |  279 +++++++++++++++++++++++----------------------------\n 4 files changed, 162 insertions(+), 161 deletions(-)\n\ndiff --git a/builtin-rev-parse.c b/builtin-rev-parse.c\nindex 497903a..15bdb72 100644\n--- a/builtin-rev-parse.c\n+++ b/builtin-rev-parse.c\n@@ -320,6 +320,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--show-cdup\")) {\n+\t\t\t\tif (!is_inside_work_tree()) {\n+\t\t\t\t\tconst char *work_tree =\n+\t\t\t\t\t\tget_git_work_tree();\n+\t\t\t\t\tif (work_tree)\n+\t\t\t\t\t\tprintf(\"%s\\n\", work_tree);\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n \t\t\t\tconst char *pfx = prefix;\n \t\t\t\twhile (pfx) {\n \t\t\t\t\tpfx = strchr(pfx, '/');\ndiff --git a/cache.h b/cache.h\nindex 98af530..c0cab34 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -208,12 +208,14 @@ enum object_type {\n extern int is_bare_repository_cfg;\n extern int is_bare_repository(void);\n extern int is_inside_git_dir(void);\n+extern char *git_work_tree_cfg;\n extern int is_inside_work_tree(void);\n extern const char *get_git_dir(void);\n extern char *get_object_directory(void);\n extern char *get_refs_directory(void);\n extern char *get_index_file(void);\n extern char *get_graft_file(void);\n+extern const char *get_git_work_tree(void);\n \n #define ALTERNATE_DB_ENVIRONMENT \"GIT_ALTERNATE_OBJECT_DIRECTORIES\"\n \ndiff --git a/environment.c b/environment.c\nindex f83fb9e..26f41af 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -35,6 +35,10 @@ int pager_in_use;\n int pager_use_color = 1;\n int auto_crlf = 0;\t/* 1: both ways, -1: only when adding git objects */\n \n+/* This is set by setup_git_dir_gently() and/or git_default_config() */\n+char *git_work_tree_cfg;\n+static const char *work_tree;\n+\n static const char *git_dir;\n static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;\n \n@@ -62,15 +66,8 @@ static void setup_git_env(void)\n \n int is_bare_repository(void)\n {\n-\tconst char *dir, *s;\n-\tif (0 <= is_bare_repository_cfg)\n-\t\treturn is_bare_repository_cfg;\n-\n-\tdir = get_git_dir();\n-\tif (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))\n-\t\treturn 0;\n-\ts = strrchr(dir, '/');\n-\treturn !s || strcmp(s + 1, DEFAULT_GIT_DIR_ENVIRONMENT);\n+\t/* if core.bare is not 'false', let's see if there is a work tree */\n+\treturn is_bare_repository_cfg && !get_git_work_tree();\n }\n \n const char *get_git_dir(void)\n@@ -80,6 +77,26 @@ const char *get_git_dir(void)\n \treturn git_dir;\n }\n \n+const char *get_git_work_tree(void)\n+{\n+\tstatic int initialized = 0;\n+\tif (!initialized) {\n+\t\twork_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);\n+\t\t/* core.bare = true overrides implicit and config work tree */\n+\t\tif (!work_tree && is_bare_repository_cfg < 1) {\n+\t\t\twork_tree = git_work_tree_cfg;\n+\t\t\t/* make_absolute_path also normalizes the path */\n+\t\t\tif (work_tree && !is_absolute_path(work_tree))\n+\t\t\t\twork_tree = xstrdup(make_absolute_path(git_path(work_tree)));\n+\t\t} else if (work_tree)\n+\t\t\twork_tree = xstrdup(make_absolute_path(work_tree));\n+\t\tinitialized = 1;\n+\t\tif (work_tree)\n+\t\t\tis_bare_repository_cfg = 0;\n+\t}\n+\treturn work_tree;\n+}\n+\n char *get_object_directory(void)\n {\n \tif (!git_object_dir)\ndiff --git a/setup.c b/setup.c\nindex b54d65f..8237fe3 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1,4 +1,8 @@\n #include \"cache.h\"\n+#include \"dir.h\"\n+\n+static int inside_git_dir = -1;\n+static int inside_work_tree = -1;\n \n const char *prefix_path(const char *prefix, int len, const char *path)\n {\n@@ -170,100 +174,89 @@ static int is_git_directory(const char *suspect)\n \treturn 1;\n }\n \n-static int inside_git_dir = -1;\n-\n int is_inside_git_dir(void)\n {\n-\tif (inside_git_dir >= 0)\n-\t\treturn inside_git_dir;\n-\tdie(\"BUG: is_inside_git_dir called before setup_git_directory\");\n+\tif (inside_git_dir < 0)\n+\t\tinside_git_dir = is_inside_dir(get_git_dir());\n+\treturn inside_git_dir;\n }\n \n-static int inside_work_tree = -1;\n-\n int is_inside_work_tree(void)\n {\n-\tif (inside_git_dir >= 0)\n-\t\treturn inside_work_tree;\n-\tdie(\"BUG: is_inside_work_tree called before setup_git_directory\");\n+\tif (inside_work_tree < 0)\n+\t\tinside_work_tree = is_inside_dir(get_git_work_tree());\n+\treturn inside_work_tree;\n }\n \n-static char *gitworktree_config;\n-\n-static int git_setup_config(const char *var, const char *value)\n+/*\n+ * If no worktree was given, and we are outside of a default work tree,\n+ * now is the time to set it.\n+ *\n+ * In other words, if the user calls git with something like\n+ *\n+ *\tgit --git-dir=/some/where/else/.git bla\n+ *\n+ * default to /some/where/else as working directory; if the specified\n+ * git-dir does not end in \"/.git\", the cwd is used as working directory.\n+ */\n+const char* set_work_tree(const char *dir)\n {\n-\tif (!strcmp(var, \"core.worktree\")) {\n-\t\tif (gitworktree_config)\n-\t\t\tstrlcpy(gitworktree_config, value, PATH_MAX);\n-\t\treturn 0;\n+\tchar dir_buffer[PATH_MAX];\n+\tstatic char buffer[PATH_MAX + 1], *rel = NULL;\n+\tint len, postfix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1;\n+\n+\t/* strip the variable 'dir' of the postfix \"/.git\" if it has it */\n+\tlen = strlen(dir);\n+\tif (len > postfix_len && !strcmp(dir + len - postfix_len,\n+\t\t\t\t\"/\" DEFAULT_GIT_DIR_ENVIRONMENT)) {\n+\t\t\tstrncpy(dir_buffer, dir, len - postfix_len);\n+\n+\t\t/* are we inside the default work tree? */\n+\t\trel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer);\n+\t}\n+\t/* if rel is set, the cwd is _not_ the current working tree */\n+\tif (rel && *rel) {\n+\t\tif (!is_absolute_path(dir))\n+\t\t\tset_git_dir(make_absolute_path(dir));\n+\t\tdir = dir_buffer;\n+\t\tchdir(dir);\n+\t\tstrcat(rel, \"/\");\n+\t\tinside_git_dir = 0;\n+\t} else {\n+\t\trel = NULL;\n+\t\tdir = getcwd(buffer, sizeof(buffer));\n \t}\n-\treturn git_default_config(var, value);\n+\tgit_work_tree_cfg = xstrdup(dir);\n+\tinside_work_tree = 1;\n+\n+\treturn rel;\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 const char *setup_git_directory_gently(int *nongit_ok)\n {\n+\tconst char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);\n \tstatic char cwd[PATH_MAX+1];\n-\tchar worktree[PATH_MAX+1], gitdir[PATH_MAX+1];\n-\tconst char *gitdirenv, *gitworktree;\n-\tint wt_rel_gitdir = 0;\n+\tconst char *gitdirenv;\n+\tint len, offset;\n \n+\t/*\n+\t * If GIT_DIR is set explicitly, we're not going\n+\t * to do any discovery, but we still do repository\n+\t * validation.\n+\t */\n \tgitdirenv = getenv(GIT_DIR_ENVIRONMENT);\n-\tif (!gitdirenv) {\n-\t\tint len, offset;\n-\n-\t\tif (!getcwd(cwd, sizeof(cwd)-1))\n-\t\t\tdie(\"Unable to read current working directory\");\n-\n-\t\toffset = len = strlen(cwd);\n-\t\tfor (;;) {\n-\t\t\tif (is_git_directory(\".git\"))\n-\t\t\t\tbreak;\n-\t\t\tif (offset == 0) {\n-\t\t\t\toffset = -1;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tchdir(\"..\");\n-\t\t\twhile (cwd[--offset] != '/')\n-\t\t\t\t; /* do nothing */\n-\t\t}\n-\n-\t\tif (offset >= 0) {\n-\t\t\tinside_work_tree = 1;\n-\t\t\tgit_config(git_default_config);\n-\t\t\tif (offset == len) {\n-\t\t\t\tinside_git_dir = 0;\n-\t\t\t\treturn NULL;\n-\t\t\t}\n-\n-\t\t\tcwd[len++] = '/';\n-\t\t\tcwd[len] = '\\0';\n-\t\t\tinside_git_dir = !prefixcmp(cwd + offset + 1, \".git/\");\n-\t\t\treturn cwd + offset + 1;\n-\t\t}\n-\n-\t\tif (chdir(cwd))\n-\t\t\tdie(\"Cannot come back to cwd\");\n-\t\tif (!is_git_directory(\".\")) {\n-\t\t\tif (nongit_ok) {\n-\t\t\t\t*nongit_ok = 1;\n-\t\t\t\treturn NULL;\n-\t\t\t}\n-\t\t\tdie(\"Not a git repository\");\n-\t\t}\n-\t\tsetenv(GIT_DIR_ENVIRONMENT, cwd, 1);\n-\t\tgitdirenv = getenv(GIT_DIR_ENVIRONMENT);\n-\t\tif (!gitdirenv)\n-\t\t\tdie(\"getenv after setenv failed\");\n-\t}\n-\n-\tif (PATH_MAX - 40 < strlen(gitdirenv)) {\n-\t\tif (nongit_ok) {\n-\t\t\t*nongit_ok = 1;\n+\tif (gitdirenv) {\n+\t\tif (PATH_MAX - 40 < strlen(gitdirenv))\n+\t\t\tdie(\"'$%s' too big\", GIT_DIR_ENVIRONMENT);\n+\t\tif (is_git_directory(gitdirenv)) {\n+\t\t\tif (!work_tree_env)\n+\t\t\t\treturn set_work_tree(gitdirenv);\n \t\t\treturn NULL;\n \t\t}\n-\t\tdie(\"$%s too big\", GIT_DIR_ENVIRONMENT);\n-\t}\n-\tif (!is_git_directory(gitdirenv)) {\n \t\tif (nongit_ok) {\n \t\t\t*nongit_ok = 1;\n \t\t\treturn NULL;\n@@ -273,92 +266,53 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \n \tif (!getcwd(cwd, sizeof(cwd)-1))\n \t\tdie(\"Unable to read current working directory\");\n-\tif (chdir(gitdirenv)) {\n-\t\tif (nongit_ok) {\n-\t\t\t*nongit_ok = 1;\n-\t\t\treturn NULL;\n-\t\t}\n-\t\tdie(\"Cannot change directory to $%s '%s'\",\n-\t\t\tGIT_DIR_ENVIRONMENT, gitdirenv);\n-\t}\n-\tif (!getcwd(gitdir, sizeof(gitdir)-1))\n-\t\tdie(\"Unable to read current working directory\");\n-\tif (chdir(cwd))\n-\t\tdie(\"Cannot come back to cwd\");\n \n \t/*\n-\t * In case there is a work tree we may change the directory,\n-\t * therefore make GIT_DIR an absolute path.\n+\t * Test in the following order (relative to the cwd):\n+\t * - .git/\n+\t * - ./ (bare)\n+\t * - ../.git/\n+\t * - ../ (bare)\n+\t * - ../../.git/\n+\t *   etc.\n \t */\n-\tif (gitdirenv[0] != '/') {\n-\t\tsetenv(GIT_DIR_ENVIRONMENT, gitdir, 1);\n-\t\tgitdirenv = getenv(GIT_DIR_ENVIRONMENT);\n-\t\tif (!gitdirenv)\n-\t\t\tdie(\"getenv after setenv failed\");\n-\t\tif (PATH_MAX - 40 < strlen(gitdirenv)) {\n-\t\t\tif (nongit_ok) {\n-\t\t\t\t*nongit_ok = 1;\n-\t\t\t\treturn NULL;\n-\t\t\t}\n-\t\t\tdie(\"$%s too big after expansion to absolute path\",\n-\t\t\t\tGIT_DIR_ENVIRONMENT);\n-\t\t}\n-\t}\n-\n-\tstrcat(cwd, \"/\");\n-\tstrcat(gitdir, \"/\");\n-\tinside_git_dir = !prefixcmp(cwd, gitdir);\n-\n-\tgitworktree = getenv(GIT_WORK_TREE_ENVIRONMENT);\n-\tif (!gitworktree) {\n-\t\tgitworktree_config = worktree;\n-\t\tworktree[0] = '\\0';\n-\t}\n-\tgit_config(git_setup_config);\n-\tif (!gitworktree) {\n-\t\tgitworktree_config = NULL;\n-\t\tif (worktree[0])\n-\t\t\tgitworktree = worktree;\n-\t\tif (gitworktree && gitworktree[0] != '/')\n-\t\t\twt_rel_gitdir = 1;\n-\t}\n-\n-\tif (wt_rel_gitdir && chdir(gitdirenv))\n-\t\tdie(\"Cannot change directory to $%s '%s'\",\n-\t\t\tGIT_DIR_ENVIRONMENT, gitdirenv);\n-\tif (gitworktree && chdir(gitworktree)) {\n-\t\tif (nongit_ok) {\n-\t\t\tif (wt_rel_gitdir && chdir(cwd))\n-\t\t\t\tdie(\"Cannot come back to cwd\");\n-\t\t\t*nongit_ok = 1;\n+\toffset = len = strlen(cwd);\n+\tfor (;;) {\n+\t\tif (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))\n+\t\t\tbreak;\n+\t\tif (is_git_directory(\".\")) {\n+\t\t\tinside_git_dir = 1;\n+\t\t\tif (!work_tree_env)\n+\t\t\t\tinside_work_tree = 0;\n+\t\t\tsetenv(GIT_DIR_ENVIRONMENT, \".\", 1);\n \t\t\treturn NULL;\n \t\t}\n-\t\tif (wt_rel_gitdir)\n-\t\t\tdie(\"Cannot change directory to working tree '%s'\"\n-\t\t\t\t\" from $%s\", gitworktree, GIT_DIR_ENVIRONMENT);\n-\t\telse\n-\t\t\tdie(\"Cannot change directory to working tree '%s'\",\n-\t\t\t\tgitworktree);\n-\t}\n-\tif (!getcwd(worktree, sizeof(worktree)-1))\n-\t\tdie(\"Unable to read current working directory\");\n-\tstrcat(worktree, \"/\");\n-\tinside_work_tree = !prefixcmp(cwd, worktree);\n-\n-\tif (gitworktree && inside_work_tree && !prefixcmp(worktree, gitdir) &&\n-\t    strcmp(worktree, gitdir)) {\n-\t\tinside_git_dir = 0;\n+\t\tchdir(\"..\");\n+\t\tdo {\n+\t\t\tif (!offset) {\n+\t\t\t\tif (nongit_ok) {\n+\t\t\t\t\tif (chdir(cwd))\n+\t\t\t\t\t\tdie(\"Cannot come back to cwd\");\n+\t\t\t\t\t*nongit_ok = 1;\n+\t\t\t\t\treturn NULL;\n+\t\t\t\t}\n+\t\t\t\tdie(\"Not a git repository\");\n+\t\t\t}\n+\t\t} while (cwd[--offset] != '/');\n \t}\n \n-\tif (!inside_work_tree) {\n-\t\tif (chdir(cwd))\n-\t\t\tdie(\"Cannot come back to cwd\");\n+\tinside_git_dir = 0;\n+\tif (!work_tree_env)\n+\t\tinside_work_tree = 1;\n+\tgit_work_tree_cfg = xstrndup(cwd, offset);\n+\tif (offset == len)\n \t\treturn NULL;\n-\t}\n \n-\tif (!strcmp(cwd, worktree))\n-\t\treturn NULL;\n-\treturn cwd+strlen(worktree);\n+\t/* Make \"offset\" point to past the '/', and add a '/' at the end */\n+\toffset++;\n+\tcwd[len++] = '/';\n+\tcwd[len] = 0;\n+\treturn cwd + offset;\n }\n \n int git_config_perm(const char *var, const char *value)\n@@ -386,6 +340,16 @@ int check_repository_format_version(const char *var, const char *value)\n \t\trepository_format_version = git_config_int(var, value);\n \telse if (strcmp(var, \"core.sharedrepository\") == 0)\n \t\tshared_repository = git_config_perm(var, value);\n+\telse if (strcmp(var, \"core.bare\") == 0) {\n+\t\tis_bare_repository_cfg = git_config_bool(var, value);\n+\t\tif (is_bare_repository_cfg == 1)\n+\t\t\tinside_work_tree = -1;\n+\t} else if (strcmp(var, \"core.worktree\") == 0) {\n+\t\tif (git_work_tree_cfg)\n+\t\t\tfree(git_work_tree_cfg);\n+\t\tgit_work_tree_cfg = xstrdup(value);\n+\t\tinside_work_tree = -1;\n+\t}\n \treturn 0;\n }\n \n@@ -402,5 +366,16 @@ const char *setup_git_directory(void)\n {\n \tconst char *retval = setup_git_directory_gently(NULL);\n \tcheck_repository_format();\n+\n+\t/* If the work tree is not the default one, recompute prefix */\n+\tif (inside_work_tree < 0) {\n+\t\tstatic char buffer[PATH_MAX + 1];\n+\t\tif (retval && chdir(retval))\n+\t\t\tdie (\"Could not jump back into original cwd\");\n+\t\tchar *rel = get_relative_cwd(buffer, PATH_MAX,\n+\t\t\t\tget_git_work_tree());\n+\t\treturn rel && *rel ? strcat(rel, \"/\") : NULL;\n+\t}\n+\n \treturn retval;\n }\n-- \n1.5.3.rc3.28.g1406\n"},{"id":"49026","messageId":"Pine.LNX.4.64.0707300025100.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"[PATCH 5/9] Add set_git_dir() function","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:25:18Z","receivedAt":"2007-07-29T23:25:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nWith the function set_git_dir() you can reset the path that will\nbe used for git_path(), git_dir() and friends.\n\nThe responsibility to close files and throw away information from the\nold git_dir lies with the caller.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n cache.h       |    1 +\n environment.c |    8 ++++++++\n 2 files changed, 9 insertions(+), 0 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex c0cab34..36963f2 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -216,6 +216,7 @@ extern char *get_refs_directory(void);\n extern char *get_index_file(void);\n extern char *get_graft_file(void);\n extern const char *get_git_work_tree(void);\n+extern int set_git_dir(const char *path);\n \n #define ALTERNATE_DB_ENVIRONMENT \"GIT_ALTERNATE_OBJECT_DIRECTORIES\"\n \ndiff --git a/environment.c b/environment.c\nindex 26f41af..2af12fd 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -124,3 +124,11 @@ char *get_graft_file(void)\n \t\tsetup_git_env();\n \treturn git_graft_file;\n }\n+\n+int set_git_dir(const char *path)\n+{\n+\tif (setenv(GIT_DIR_ENVIRONMENT, path, 1))\n+\t\treturn error(\"Could not set GIT_DIR to '%s'\", path);\n+\tsetup_git_env();\n+\treturn 0;\n+}\n-- \n1.5.3.rc3.28.g1406\n"},{"id":"49027","messageId":"Pine.LNX.4.64.0707300025250.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"[PATCH 6/9] work-trees are allowed inside a git-dir","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:25:42Z","receivedAt":"2007-07-29T23:25:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nIt is allowed to call\n\n\t$ git --git-dir=../ --work-tree=. bla\n\nwhen you really want to.  In this case, you are both in the git directory\nand in the working tree.\n\nThe earlier handling of this situation was seriously bogus.  For regular\nworking tree operations, it checked if inside git dir.  That makes no\nsense, of course, since the check should be for a work tree, and nothing\nelse.\n\nFix that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin-ls-files.c |    8 +++++---\n git-sh-setup.sh    |    3 +--\n git.c              |   11 ++++++++---\n 3 files changed, 14 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex 61577ea..d36181a 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -469,9 +469,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\tbreak;\n \t}\n \n-\tif (require_work_tree &&\n-\t\t\t(!is_inside_work_tree() || is_inside_git_dir()))\n-\t\tdie(\"This operation must be run in a work tree\");\n+\tif (require_work_tree && !is_inside_work_tree()) {\n+\t\tconst char *work_tree = get_git_work_tree();\n+\t\tif (!work_tree || chdir(work_tree))\n+\t\t\tdie(\"This operation must be run in a work tree\");\n+\t}\n \n \tpathspec = get_pathspec(prefix, argv + i);\n \ndiff --git a/git-sh-setup.sh b/git-sh-setup.sh\nindex c51985e..7bef43f 100755\n--- a/git-sh-setup.sh\n+++ b/git-sh-setup.sh\n@@ -59,8 +59,7 @@ cd_to_toplevel () {\n }\n \n require_work_tree () {\n-\ttest $(git rev-parse --is-inside-work-tree) = true &&\n-\ttest $(git rev-parse --is-inside-git-dir) = false ||\n+\ttest $(git rev-parse --is-inside-work-tree) = true ||\n \tdie \"fatal: $0 cannot be used without a working tree.\"\n }\n \ndiff --git a/git.c b/git.c\nindex a647f9c..2433355 100644\n--- a/git.c\n+++ b/git.c\n@@ -272,9 +272,14 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)\n \t\tprefix = setup_git_directory();\n \tif (p->option & USE_PAGER)\n \t\tsetup_pager();\n-\tif ((p->option & NEED_WORK_TREE) &&\n-\t    (!is_inside_work_tree() || is_inside_git_dir()))\n-\t\tdie(\"%s must be run in a work tree\", p->cmd);\n+\tif (p->option & NEED_WORK_TREE) {\n+\t\tconst char *work_tree = get_git_work_tree();\n+\t\tconst char *git_dir = get_git_dir();\n+\t\tif (!is_absolute_path(git_dir))\n+\t\t\tset_git_dir(make_absolute_path(git_dir));\n+\t\tif (!work_tree || chdir(work_tree))\n+\t\t\tdie(\"%s must be run in a work tree\", p->cmd);\n+\t}\n \ttrace_argv_printf(argv, argc, \"trace: built-in: git\");\n \n \tstatus = p->fn(argc, argv, prefix);\n-- \n1.5.3.rc3.28.g1406\n"},{"id":"49028","messageId":"Pine.LNX.4.64.0707300025500.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"[PATCH 7/9] init: use get_git_work_tree() instead of rolling our own","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:25:57Z","receivedAt":"2007-07-29T23:25:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin-init-db.c |   48 +++++++++++-------------------------------------\n 1 files changed, 11 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin-init-db.c b/builtin-init-db.c\nindex 66ddaeb..0d9b1e0 100644\n--- a/builtin-init-db.c\n+++ b/builtin-init-db.c\n@@ -174,36 +174,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di\n \tclosedir(dir);\n }\n \n-/*\n- * Get the full path to the working tree specified in $GIT_WORK_TREE\n- * or NULL if no working tree is specified.\n- */\n-static const char *get_work_tree(void)\n-{\n-\tconst char *git_work_tree;\n-\tchar cwd[PATH_MAX];\n-\tstatic char worktree[PATH_MAX];\n-\n-\tgit_work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);\n-\tif (!git_work_tree)\n-\t\treturn NULL;\n-\tif (!getcwd(cwd, sizeof(cwd)))\n-\t\tdie(\"Unable to read current working directory\");\n-\tif (chdir(git_work_tree))\n-\t\tdie(\"Cannot change directory to specified working tree '%s'\",\n-\t\t\tgit_work_tree);\n-\tif (git_work_tree[0] != '/') {\n-\t\tif (!getcwd(worktree, sizeof(worktree)))\n-\t\t\tdie(\"Unable to read current working directory\");\n-\t\tgit_work_tree = worktree;\n-\t}\n-\tif (chdir(cwd))\n-\t\tdie(\"Cannot come back to cwd\");\n-\treturn git_work_tree;\n-}\n-\n-static int create_default_files(const char *git_dir, const char *git_work_tree,\n-\tconst char *template_path)\n+static int create_default_files(const char *git_dir, const char *template_path)\n {\n \tunsigned len = strlen(git_dir);\n \tstatic char path[PATH_MAX];\n@@ -282,16 +253,16 @@ static int create_default_files(const char *git_dir, const char *git_work_tree,\n \t}\n \tgit_config_set(\"core.filemode\", filemode ? \"true\" : \"false\");\n \n-\tif (is_bare_repository() && !git_work_tree) {\n+\tif (is_bare_repository())\n \t\tgit_config_set(\"core.bare\", \"true\");\n-\t}\n \telse {\n+\t\tconst char *work_tree = get_git_work_tree();\n \t\tgit_config_set(\"core.bare\", \"false\");\n \t\t/* allow template config file to override the default */\n \t\tif (log_all_ref_updates == -1)\n \t\t    git_config_set(\"core.logallrefupdates\", \"true\");\n-\t\tif (git_work_tree)\n-\t\t\tgit_config_set(\"core.worktree\", git_work_tree);\n+\t\tif (work_tree != git_work_tree_cfg)\n+\t\t\tgit_config_set(\"core.worktree\", work_tree);\n \t}\n \treturn reinit;\n }\n@@ -308,7 +279,6 @@ static const char init_db_usage[] =\n int cmd_init_db(int argc, const char **argv, const char *prefix)\n {\n \tconst char *git_dir;\n-\tconst char *git_work_tree;\n \tconst char *sha1_dir;\n \tconst char *template_dir = NULL;\n \tchar *path;\n@@ -329,7 +299,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\t\tusage(init_db_usage);\n \t}\n \n-\tgit_work_tree = get_work_tree();\n+\tgit_work_tree_cfg = xcalloc(PATH_MAX, 1);\n+\tif (!getcwd(git_work_tree_cfg, PATH_MAX))\n+\t\tdie (\"Cannot access current working directory.\");\n+\tif (access(get_git_work_tree(), X_OK))\n+\t\tdie (\"Cannot access work tree '%s'\", get_git_work_tree());\n \n \t/*\n \t * Set up the default .git directory contents\n@@ -346,7 +320,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t */\n \tcheck_repository_format();\n \n-\treinit = create_default_files(git_dir, git_work_tree, template_dir);\n+\treinit = create_default_files(git_dir, template_dir);\n \n \t/*\n \t * And set up the object store.\n-- \n1.5.3.rc3.28.g1406\n"},{"id":"49029","messageId":"Pine.LNX.4.64.0707300026030.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"[PATCH 8/9] Fix t1501 for updated work-tree logic","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:26:22Z","receivedAt":"2007-07-29T23:26:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nt1501 is still a PITA to debug.  But I decided not to change it, so\nthat it is easier to see what the differences in the logic are.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t1501-worktree.sh |   24 +++++++++++++++++++-----\n 1 files changed, 19 insertions(+), 5 deletions(-)\n\ndiff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh\nindex aadeeab..7322161 100755\n--- a/t/t1501-worktree.sh\n+++ b/t/t1501-worktree.sh\n@@ -33,17 +33,17 @@ mv .git repo.git || exit 1\n \n say \"core.worktree = relative path\"\n export GIT_DIR=repo.git\n-export GIT_CONFIG=$GIT_DIR/config\n+export GIT_CONFIG=\"$(pwd)\"/$GIT_DIR/config\n unset GIT_WORK_TREE\n git config core.worktree ../work\n test_rev_parse 'outside'      false false false\n cd work || exit 1\n export GIT_DIR=../repo.git\n-export GIT_CONFIG=$GIT_DIR/config\n+export GIT_CONFIG=\"$(pwd)\"/$GIT_DIR/config\n test_rev_parse 'inside'       false false true ''\n cd sub/dir || exit 1\n export GIT_DIR=../../../repo.git\n-export GIT_CONFIG=$GIT_DIR/config\n+export GIT_CONFIG=\"$(pwd)\"/$GIT_DIR/config\n test_rev_parse 'subdirectory' false false true sub/dir/\n cd ../../.. || exit 1\n \n@@ -84,9 +84,23 @@ test_rev_parse 'in repo.git'              false true  false\n cd objects || exit 1\n test_rev_parse 'in repo.git/objects'      false true  false\n cd ../work || exit 1\n-test_rev_parse 'in repo.git/work'         false false true ''\n+test_rev_parse 'in repo.git/work'         false true true ''\n cd sub/dir || exit 1\n-test_rev_parse 'in repo.git/sub/dir' false false true sub/dir/\n+test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/\n cd ../../../.. || exit 1\n \n+test_expect_success 'repo finds its work tree' '\n+\t(cd repo.git &&\n+\t : > work/sub/dir/untracked &&\n+\t test sub/dir/untracked = \"$(git ls-files --others)\")\n+'\n+\n+test_expect_success 'repo finds its work tree from work tree, too' '\n+\t(cd repo.git/work/sub/dir &&\n+\t : > tracked &&\n+\t git --git-dir=../../.. add tracked &&\n+\t cd ../../.. &&\n+\t test sub/dir/tracked = \"$(git ls-files)\")\n+'\n+\n test_done\n-- \n1.5.3.rc3.28.g1406\n"},{"id":"49030","messageId":"Pine.LNX.4.64.0707300026350.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"[PATCH 9/9] Fix t1500 for sane work-tree behavior","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:26:47Z","receivedAt":"2007-07-29T23:26:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nWhen GIT_DIR=../.git, and no worktree is specified, it is reasonable\nto assume that the repository is not bare, that the work tree is \"..\"\nand that the prefix is the basename of the current directory.\n\nThis is the sane behavior.\n\nt1500 tested for the old behavior, which was plain wrong.  And this\npatch fixes it minimally.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t1500-rev-parse.sh |   20 ++++++++++----------\n 1 files changed, 10 insertions(+), 10 deletions(-)\n\ndiff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh\nindex ec49966..bea40cb 100755\n--- a/t/t1500-rev-parse.sh\n+++ b/t/t1500-rev-parse.sh\n@@ -31,9 +31,9 @@ test_rev_parse() {\n test_rev_parse toplevel false false true ''\n \n cd .git || exit 1\n-test_rev_parse .git/ false true true .git/\n+test_rev_parse .git/ true true false ''\n cd objects || exit 1\n-test_rev_parse .git/objects/ false true true .git/objects/\n+test_rev_parse .git/objects/ true true false ''\n cd ../.. || exit 1\n \n mkdir -p sub/dir || exit 1\n@@ -42,7 +42,7 @@ test_rev_parse subdirectory false false true sub/dir/\n cd ../.. || exit 1\n \n git config core.bare true\n-test_rev_parse 'core.bare = true' true false true\n+test_rev_parse 'core.bare = true' true false false\n \n git config --unset core.bare\n test_rev_parse 'core.bare undefined' false false true\n@@ -50,28 +50,28 @@ test_rev_parse 'core.bare undefined' false false true\n mkdir work || exit 1\n cd work || exit 1\n export GIT_DIR=../.git\n-export GIT_CONFIG=\"$GIT_DIR\"/config\n+export GIT_CONFIG=\"$(pwd)\"/../.git/config\n \n git config core.bare false\n-test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''\n+test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true work/\n \n git config core.bare true\n-test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false true ''\n+test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false ''\n \n git config --unset core.bare\n-test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''\n+test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true work/\n \n mv ../.git ../repo.git || exit 1\n export GIT_DIR=../repo.git\n-export GIT_CONFIG=\"$GIT_DIR\"/config\n+export GIT_CONFIG=\"$(pwd)\"/../repo.git/config\n \n git config core.bare false\n test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''\n \n git config core.bare true\n-test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false true ''\n+test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false false ''\n \n git config --unset core.bare\n-test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' true false true ''\n+test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' false false true ''\n \n test_done\n-- \n1.5.3.rc3.28.g1406\n"},{"id":"49031","messageId":"Pine.LNX.4.64.0707300027290.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"[UNWANTED PATCH] Die if core.bare = true and core.worktree is set","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-07-29T23:29:37Z","receivedAt":"2007-07-29T23:29:37Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tThis patch is just here to demonstrate how a core.bare = true / \n\tcore.worktree conflict could be caught.\n\n\tPersonally, I do not deem it worth the effort, as can be seen by \n\tthe lack of adjustments to t1501, which fails utterly with this \n\tpatch.\n\n environment.c |    3 +--\n setup.c       |    8 ++++++--\n 2 files changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/environment.c b/environment.c\nindex 2af12fd..4c5db32 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -82,8 +82,7 @@ const char *get_git_work_tree(void)\n \tstatic int initialized = 0;\n \tif (!initialized) {\n \t\twork_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);\n-\t\t/* core.bare = true overrides implicit and config work tree */\n-\t\tif (!work_tree && is_bare_repository_cfg < 1) {\n+\t\tif (!work_tree) {\n \t\t\twork_tree = git_work_tree_cfg;\n \t\t\t/* make_absolute_path also normalizes the path */\n \t\t\tif (work_tree && !is_absolute_path(work_tree))\ndiff --git a/setup.c b/setup.c\nindex 8237fe3..b0febed 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -342,13 +342,17 @@ int check_repository_format_version(const char *var, const char *value)\n \t\tshared_repository = git_config_perm(var, value);\n \telse if (strcmp(var, \"core.bare\") == 0) {\n \t\tis_bare_repository_cfg = git_config_bool(var, value);\n-\t\tif (is_bare_repository_cfg == 1)\n-\t\t\tinside_work_tree = -1;\n+\t\tif (is_bare_repository_cfg == 1 && inside_work_tree < 0)\n+\t\t\tdie (\"Contradicting config settings for \"\n+\t\t\t\t\"bare and worktree\");\n \t} else if (strcmp(var, \"core.worktree\") == 0) {\n \t\tif (git_work_tree_cfg)\n \t\t\tfree(git_work_tree_cfg);\n \t\tgit_work_tree_cfg = xstrdup(value);\n \t\tinside_work_tree = -1;\n+\t\tif (is_bare_repository_cfg == 1)\n+\t\t\tdie (\"Contradicting config settings for \"\n+\t\t\t\t\"bare and worktree\");\n \t}\n \treturn 0;\n }\n-- \n1.5.3.rc3.28.g1406\n"},{"id":"49256","messageId":"Pine.LNX.4.64.0708010058130.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0707300016470.14781@racer.site","subject":"Re: [PATCH 0/9] work-tree clean ups","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T00:28:30Z","receivedAt":"2007-08-01T00:28:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nso this is yet another revision of the work-tree clean ups (sorry to all \nthose who grow tired of it; I feel with you: I am tired of it, too).\n\nJunio rightfully pointed out that the tests do not succeed after each \nsingle step of the series.\n\nAlas, after thinking about it for quite some time, I do not think there is \nany way around squashing all the earlier steps 4,6--9 into one step.\n\nThere is not really much that can be done about step 6/9: if we are in a \nwork tree: that does not mean that we are _not_ in the git_dir.  (And no, \nthis does not break git-clean, as a work tree is a work tree is a work \ntree.  If the user was stupid enough to specify the same directory as \nGIT_DIR and GIT_WORK_TREE, then that is _her_ problem.  Git is a powerful \ntool, and you can harm yourself with it.  Tough.)\n\nPatch 7/9 is needed, because the old logic in git-init was wrong, and only \nhidden by the fact that the work-tree logic was implemented wrongly to \nbegin with.\n\nPatches 8 and 9/9 only updated the tests to ensure a sane logic, instead \nof an unsane one.  So they are needed, too.\n\nNote: if you are in a bare repository (a repository which either says \n\"core.bare = false\" in the config, or which is a direct ancestor \ndirectory, i.e. ../[...]/.. of the current working directory) there will \n_not_ be an automatic working directory assignment.  You will be operating \n_without_ any work tree, unless you specify one.\n\nI somehow feel that core.bare = true weighs more than core.worktree = \n/some/thing, and therefore I implemented it that way, but hey, if enough \npeople disagree, then I'll change it.\n\nMaybe I should add two comments?\n\nNamely that setup_git_directory_gently() does _not_ check the config if \nthe repository version is right, and where the working directory is, and \nif the repository is bare.  setup_git_directory() does...\n\nAnd that setup_git_directory_gently() _does_ try to be smart about \nget_git_work_tree(), is_inside_git_dir() and is_inside_work_tree() by \nassigning their return values, and only if core.bare or core.worktree (or \n--work-tree=<something> or GIT_WORK_TREE) are set, get_git_work_tree() and \nis_inside_work_tree() are reset to recalculate what is happening...  \n(actually, that is not completely true: if we _know_ that GIT_WORK_TREE is \nset, or --work-tree=<something> which is almost the same, we will defer \nthe calculation until one of the functions get_git_work_tree() and \nis_inside_work_tree() is called.)\n\nIMHO we should (probably after 1.5.3) change setup_git_directory_gently() \nto call check_repository_format() in every return path, so that we \nascertain that the current repository is recent enough.  Because that \nfunction now checks also if the repo is bare, and if it has a worktree \nset, in addition to ensuring a valid repository.\n\nIn hindsight, I should have separated the \"check .git/, then ./, and if no \ngit_dir was found, continue with the parent directory\" into a separate \npatch, but frankly, I am sick and tired of the work-tree series.  It was \nnot done right in the first place, and it used hard-to-understand code to \nhide the fact.\n\nCiao,\nDscho\n\nP.S.: After reading my patch to the tests, I have to disagree strongly \nwith my notion that _not_ cleaning up the tests to use some sane syntax \nwould make them clearer.  Nevertheless, I think I'll let them stand as an \nexample how _not_ to write tests.\n"},{"id":"49257","messageId":"Pine.LNX.4.64.0708010128430.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708010058130.14781@racer.site","subject":"[PATCH 1/4] Add is_absolute_path() and make_absolute_path()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T00:28:59Z","receivedAt":"2007-08-01T00:28:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThis patch adds convenience functions to work with absolute paths.\nThe function is_absolute_path() should help the efforts to integrate\nthe MinGW fork.\n\nNote that make_absolute_path() returns a pointer to a static buffer.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile             |    2 +-\n cache.h              |    5 ++++\n path.c               |   65 ++++++++++++++++++++++++++++++++++++++++++++++++++\n t/t0000-basic.sh     |   16 ++++++++++++\n test-absolute-path.c |   11 ++++++++\n 5 files changed, 98 insertions(+), 1 deletions(-)\n create mode 100644 test-absolute-path.c\n\ndiff --git a/Makefile b/Makefile\nindex 149df1b..41c4de0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -937,7 +937,7 @@ endif\n \n ### Testing rules\n \n-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X\n+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X\n \n all:: $(TEST_PROGRAMS)\n \ndiff --git a/cache.h b/cache.h\nindex 53801b8..98af530 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -358,6 +358,11 @@ int git_config_perm(const char *var, const char *value);\n int adjust_shared_perm(const char *path);\n int safe_create_leading_directories(char *path);\n char *enter_repo(char *path, int strict);\n+static inline int is_absolute_path(const char *path)\n+{\n+\treturn path[0] == '/';\n+}\n+const char *make_absolute_path(const char *path);\n \n /* Read and unpack a sha1 file into memory, write memory to a sha1 file */\n extern int sha1_object_info(const unsigned char *, unsigned long *);\ndiff --git a/path.c b/path.c\nindex dfff41f..27b4ac9 100644\n--- a/path.c\n+++ b/path.c\n@@ -288,3 +288,68 @@ int adjust_shared_perm(const char *path)\n \t\treturn -2;\n \treturn 0;\n }\n+\n+/* We allow \"recursive\" symbolic links. Only within reason, though. */\n+#define MAXDEPTH 5\n+\n+const char *make_absolute_path(const char *path)\n+{\n+\tstatic char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];\n+\tchar cwd[1024] = \"\";\n+\tint buf_index = 1, len;\n+\n+\tint depth = MAXDEPTH;\n+\tchar *last_elem = NULL;\n+\tstruct stat st;\n+\n+\tif (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)\n+\t\tdie (\"Too long path: %.*s\", 60, path);\n+\n+\twhile (depth--) {\n+\t\tif (stat(buf, &st) || !S_ISDIR(st.st_mode)) {\n+\t\t\tchar *last_slash = strrchr(buf, '/');\n+\t\t\tif (last_slash) {\n+\t\t\t\t*last_slash = '\\0';\n+\t\t\t\tlast_elem = xstrdup(last_slash + 1);\n+\t\t\t} else\n+\t\t\t\tlast_elem = xstrdup(buf);\n+\t\t}\n+\n+\t\tif (*buf) {\n+\t\t\tif (!*cwd && !getcwd(cwd, sizeof(cwd)))\n+\t\t\t\tdie (\"Could not get current working directory\");\n+\n+\t\t\tif (chdir(buf))\n+\t\t\t\tdie (\"Could not switch to '%s'\", buf);\n+\t\t}\n+\t\tif (getcwd(buf, PATH_MAX) < 0)\n+\t\t\tdie (\"Could not get current working directory\");\n+\n+\t\tif (last_elem) {\n+\t\t\tint len = strlen(buf);\n+\t\t\tif (len + strlen(last_elem) + 2 > PATH_MAX)\n+\t\t\t\tdie (\"Too long path name: '%s/%s'\",\n+\t\t\t\t\t\tbuf, last_elem);\n+\t\t\tbuf[len] = '/';\n+\t\t\tstrcpy(buf + len + 1, last_elem);\n+\t\t\tfree(last_elem);\n+\t\t\tlast_elem = NULL;\n+\t\t}\n+\n+\t\tif (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {\n+\t\t\tlen = readlink(buf, next_buf, PATH_MAX);\n+\t\t\tif (len < 0)\n+\t\t\t\tdie (\"Invalid symlink: %s\", buf);\n+\t\t\tnext_buf[len] = '\\0';\n+\t\t\tbuf = next_buf;\n+\t\t\tbuf_index = 1 - buf_index;\n+\t\t\tnext_buf = bufs[buf_index];\n+\t\t} else\n+\t\t\tbreak;\n+\t}\n+\n+\tif (*cwd && chdir(cwd))\n+\t\tdie (\"Could not change back to '%s'\", cwd);\n+\n+\treturn buf;\n+}\ndiff --git a/t/t0000-basic.sh b/t/t0000-basic.sh\nindex 4bba9c0..4e49d59 100755\n--- a/t/t0000-basic.sh\n+++ b/t/t0000-basic.sh\n@@ -281,4 +281,20 @@ test_expect_success 'update-index D/F conflict' '\n \ttest $numpath0 = 1\n '\n \n+test_expect_success 'absolute path works as expected' '\n+\tmkdir first &&\n+\tln -s ../.git first/.git &&\n+\tmkdir second &&\n+\tln -s ../first second/other &&\n+\tmkdir third &&\n+\tdir=\"$(cd .git; pwd -P)\" &&\n+\tdir2=third/../second/other/.git &&\n+\ttest \"$dir\" = \"$(test-absolute-path $dir2)\" &&\n+\tfile=\"$dir\"/index &&\n+\ttest \"$file\" = \"$(test-absolute-path $dir2/index)\" &&\n+\tln -s ../first/file .git/syml &&\n+\tsym=\"$(cd first; pwd -P)\"/file &&\n+\ttest \"$sym\" = \"$(test-absolute-path $dir2/syml)\"\n+'\n+\n test_done\ndiff --git a/test-absolute-path.c b/test-absolute-path.c\nnew file mode 100644\nindex 0000000..c959ea2\n--- /dev/null\n+++ b/test-absolute-path.c\n@@ -0,0 +1,11 @@\n+#include \"cache.h\"\n+\n+int main(int argc, char **argv)\n+{\n+\twhile (argc > 1) {\n+\t\tputs(make_absolute_path(argv[1]));\n+\t\targc--;\n+\t\targv++;\n+\t}\n+\treturn 0;\n+}\n-- \n1.5.3.rc3.94.g3024\n"},{"id":"49258","messageId":"Pine.LNX.4.64.0708010129090.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708010058130.14781@racer.site","subject":"[PATCH 2/4] Add functions get_relative_cwd() and is_inside_dir()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T00:29:17Z","receivedAt":"2007-08-01T00:29:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThe function get_relative_cwd() works just as getcwd(), only that it\ntakes an absolute path as additional parameter, returning the prefix\nof the current working directory relative to the given path.  If the\ncwd is no subdirectory of the given path, it returns NULL.\n\nis_inside_dir() is just a trivial wrapper over get_relative_cwd().\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n dir.c |   32 ++++++++++++++++++++++++++++++++\n dir.h |    3 +++\n 2 files changed, 35 insertions(+), 0 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex 8d8faf5..5ea269a 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -642,3 +642,35 @@ file_exists(const char *f)\n   struct stat sb;\n   return stat(f, &sb) == 0;\n }\n+\n+/*\n+ * get_relative_cwd() gets the prefix of the current working directory\n+ * relative to 'dir'.  If we are not inside 'dir', it returns NULL.\n+ * As a convenience, it also returns NULL if 'dir' is already NULL.\n+ */\n+char *get_relative_cwd(char *buffer, int size, const char *dir)\n+{\n+\tchar *cwd = buffer;\n+\n+\tif (!dir || !getcwd(buffer, size))\n+\t\treturn NULL;\n+\n+\tif (!is_absolute_path(dir))\n+\t\tdir = make_absolute_path(dir);\n+\n+\twhile (*dir && *dir == *cwd) {\n+\t\tdir++;\n+\t\tcwd++;\n+\t}\n+\tif (*dir)\n+\t\treturn NULL;\n+\tif (*cwd == '/')\n+\t\treturn cwd + 1;\n+\treturn cwd;\n+}\n+\n+int is_inside_dir(const char *dir)\n+{\n+\tchar buffer[PATH_MAX];\n+\treturn get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;\n+}\ndiff --git a/dir.h b/dir.h\nindex ec0e8ab..f55a87b 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -61,4 +61,7 @@ extern void add_exclude(const char *string, const char *base,\n extern int file_exists(const char *);\n extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len);\n \n+extern char *get_relative_cwd(char *buffer, int size, const char *dir);\n+extern int is_inside_dir(const char *dir);\n+\n #endif\n-- \n1.5.3.rc3.94.g3024\n\n#\n"},{"id":"49259","messageId":"Pine.LNX.4.64.0708010129310.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708010058130.14781@racer.site","subject":"[PATCH 3/4] Add set_git_dir() function","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T00:29:38Z","receivedAt":"2007-08-01T00:29:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nWith the function set_git_dir() you can reset the path that will\nbe used for git_path(), git_dir() and friends.\n\nThe responsibility to close files and throw away information from the\nold git_dir lies with the caller.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n cache.h       |    1 +\n environment.c |    8 ++++++++\n 2 files changed, 9 insertions(+), 0 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 98af530..e1f94cb 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -214,6 +214,7 @@ extern char *get_object_directory(void);\n extern char *get_refs_directory(void);\n extern char *get_index_file(void);\n extern char *get_graft_file(void);\n+extern int set_git_dir(const char *path);\n \n #define ALTERNATE_DB_ENVIRONMENT \"GIT_ALTERNATE_OBJECT_DIRECTORIES\"\n \ndiff --git a/environment.c b/environment.c\nindex f83fb9e..a571fae 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -107,3 +107,11 @@ char *get_graft_file(void)\n \t\tsetup_git_env();\n \treturn git_graft_file;\n }\n+\n+int set_git_dir(const char *path)\n+{\n+\tif (setenv(GIT_DIR_ENVIRONMENT, path, 1))\n+\t\treturn error(\"Could not set GIT_DIR to '%s'\", path);\n+\tsetup_git_env();\n+\treturn 0;\n+}\n-- \n1.5.3.rc3.94.g3024\n"},{"id":"49260","messageId":"Pine.LNX.4.64.0708010129530.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708010058130.14781@racer.site","subject":"[PATCH 4/4] Clean up work-tree handling","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T00:30:14Z","receivedAt":"2007-08-01T00:30:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThe old version of work-tree support was an unholy mess, barely readable,\nand not to the point.\n\nFor example, why do you have to provide a worktree, when it is not used?\nAs in \"git status\".  Now it works.\n\nAnother riddle was: if you can have work trees inside the git dir, why\nare some programs complaining that they need a work tree?\n\nIOW it is allowed to call\n\n\t$ git --git-dir=../ --work-tree=. bla\n\nwhen you really want to.  In this case, you are both in the git directory\nand in the working tree.  So, programs have to actually test for the right\nthing, namely if they are inside a working tree, and not if they are\ninside a git directory.\n\nAlso, GIT_DIR=../.git should behave the same as if no GIT_DIR was\nspecified, unless there is a repository in the current working directory.\nIt does now.\n\nThe logic to determine if a repository is bare, or has a work tree\n(tertium non datur), is this:\n\n--work-tree=bla overrides GIT_WORK_TREE, which overrides core.bare = true,\nwhich overrides core.worktree, which overrides GIT_DIR/.. when GIT_DIR\nends in /.git, which overrides the directory in which .git/ was found.\n\nIn related news, a long standing bug was fixed: when in .git/bla/x.git/,\nwhich is a bare repository, git formerly assumed ../.. to be the\nappropriate git dir.  This problem was reported by Shawn Pearce to have\ncaused much pain, where a colleague mistakenly ran \"git init\" in \"/\" a\nlong time ago, and bare repositories just would not work.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin-init-db.c    |   48 ++-------\n builtin-ls-files.c   |    8 +-\n builtin-rev-parse.c  |    7 ++\n cache.h              |    2 +\n environment.c        |   35 +++++--\n git-sh-setup.sh      |    3 +-\n git.c                |   11 ++-\n setup.c              |  279 +++++++++++++++++++++++---------------------------\n t/t1500-rev-parse.sh |   20 ++--\n t/t1501-worktree.sh  |   24 ++++-\n 10 files changed, 216 insertions(+), 221 deletions(-)\n\ndiff --git a/builtin-init-db.c b/builtin-init-db.c\nindex 66ddaeb..0d9b1e0 100644\n--- a/builtin-init-db.c\n+++ b/builtin-init-db.c\n@@ -174,36 +174,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di\n \tclosedir(dir);\n }\n \n-/*\n- * Get the full path to the working tree specified in $GIT_WORK_TREE\n- * or NULL if no working tree is specified.\n- */\n-static const char *get_work_tree(void)\n-{\n-\tconst char *git_work_tree;\n-\tchar cwd[PATH_MAX];\n-\tstatic char worktree[PATH_MAX];\n-\n-\tgit_work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);\n-\tif (!git_work_tree)\n-\t\treturn NULL;\n-\tif (!getcwd(cwd, sizeof(cwd)))\n-\t\tdie(\"Unable to read current working directory\");\n-\tif (chdir(git_work_tree))\n-\t\tdie(\"Cannot change directory to specified working tree '%s'\",\n-\t\t\tgit_work_tree);\n-\tif (git_work_tree[0] != '/') {\n-\t\tif (!getcwd(worktree, sizeof(worktree)))\n-\t\t\tdie(\"Unable to read current working directory\");\n-\t\tgit_work_tree = worktree;\n-\t}\n-\tif (chdir(cwd))\n-\t\tdie(\"Cannot come back to cwd\");\n-\treturn git_work_tree;\n-}\n-\n-static int create_default_files(const char *git_dir, const char *git_work_tree,\n-\tconst char *template_path)\n+static int create_default_files(const char *git_dir, const char *template_path)\n {\n \tunsigned len = strlen(git_dir);\n \tstatic char path[PATH_MAX];\n@@ -282,16 +253,16 @@ static int create_default_files(const char *git_dir, const char *git_work_tree,\n \t}\n \tgit_config_set(\"core.filemode\", filemode ? \"true\" : \"false\");\n \n-\tif (is_bare_repository() && !git_work_tree) {\n+\tif (is_bare_repository())\n \t\tgit_config_set(\"core.bare\", \"true\");\n-\t}\n \telse {\n+\t\tconst char *work_tree = get_git_work_tree();\n \t\tgit_config_set(\"core.bare\", \"false\");\n \t\t/* allow template config file to override the default */\n \t\tif (log_all_ref_updates == -1)\n \t\t    git_config_set(\"core.logallrefupdates\", \"true\");\n-\t\tif (git_work_tree)\n-\t\t\tgit_config_set(\"core.worktree\", git_work_tree);\n+\t\tif (work_tree != git_work_tree_cfg)\n+\t\t\tgit_config_set(\"core.worktree\", work_tree);\n \t}\n \treturn reinit;\n }\n@@ -308,7 +279,6 @@ static const char init_db_usage[] =\n int cmd_init_db(int argc, const char **argv, const char *prefix)\n {\n \tconst char *git_dir;\n-\tconst char *git_work_tree;\n \tconst char *sha1_dir;\n \tconst char *template_dir = NULL;\n \tchar *path;\n@@ -329,7 +299,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\t\tusage(init_db_usage);\n \t}\n \n-\tgit_work_tree = get_work_tree();\n+\tgit_work_tree_cfg = xcalloc(PATH_MAX, 1);\n+\tif (!getcwd(git_work_tree_cfg, PATH_MAX))\n+\t\tdie (\"Cannot access current working directory.\");\n+\tif (access(get_git_work_tree(), X_OK))\n+\t\tdie (\"Cannot access work tree '%s'\", get_git_work_tree());\n \n \t/*\n \t * Set up the default .git directory contents\n@@ -346,7 +320,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t */\n \tcheck_repository_format();\n \n-\treinit = create_default_files(git_dir, git_work_tree, template_dir);\n+\treinit = create_default_files(git_dir, template_dir);\n \n \t/*\n \t * And set up the object store.\ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex 61577ea..d36181a 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -469,9 +469,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\tbreak;\n \t}\n \n-\tif (require_work_tree &&\n-\t\t\t(!is_inside_work_tree() || is_inside_git_dir()))\n-\t\tdie(\"This operation must be run in a work tree\");\n+\tif (require_work_tree && !is_inside_work_tree()) {\n+\t\tconst char *work_tree = get_git_work_tree();\n+\t\tif (!work_tree || chdir(work_tree))\n+\t\t\tdie(\"This operation must be run in a work tree\");\n+\t}\n \n \tpathspec = get_pathspec(prefix, argv + i);\n \ndiff --git a/builtin-rev-parse.c b/builtin-rev-parse.c\nindex 497903a..15bdb72 100644\n--- a/builtin-rev-parse.c\n+++ b/builtin-rev-parse.c\n@@ -320,6 +320,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--show-cdup\")) {\n+\t\t\t\tif (!is_inside_work_tree()) {\n+\t\t\t\t\tconst char *work_tree =\n+\t\t\t\t\t\tget_git_work_tree();\n+\t\t\t\t\tif (work_tree)\n+\t\t\t\t\t\tprintf(\"%s\\n\", work_tree);\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n \t\t\t\tconst char *pfx = prefix;\n \t\t\t\twhile (pfx) {\n \t\t\t\t\tpfx = strchr(pfx, '/');\ndiff --git a/cache.h b/cache.h\nindex e1f94cb..e97af18 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -208,6 +208,7 @@ enum object_type {\n extern int is_bare_repository_cfg;\n extern int is_bare_repository(void);\n extern int is_inside_git_dir(void);\n+extern char *git_work_tree_cfg;\n extern int is_inside_work_tree(void);\n extern const char *get_git_dir(void);\n extern char *get_object_directory(void);\n@@ -215,6 +216,7 @@ extern char *get_refs_directory(void);\n 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 \n #define ALTERNATE_DB_ENVIRONMENT \"GIT_ALTERNATE_OBJECT_DIRECTORIES\"\n \ndiff --git a/environment.c b/environment.c\nindex a571fae..2af12fd 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -35,6 +35,10 @@ int pager_in_use;\n int pager_use_color = 1;\n int auto_crlf = 0;\t/* 1: both ways, -1: only when adding git objects */\n \n+/* This is set by setup_git_dir_gently() and/or git_default_config() */\n+char *git_work_tree_cfg;\n+static const char *work_tree;\n+\n static const char *git_dir;\n static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;\n \n@@ -62,15 +66,8 @@ static void setup_git_env(void)\n \n int is_bare_repository(void)\n {\n-\tconst char *dir, *s;\n-\tif (0 <= is_bare_repository_cfg)\n-\t\treturn is_bare_repository_cfg;\n-\n-\tdir = get_git_dir();\n-\tif (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))\n-\t\treturn 0;\n-\ts = strrchr(dir, '/');\n-\treturn !s || strcmp(s + 1, DEFAULT_GIT_DIR_ENVIRONMENT);\n+\t/* if core.bare is not 'false', let's see if there is a work tree */\n+\treturn is_bare_repository_cfg && !get_git_work_tree();\n }\n \n const char *get_git_dir(void)\n@@ -80,6 +77,26 @@ const char *get_git_dir(void)\n \treturn git_dir;\n }\n \n+const char *get_git_work_tree(void)\n+{\n+\tstatic int initialized = 0;\n+\tif (!initialized) {\n+\t\twork_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);\n+\t\t/* core.bare = true overrides implicit and config work tree */\n+\t\tif (!work_tree && is_bare_repository_cfg < 1) {\n+\t\t\twork_tree = git_work_tree_cfg;\n+\t\t\t/* make_absolute_path also normalizes the path */\n+\t\t\tif (work_tree && !is_absolute_path(work_tree))\n+\t\t\t\twork_tree = xstrdup(make_absolute_path(git_path(work_tree)));\n+\t\t} else if (work_tree)\n+\t\t\twork_tree = xstrdup(make_absolute_path(work_tree));\n+\t\tinitialized = 1;\n+\t\tif (work_tree)\n+\t\t\tis_bare_repository_cfg = 0;\n+\t}\n+\treturn work_tree;\n+}\n+\n char *get_object_directory(void)\n {\n \tif (!git_object_dir)\ndiff --git a/git-sh-setup.sh b/git-sh-setup.sh\nindex c51985e..7bef43f 100755\n--- a/git-sh-setup.sh\n+++ b/git-sh-setup.sh\n@@ -59,8 +59,7 @@ cd_to_toplevel () {\n }\n \n require_work_tree () {\n-\ttest $(git rev-parse --is-inside-work-tree) = true &&\n-\ttest $(git rev-parse --is-inside-git-dir) = false ||\n+\ttest $(git rev-parse --is-inside-work-tree) = true ||\n \tdie \"fatal: $0 cannot be used without a working tree.\"\n }\n \ndiff --git a/git.c b/git.c\nindex a647f9c..2433355 100644\n--- a/git.c\n+++ b/git.c\n@@ -272,9 +272,14 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)\n \t\tprefix = setup_git_directory();\n \tif (p->option & USE_PAGER)\n \t\tsetup_pager();\n-\tif ((p->option & NEED_WORK_TREE) &&\n-\t    (!is_inside_work_tree() || is_inside_git_dir()))\n-\t\tdie(\"%s must be run in a work tree\", p->cmd);\n+\tif (p->option & NEED_WORK_TREE) {\n+\t\tconst char *work_tree = get_git_work_tree();\n+\t\tconst char *git_dir = get_git_dir();\n+\t\tif (!is_absolute_path(git_dir))\n+\t\t\tset_git_dir(make_absolute_path(git_dir));\n+\t\tif (!work_tree || chdir(work_tree))\n+\t\t\tdie(\"%s must be run in a work tree\", p->cmd);\n+\t}\n \ttrace_argv_printf(argv, argc, \"trace: built-in: git\");\n \n \tstatus = p->fn(argc, argv, prefix);\ndiff --git a/setup.c b/setup.c\nindex b54d65f..8237fe3 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1,4 +1,8 @@\n #include \"cache.h\"\n+#include \"dir.h\"\n+\n+static int inside_git_dir = -1;\n+static int inside_work_tree = -1;\n \n const char *prefix_path(const char *prefix, int len, const char *path)\n {\n@@ -170,100 +174,89 @@ static int is_git_directory(const char *suspect)\n \treturn 1;\n }\n \n-static int inside_git_dir = -1;\n-\n int is_inside_git_dir(void)\n {\n-\tif (inside_git_dir >= 0)\n-\t\treturn inside_git_dir;\n-\tdie(\"BUG: is_inside_git_dir called before setup_git_directory\");\n+\tif (inside_git_dir < 0)\n+\t\tinside_git_dir = is_inside_dir(get_git_dir());\n+\treturn inside_git_dir;\n }\n \n-static int inside_work_tree = -1;\n-\n int is_inside_work_tree(void)\n {\n-\tif (inside_git_dir >= 0)\n-\t\treturn inside_work_tree;\n-\tdie(\"BUG: is_inside_work_tree called before setup_git_directory\");\n+\tif (inside_work_tree < 0)\n+\t\tinside_work_tree = is_inside_dir(get_git_work_tree());\n+\treturn inside_work_tree;\n }\n \n-static char *gitworktree_config;\n-\n-static int git_setup_config(const char *var, const char *value)\n+/*\n+ * If no worktree was given, and we are outside of a default work tree,\n+ * now is the time to set it.\n+ *\n+ * In other words, if the user calls git with something like\n+ *\n+ *\tgit --git-dir=/some/where/else/.git bla\n+ *\n+ * default to /some/where/else as working directory; if the specified\n+ * git-dir does not end in \"/.git\", the cwd is used as working directory.\n+ */\n+const char* set_work_tree(const char *dir)\n {\n-\tif (!strcmp(var, \"core.worktree\")) {\n-\t\tif (gitworktree_config)\n-\t\t\tstrlcpy(gitworktree_config, value, PATH_MAX);\n-\t\treturn 0;\n+\tchar dir_buffer[PATH_MAX];\n+\tstatic char buffer[PATH_MAX + 1], *rel = NULL;\n+\tint len, postfix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1;\n+\n+\t/* strip the variable 'dir' of the postfix \"/.git\" if it has it */\n+\tlen = strlen(dir);\n+\tif (len > postfix_len && !strcmp(dir + len - postfix_len,\n+\t\t\t\t\"/\" DEFAULT_GIT_DIR_ENVIRONMENT)) {\n+\t\t\tstrncpy(dir_buffer, dir, len - postfix_len);\n+\n+\t\t/* are we inside the default work tree? */\n+\t\trel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer);\n+\t}\n+\t/* if rel is set, the cwd is _not_ the current working tree */\n+\tif (rel && *rel) {\n+\t\tif (!is_absolute_path(dir))\n+\t\t\tset_git_dir(make_absolute_path(dir));\n+\t\tdir = dir_buffer;\n+\t\tchdir(dir);\n+\t\tstrcat(rel, \"/\");\n+\t\tinside_git_dir = 0;\n+\t} else {\n+\t\trel = NULL;\n+\t\tdir = getcwd(buffer, sizeof(buffer));\n \t}\n-\treturn git_default_config(var, value);\n+\tgit_work_tree_cfg = xstrdup(dir);\n+\tinside_work_tree = 1;\n+\n+\treturn rel;\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 const char *setup_git_directory_gently(int *nongit_ok)\n {\n+\tconst char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);\n \tstatic char cwd[PATH_MAX+1];\n-\tchar worktree[PATH_MAX+1], gitdir[PATH_MAX+1];\n-\tconst char *gitdirenv, *gitworktree;\n-\tint wt_rel_gitdir = 0;\n+\tconst char *gitdirenv;\n+\tint len, offset;\n \n+\t/*\n+\t * If GIT_DIR is set explicitly, we're not going\n+\t * to do any discovery, but we still do repository\n+\t * validation.\n+\t */\n \tgitdirenv = getenv(GIT_DIR_ENVIRONMENT);\n-\tif (!gitdirenv) {\n-\t\tint len, offset;\n-\n-\t\tif (!getcwd(cwd, sizeof(cwd)-1))\n-\t\t\tdie(\"Unable to read current working directory\");\n-\n-\t\toffset = len = strlen(cwd);\n-\t\tfor (;;) {\n-\t\t\tif (is_git_directory(\".git\"))\n-\t\t\t\tbreak;\n-\t\t\tif (offset == 0) {\n-\t\t\t\toffset = -1;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tchdir(\"..\");\n-\t\t\twhile (cwd[--offset] != '/')\n-\t\t\t\t; /* do nothing */\n-\t\t}\n-\n-\t\tif (offset >= 0) {\n-\t\t\tinside_work_tree = 1;\n-\t\t\tgit_config(git_default_config);\n-\t\t\tif (offset == len) {\n-\t\t\t\tinside_git_dir = 0;\n-\t\t\t\treturn NULL;\n-\t\t\t}\n-\n-\t\t\tcwd[len++] = '/';\n-\t\t\tcwd[len] = '\\0';\n-\t\t\tinside_git_dir = !prefixcmp(cwd + offset + 1, \".git/\");\n-\t\t\treturn cwd + offset + 1;\n-\t\t}\n-\n-\t\tif (chdir(cwd))\n-\t\t\tdie(\"Cannot come back to cwd\");\n-\t\tif (!is_git_directory(\".\")) {\n-\t\t\tif (nongit_ok) {\n-\t\t\t\t*nongit_ok = 1;\n-\t\t\t\treturn NULL;\n-\t\t\t}\n-\t\t\tdie(\"Not a git repository\");\n-\t\t}\n-\t\tsetenv(GIT_DIR_ENVIRONMENT, cwd, 1);\n-\t\tgitdirenv = getenv(GIT_DIR_ENVIRONMENT);\n-\t\tif (!gitdirenv)\n-\t\t\tdie(\"getenv after setenv failed\");\n-\t}\n-\n-\tif (PATH_MAX - 40 < strlen(gitdirenv)) {\n-\t\tif (nongit_ok) {\n-\t\t\t*nongit_ok = 1;\n+\tif (gitdirenv) {\n+\t\tif (PATH_MAX - 40 < strlen(gitdirenv))\n+\t\t\tdie(\"'$%s' too big\", GIT_DIR_ENVIRONMENT);\n+\t\tif (is_git_directory(gitdirenv)) {\n+\t\t\tif (!work_tree_env)\n+\t\t\t\treturn set_work_tree(gitdirenv);\n \t\t\treturn NULL;\n \t\t}\n-\t\tdie(\"$%s too big\", GIT_DIR_ENVIRONMENT);\n-\t}\n-\tif (!is_git_directory(gitdirenv)) {\n \t\tif (nongit_ok) {\n \t\t\t*nongit_ok = 1;\n \t\t\treturn NULL;\n@@ -273,92 +266,53 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \n \tif (!getcwd(cwd, sizeof(cwd)-1))\n \t\tdie(\"Unable to read current working directory\");\n-\tif (chdir(gitdirenv)) {\n-\t\tif (nongit_ok) {\n-\t\t\t*nongit_ok = 1;\n-\t\t\treturn NULL;\n-\t\t}\n-\t\tdie(\"Cannot change directory to $%s '%s'\",\n-\t\t\tGIT_DIR_ENVIRONMENT, gitdirenv);\n-\t}\n-\tif (!getcwd(gitdir, sizeof(gitdir)-1))\n-\t\tdie(\"Unable to read current working directory\");\n-\tif (chdir(cwd))\n-\t\tdie(\"Cannot come back to cwd\");\n \n \t/*\n-\t * In case there is a work tree we may change the directory,\n-\t * therefore make GIT_DIR an absolute path.\n+\t * Test in the following order (relative to the cwd):\n+\t * - .git/\n+\t * - ./ (bare)\n+\t * - ../.git/\n+\t * - ../ (bare)\n+\t * - ../../.git/\n+\t *   etc.\n \t */\n-\tif (gitdirenv[0] != '/') {\n-\t\tsetenv(GIT_DIR_ENVIRONMENT, gitdir, 1);\n-\t\tgitdirenv = getenv(GIT_DIR_ENVIRONMENT);\n-\t\tif (!gitdirenv)\n-\t\t\tdie(\"getenv after setenv failed\");\n-\t\tif (PATH_MAX - 40 < strlen(gitdirenv)) {\n-\t\t\tif (nongit_ok) {\n-\t\t\t\t*nongit_ok = 1;\n-\t\t\t\treturn NULL;\n-\t\t\t}\n-\t\t\tdie(\"$%s too big after expansion to absolute path\",\n-\t\t\t\tGIT_DIR_ENVIRONMENT);\n-\t\t}\n-\t}\n-\n-\tstrcat(cwd, \"/\");\n-\tstrcat(gitdir, \"/\");\n-\tinside_git_dir = !prefixcmp(cwd, gitdir);\n-\n-\tgitworktree = getenv(GIT_WORK_TREE_ENVIRONMENT);\n-\tif (!gitworktree) {\n-\t\tgitworktree_config = worktree;\n-\t\tworktree[0] = '\\0';\n-\t}\n-\tgit_config(git_setup_config);\n-\tif (!gitworktree) {\n-\t\tgitworktree_config = NULL;\n-\t\tif (worktree[0])\n-\t\t\tgitworktree = worktree;\n-\t\tif (gitworktree && gitworktree[0] != '/')\n-\t\t\twt_rel_gitdir = 1;\n-\t}\n-\n-\tif (wt_rel_gitdir && chdir(gitdirenv))\n-\t\tdie(\"Cannot change directory to $%s '%s'\",\n-\t\t\tGIT_DIR_ENVIRONMENT, gitdirenv);\n-\tif (gitworktree && chdir(gitworktree)) {\n-\t\tif (nongit_ok) {\n-\t\t\tif (wt_rel_gitdir && chdir(cwd))\n-\t\t\t\tdie(\"Cannot come back to cwd\");\n-\t\t\t*nongit_ok = 1;\n+\toffset = len = strlen(cwd);\n+\tfor (;;) {\n+\t\tif (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))\n+\t\t\tbreak;\n+\t\tif (is_git_directory(\".\")) {\n+\t\t\tinside_git_dir = 1;\n+\t\t\tif (!work_tree_env)\n+\t\t\t\tinside_work_tree = 0;\n+\t\t\tsetenv(GIT_DIR_ENVIRONMENT, \".\", 1);\n \t\t\treturn NULL;\n \t\t}\n-\t\tif (wt_rel_gitdir)\n-\t\t\tdie(\"Cannot change directory to working tree '%s'\"\n-\t\t\t\t\" from $%s\", gitworktree, GIT_DIR_ENVIRONMENT);\n-\t\telse\n-\t\t\tdie(\"Cannot change directory to working tree '%s'\",\n-\t\t\t\tgitworktree);\n-\t}\n-\tif (!getcwd(worktree, sizeof(worktree)-1))\n-\t\tdie(\"Unable to read current working directory\");\n-\tstrcat(worktree, \"/\");\n-\tinside_work_tree = !prefixcmp(cwd, worktree);\n-\n-\tif (gitworktree && inside_work_tree && !prefixcmp(worktree, gitdir) &&\n-\t    strcmp(worktree, gitdir)) {\n-\t\tinside_git_dir = 0;\n+\t\tchdir(\"..\");\n+\t\tdo {\n+\t\t\tif (!offset) {\n+\t\t\t\tif (nongit_ok) {\n+\t\t\t\t\tif (chdir(cwd))\n+\t\t\t\t\t\tdie(\"Cannot come back to cwd\");\n+\t\t\t\t\t*nongit_ok = 1;\n+\t\t\t\t\treturn NULL;\n+\t\t\t\t}\n+\t\t\t\tdie(\"Not a git repository\");\n+\t\t\t}\n+\t\t} while (cwd[--offset] != '/');\n \t}\n \n-\tif (!inside_work_tree) {\n-\t\tif (chdir(cwd))\n-\t\t\tdie(\"Cannot come back to cwd\");\n+\tinside_git_dir = 0;\n+\tif (!work_tree_env)\n+\t\tinside_work_tree = 1;\n+\tgit_work_tree_cfg = xstrndup(cwd, offset);\n+\tif (offset == len)\n \t\treturn NULL;\n-\t}\n \n-\tif (!strcmp(cwd, worktree))\n-\t\treturn NULL;\n-\treturn cwd+strlen(worktree);\n+\t/* Make \"offset\" point to past the '/', and add a '/' at the end */\n+\toffset++;\n+\tcwd[len++] = '/';\n+\tcwd[len] = 0;\n+\treturn cwd + offset;\n }\n \n int git_config_perm(const char *var, const char *value)\n@@ -386,6 +340,16 @@ int check_repository_format_version(const char *var, const char *value)\n \t\trepository_format_version = git_config_int(var, value);\n \telse if (strcmp(var, \"core.sharedrepository\") == 0)\n \t\tshared_repository = git_config_perm(var, value);\n+\telse if (strcmp(var, \"core.bare\") == 0) {\n+\t\tis_bare_repository_cfg = git_config_bool(var, value);\n+\t\tif (is_bare_repository_cfg == 1)\n+\t\t\tinside_work_tree = -1;\n+\t} else if (strcmp(var, \"core.worktree\") == 0) {\n+\t\tif (git_work_tree_cfg)\n+\t\t\tfree(git_work_tree_cfg);\n+\t\tgit_work_tree_cfg = xstrdup(value);\n+\t\tinside_work_tree = -1;\n+\t}\n \treturn 0;\n }\n \n@@ -402,5 +366,16 @@ const char *setup_git_directory(void)\n {\n \tconst char *retval = setup_git_directory_gently(NULL);\n \tcheck_repository_format();\n+\n+\t/* If the work tree is not the default one, recompute prefix */\n+\tif (inside_work_tree < 0) {\n+\t\tstatic char buffer[PATH_MAX + 1];\n+\t\tif (retval && chdir(retval))\n+\t\t\tdie (\"Could not jump back into original cwd\");\n+\t\tchar *rel = get_relative_cwd(buffer, PATH_MAX,\n+\t\t\t\tget_git_work_tree());\n+\t\treturn rel && *rel ? strcat(rel, \"/\") : NULL;\n+\t}\n+\n \treturn retval;\n }\ndiff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh\nindex ec49966..bea40cb 100755\n--- a/t/t1500-rev-parse.sh\n+++ b/t/t1500-rev-parse.sh\n@@ -31,9 +31,9 @@ test_rev_parse() {\n test_rev_parse toplevel false false true ''\n \n cd .git || exit 1\n-test_rev_parse .git/ false true true .git/\n+test_rev_parse .git/ true true false ''\n cd objects || exit 1\n-test_rev_parse .git/objects/ false true true .git/objects/\n+test_rev_parse .git/objects/ true true false ''\n cd ../.. || exit 1\n \n mkdir -p sub/dir || exit 1\n@@ -42,7 +42,7 @@ test_rev_parse subdirectory false false true sub/dir/\n cd ../.. || exit 1\n \n git config core.bare true\n-test_rev_parse 'core.bare = true' true false true\n+test_rev_parse 'core.bare = true' true false false\n \n git config --unset core.bare\n test_rev_parse 'core.bare undefined' false false true\n@@ -50,28 +50,28 @@ test_rev_parse 'core.bare undefined' false false true\n mkdir work || exit 1\n cd work || exit 1\n export GIT_DIR=../.git\n-export GIT_CONFIG=\"$GIT_DIR\"/config\n+export GIT_CONFIG=\"$(pwd)\"/../.git/config\n \n git config core.bare false\n-test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''\n+test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true work/\n \n git config core.bare true\n-test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false true ''\n+test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false ''\n \n git config --unset core.bare\n-test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''\n+test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true work/\n \n mv ../.git ../repo.git || exit 1\n export GIT_DIR=../repo.git\n-export GIT_CONFIG=\"$GIT_DIR\"/config\n+export GIT_CONFIG=\"$(pwd)\"/../repo.git/config\n \n git config core.bare false\n test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''\n \n git config core.bare true\n-test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false true ''\n+test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false false ''\n \n git config --unset core.bare\n-test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' true false true ''\n+test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' false false true ''\n \n test_done\ndiff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh\nindex aadeeab..7322161 100755\n--- a/t/t1501-worktree.sh\n+++ b/t/t1501-worktree.sh\n@@ -33,17 +33,17 @@ mv .git repo.git || exit 1\n \n say \"core.worktree = relative path\"\n export GIT_DIR=repo.git\n-export GIT_CONFIG=$GIT_DIR/config\n+export GIT_CONFIG=\"$(pwd)\"/$GIT_DIR/config\n unset GIT_WORK_TREE\n git config core.worktree ../work\n test_rev_parse 'outside'      false false false\n cd work || exit 1\n export GIT_DIR=../repo.git\n-export GIT_CONFIG=$GIT_DIR/config\n+export GIT_CONFIG=\"$(pwd)\"/$GIT_DIR/config\n test_rev_parse 'inside'       false false true ''\n cd sub/dir || exit 1\n export GIT_DIR=../../../repo.git\n-export GIT_CONFIG=$GIT_DIR/config\n+export GIT_CONFIG=\"$(pwd)\"/$GIT_DIR/config\n test_rev_parse 'subdirectory' false false true sub/dir/\n cd ../../.. || exit 1\n \n@@ -84,9 +84,23 @@ test_rev_parse 'in repo.git'              false true  false\n cd objects || exit 1\n test_rev_parse 'in repo.git/objects'      false true  false\n cd ../work || exit 1\n-test_rev_parse 'in repo.git/work'         false false true ''\n+test_rev_parse 'in repo.git/work'         false true true ''\n cd sub/dir || exit 1\n-test_rev_parse 'in repo.git/sub/dir' false false true sub/dir/\n+test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/\n cd ../../../.. || exit 1\n \n+test_expect_success 'repo finds its work tree' '\n+\t(cd repo.git &&\n+\t : > work/sub/dir/untracked &&\n+\t test sub/dir/untracked = \"$(git ls-files --others)\")\n+'\n+\n+test_expect_success 'repo finds its work tree from work tree, too' '\n+\t(cd repo.git/work/sub/dir &&\n+\t : > tracked &&\n+\t git --git-dir=../../.. add tracked &&\n+\t cd ../../.. &&\n+\t test sub/dir/tracked = \"$(git ls-files)\")\n+'\n+\n test_done\n-- \n1.5.3.rc3.94.g3024\n"},{"id":"49262","messageId":"7vbqdsdqfd.fsf@assigned-by-dhcp.cox.net","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708010058130.14781@racer.site","subject":"Re: [PATCH 0/9] work-tree clean ups","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-08-01T00:55:18Z","receivedAt":"2007-08-01T00:55:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> There is not really much that can be done about step 6/9: if we are in a \n> work tree: that does not mean that we are _not_ in the git_dir.  (And no, \n> this does not break git-clean, as a work tree is a work tree is a work \n> tree.  If the user was stupid enough to specify the same directory as \n> GIT_DIR and GIT_WORK_TREE, then that is _her_ problem.  Git is a powerful \n> tool, and you can harm yourself with it.  Tough.)\n\nI think we might have a slight misunderstanding.  The \"clean\"\nissue that was raised in an ancient thread was this sequence:\n\n\t$ git init\n        $ cd .git\n        $ git clean\n\nIt did not involve GIT_DIR (nor GIT_WORK_TREE as it was not even\nthere).  The point was that not all subdirectories of the\ntoplevel (i.e. the directory on the filesystem that corresponds\nto the root level of your index entries and trees contained in\nyour commits) were safe to answer yes when asked \"are we safe to\nperform this worktree oriented command here\".  Because no trees\nnor index would have .git/ subdirectory tracked, \"git clean\"\nwill happily remove everything under .git/ (which is $cwd in the\nabove sequence).\n\nI personally feel that the above sequence is a pilot error and\nnot worth worrying about, but as people wanted to have that\nextra safety, and as we added that (arguably stupid) safety way\nbefore the WORK_TREE stuff, we should mention it if we are\nchanging the behaviour and lifting it with this patch series.\n\n> Note: if you are in a bare repository (a repository which either says \n> \"core.bare = false\" in the config, or which is a direct ancestor \n> directory, i.e. ../[...]/.. of the current working directory) there will \n> _not_ be an automatic working directory assignment.  You will be operating \n> _without_ any work tree, unless you specify one.\n\nSorry, I cannot interpret the condition part of the sentence,\nnor \"There will _not_ be an automatic assignment\" part.\n\nBy the latter, do you mean to say your $cwd is assumed to be the\ntop of the working tree unless GIT_WORK_TREE or core.worktree,\nif you are in a bare repository?  Or it is assumed that you do\nnot have a worktree and worktree oriented operations that\nrequire a worktree such as \"git diff-files\" and \"git status\"\nwill fail?\n\n> I somehow feel that core.bare = true weighs more than core.worktree = \n> /some/thing, and therefore I implemented it that way, but hey, if enough \n> people disagree, then I'll change it.\n\nPersonally, I think\n\n [core]\n     bare = true\n     worktree = /some/where\n\nis a configuration error, but probably I am missing a useful use\ncase for such a configuration?\n\n> IMHO we should (probably after 1.5.3) change setup_git_directory_gently() \n> to call check_repository_format() in every return path, so that we \n> ascertain that the current repository is recent enough.  Because that \n> function now checks also if the repo is bare, and if it has a worktree \n> set, in addition to ensuring a valid repository.\n\nAgreed; gently() is there primarily because some commands do not\nmind not having a git repository at all and if we do have a\nrepository to work against we probably should do the same checks\nas setup_git_directory() would.\n"},{"id":"49265","messageId":"Pine.LNX.4.64.0708010211071.14781@racer.site","threadId":"9300","inReplyTo":"7vbqdsdqfd.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 0/9] work-tree clean ups","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T01:13:59Z","receivedAt":"2007-08-01T01:13:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 31 Jul 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > There is not really much that can be done about step 6/9: if we are in a \n> > work tree: that does not mean that we are _not_ in the git_dir.  (And no, \n> > this does not break git-clean, as a work tree is a work tree is a work \n> > tree.  If the user was stupid enough to specify the same directory as \n> > GIT_DIR and GIT_WORK_TREE, then that is _her_ problem.  Git is a powerful \n> > tool, and you can harm yourself with it.  Tough.)\n> \n> I think we might have a slight misunderstanding.  The \"clean\"\n> issue that was raised in an ancient thread was this sequence:\n> \n> \t$ git init\n>         $ cd .git\n>         $ git clean\n> \n> It did not involve GIT_DIR (nor GIT_WORK_TREE as it was not even\n> there).\n\nI very much _did_ mean that case.  When \"git clean\" is run in \".git/\", it \nshould not say that it is in the working tree.  But I guess that my patch \nseries is not really looking out for that;  I'll make that an add-on \npatch.  (But that _will_ have to wait until tomorrow afternoon.)\n\nCiao,\nDscho\n"},{"id":"49277","messageId":"7vy7gvdgtn.fsf@assigned-by-dhcp.cox.net","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708010129090.14781@racer.site","subject":"Re: [PATCH 2/4] Add functions get_relative_cwd() and is_inside_dir()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-08-01T04:22:44Z","receivedAt":"2007-08-01T04:22:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> The function get_relative_cwd() works just as getcwd(), only that it\n> takes an absolute path as additional parameter, returning the prefix\n> of the current working directory relative to the given path.  If the\n> cwd is no subdirectory of the given path, it returns NULL.\n> ...\n> +/*\n> + * get_relative_cwd() gets the prefix of the current working directory\n> + * relative to 'dir'.  If we are not inside 'dir', it returns NULL.\n> + * As a convenience, it also returns NULL if 'dir' is already NULL.\n> + */\n> +char *get_relative_cwd(char *buffer, int size, const char *dir)\n> +{\n> +\tchar *cwd = buffer;\n> +\n> +\tif (!dir || !getcwd(buffer, size))\n> +\t\treturn NULL;\n\nWhen is it not a fatal error if get_relative_cwd() is called\nwith a NULL dir parameter, or getcwd() fails?\n\nIf there is no valid such cases, I would rather have this\ndie(), former with \"BUG\" and the latter with strerror(errno).\n"},{"id":"49280","messageId":"7v3az3deac.fsf@assigned-by-dhcp.cox.net","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708010129530.14781@racer.site","subject":"Re: [PATCH 4/4] Clean up work-tree handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-08-01T05:17:31Z","receivedAt":"2007-08-01T05:17:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> The old version of work-tree support was an unholy mess, barely readable,\n> and not to the point.\n>\n> For example, why do you have to provide a worktree, when it is not used?\n> As in \"git status\".  Now it works.\n> ...\n\nWithout continuing with negatives, let's try to define the new,\ncorrected world order.\n\nI do not think the following is exactly what your cleaned-up\nversion tries to perform, but I am writing this down primarily\nto demonstrate the style and the level of detail I expect to\naccompany a clean-up patch like this.\n\n----------------------------------------------------------------\n\nDefinitions:\n\n - You can have \"checked out\" files on the filesystem, and such\n   files are said to be in your \"work tree\".  The directory\n   on the filesystem that corresponds to the toplevel entries of\n   the index and the tree objects directly contained in the\n   commit objects is called \"the toplevel of the work tree\",\n   or simply \"work tree\" if it is not ambiguous from the\n   context.\n\n - The directory that holds git repository information is called\n   \"git directory\".  This is typically .git directory at the\n   toplevel of your work tree, but not necessarily so.\n\n - You can perform many git operations without a work tree, but\n   some operations fundamentally require you to have one\n   (e.g. checkout and diff, unless two tree-ishes are given, do\n   not make sense without a work tree).\n\nThere are four predicates, two interrogators, and two\nmanipulators:\n\n - is_inside_git_dir(): this returns true if the $cwd is the git\n   directory or its subdirectory. [IS THIS STILL NEEDED???]\n\n - is_inside_work_tree(): this returns true if the $cwd is\n   inside work tree (i.e. either at the toplevel of the work\n   tree or its subdirectory).  [NEEDSHELP: is .git in the usual\n   layout considered \"is_inside_work_tree()\"?  Should it?]\n\n - is_bare_repository(): this returns true if no work tree is\n   found.  There is a corresponding function usable from the\n   scripts.\n\n - require_work_tree (shell): this is called by scripts that\n   needs to have a work tree to operate, and barfs otherwise.\n\n - get_git_dir(): this returns the location of the git\n   directory.  With GIT_DIR environment variable, or --git-dir\n   command line option, you can tell git to use a specific\n   directory as the git directory.  Otherwise a directory that\n   looks like a git directory and whose name is .git is looked\n   for, in the $cwd or its parent directory.  If there is no\n   such directory, and if the $cwd looks like a git directory,\n   $cwd is the git directory.\n\n - get_git_work_tree(): this returns the location of the work\n   tree; it returns NULL if there is none.  The command line\n   option --work-tree, or the environment variable GIT_WORK_TREE\n   can specify the location; otherwise, git directory is looked\n   for so that its configuration file can be read.  If\n   core.worktree is there, that specifies the location.\n   Otherwise, if the basename of the git directory is .git, it\n   is the parent directory of the git directory.  Otherwise, you\n   do not have work tree.\n\n - set_git_dir(): used to set the git directory, internally to\n   handle --git-dir option;\n\n - set_work_tree(): used to set the git directory, internally to\n   handle --work-tree option;\n\n----------------------------------------------------------------\n\nAfter writing the above down, it strikes me odd that we do not\nhave a predicate that says \"we know the work tree is there\".\n\nIf a command wants a work tree, and if you are outside the work\ntree, then is_inside_work_tree() returns false and\nget_git_work_tree() returns non NULL, so that is a good pair of\ninterface that can be mixed and matched (e.g. you can chdir to\nthe former to perform the whole tree operation, or refuse to\nperform, based on is_inside_work_tree being false, cwd relative\noperations).\n"},{"id":"49281","messageId":"7vwswfbywq.fsf@assigned-by-dhcp.cox.net","threadId":"9300","inReplyTo":"7vy7gvdgtn.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/4] Add functions get_relative_cwd() and is_inside_dir()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-08-01T05:35:01Z","receivedAt":"2007-08-01T05:35:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n>> The function get_relative_cwd() works just as getcwd(), only that it\n>> takes an absolute path as additional parameter, returning the prefix\n>> of the current working directory relative to the given path.  If the\n>> cwd is no subdirectory of the given path, it returns NULL.\n>> ...\n>> +/*\n>> + * get_relative_cwd() gets the prefix of the current working directory\n>> + * relative to 'dir'.  If we are not inside 'dir', it returns NULL.\n>> + * As a convenience, it also returns NULL if 'dir' is already NULL.\n>> + */\n>> +char *get_relative_cwd(char *buffer, int size, const char *dir)\n>> +{\n>> +\tchar *cwd = buffer;\n>> +\n>> +\tif (!dir || !getcwd(buffer, size))\n>> +\t\treturn NULL;\n>\n> When is it not a fatal error if get_relative_cwd() is called\n> with a NULL dir parameter, or getcwd() fails?\n>\n> If there is no valid such cases, I would rather have this\n> die(), former with \"BUG\" and the latter with strerror(errno).\n\nHeh, it turns out that there is this lazy or clever (depending\non the viewpoint) caller that passes the return value of\nget_git_work_tree() to this function and expect this to return\nNULL when no work tree is found.\n\nThe callers of the is_* functions are much cleaner and in that\nsense the series is a definite improvement, but this one\nparticular obscurity makes me wonder if it is replacing one\nunholy mess with a smaller but still unholy mess.\n\nWill apply on \"master\" and will be part of -rc4, but we probably\nwould want to have a longer pre-final freeze than usual to\nreally make sure this one is good.\n"},{"id":"49291","messageId":"7vvebz7hpw.fsf@assigned-by-dhcp.cox.net","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708010129530.14781@racer.site","subject":"Re: [PATCH 4/4] Clean up work-tree handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-08-01T08:59:55Z","receivedAt":"2007-08-01T08:59:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> diff --git a/builtin-ls-files.c b/builtin-ls-files.c\n> index 61577ea..d36181a 100644\n> --- a/builtin-ls-files.c\n> +++ b/builtin-ls-files.c\n> @@ -469,9 +469,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n>  \t\tbreak;\n>  \t}\n>  \n> -\tif (require_work_tree &&\n> -\t\t\t(!is_inside_work_tree() || is_inside_git_dir()))\n> -\t\tdie(\"This operation must be run in a work tree\");\n> +\tif (require_work_tree && !is_inside_work_tree()) {\n> +\t\tconst char *work_tree = get_git_work_tree();\n> +\t\tif (!work_tree || chdir(work_tree))\n> +\t\t\tdie(\"This operation must be run in a work tree\");\n> +\t}\n>  \n>  \tpathspec = get_pathspec(prefix, argv + i);\n>  \n\nSimilarly to this change, I am wondering if we would want to fix\nverify_non_filename() in setup.c, which does this:\n\n/*\n * Verify a filename that we got as an argument for a pathspec\n * entry. Note that a filename that begins with \"-\" never verifies\n * as true, because even if such a filename were to exist, we want\n * it to be preceded by the \"--\" marker (or we want the user to\n * use a format like \"./-filename\")\n */\nvoid verify_filename(const char *prefix, const char *arg)\n{\n...\n}\n\n/*\n * Opposite of the above: the command line did not have -- marker\n * and we parsed the arg as a refname.  It should not be interpretable\n * as a filename.\n */\nvoid verify_non_filename(const char *prefix, const char *arg)\n{\n        const char *name;\n        struct stat st;\n\n        if (!is_inside_work_tree() || is_inside_git_dir())\n                return;\n        if (*arg == '-')\n                return; /* flag */\n        name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;\n        if (!lstat(name, &st))\n                die(\"ambiguous argument '%s': both revision and filename\\n\"\n                    \"Use '--' to separate filenames from revisions\", arg);\n        if (errno != ENOENT)\n                die(\"'%s': %s\", arg, strerror(errno));\n}\n\nAt this point, we are given an ambiguous parameter, that could\nbe naming a path in the work tree.  If we are not in the work\ntree, then it is understandable that we do not have to barf.\nThe other check (i.e. \"|| is_inside_git_dir()\") does not hurt\n(iow, it is not an incorrect check per-se), because if you did\n\"cd .git && git log HEAD\" then the HEAD parameter cannot be\nnaming the path \".git/HEAD\" in the work tree, but (1) that is\nalready covered by .git/ being \"outside of work tree\", and (2)\nit is not something this function wants to check anyway\n(i.e. \"can the parameter be naming a file in the work tree?\").\n\nAm I mistaken and/or confused?\n"},{"id":"49299","messageId":"Pine.LNX.4.64.0708011147500.14781@racer.site","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708010211071.14781@racer.site","subject":"Re: [PATCH 0/9] work-tree clean ups","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T10:56:05Z","receivedAt":"2007-08-01T10:56:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 1 Aug 2007, Johannes Schindelin wrote:\n\n> On Tue, 31 Jul 2007, Junio C Hamano wrote:\n> \n> > Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> > \n> > > There is not really much that can be done about step 6/9: if we are in a \n> > > work tree: that does not mean that we are _not_ in the git_dir.  (And no, \n> > > this does not break git-clean, as a work tree is a work tree is a work \n> > > tree.  If the user was stupid enough to specify the same directory as \n> > > GIT_DIR and GIT_WORK_TREE, then that is _her_ problem.  Git is a powerful \n> > > tool, and you can harm yourself with it.  Tough.)\n> > \n> > I think we might have a slight misunderstanding.  The \"clean\"\n> > issue that was raised in an ancient thread was this sequence:\n> > \n> > \t$ git init\n> >         $ cd .git\n> >         $ git clean\n> > \n> > It did not involve GIT_DIR (nor GIT_WORK_TREE as it was not even\n> > there).\n> \n> I very much _did_ mean that case.  When \"git clean\" is run in \".git/\", it \n> should not say that it is in the working tree.  But I guess that my patch \n> series is not really looking out for that;  I'll make that an add-on \n> patch.  (But that _will_ have to wait until tomorrow afternoon.)\n\nI should not have answered so early.\n\nIn setup.c, I put in a comment that explains clearly where (in the absence \nof GIT_DIR) setup_git_directory_gently() looks for the git directory:\n\n        /*\n         * Test in the following order (relative to the cwd):\n         * - .git/\n         * - ./ (bare)\n         * - ../.git/\n         * - ../ (bare)\n         * - ../../.git/\n         *   etc.\n         */\n\nAt least I hope that this explanation is clear.\n\nSo what happens in this case:\n\n\t$ git init\n\t$ cd .git\n\t$ git clean\n\nIn setup_git_directory_gently(), it is tested first if there is a \nsubdirectory .git/.  No, none.  Then it is tested if \".\" is a git \ndirectory.  Yes!  So, work_tree is set to NULL tentatively (to be \noverridden by either core.worktree or GIT_WORK_TREE), and it is assumed to \nbe bare (also subject to overriding).  So all is well!\n\nYou might have noticed that I left out --work-tree= handling; when \n--work-tree=<something> is specified, GIT_WORK_TREE is _forced_ to the new \nvalue, so it is literally handled by the same code as GIT_WORK_TREE.\n\nCiao,\nDscho\n"},{"id":"49301","messageId":"Pine.LNX.4.64.0708011218420.14781@racer.site","threadId":"9300","inReplyTo":"7vwswfbywq.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/4] Add functions get_relative_cwd() and is_inside_dir()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T11:38:55Z","receivedAt":"2007-08-01T11:38:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 31 Jul 2007, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> >\n> >> The function get_relative_cwd() works just as getcwd(), only that it\n> >> takes an absolute path as additional parameter, returning the prefix\n> >> of the current working directory relative to the given path.  If the\n> >> cwd is no subdirectory of the given path, it returns NULL.\n> >> ...\n> >> +/*\n> >> + * get_relative_cwd() gets the prefix of the current working directory\n> >> + * relative to 'dir'.  If we are not inside 'dir', it returns NULL.\n> >> + * As a convenience, it also returns NULL if 'dir' is already NULL.\n> >> + */\n> >> +char *get_relative_cwd(char *buffer, int size, const char *dir)\n> >> +{\n> >> +\tchar *cwd = buffer;\n> >> +\n> >> +\tif (!dir || !getcwd(buffer, size))\n> >> +\t\treturn NULL;\n> >\n> > When is it not a fatal error if get_relative_cwd() is called\n> > with a NULL dir parameter, or getcwd() fails?\n> >\n> > If there is no valid such cases, I would rather have this\n> > die(), former with \"BUG\" and the latter with strerror(errno).\n> \n> Heh, it turns out that there is this lazy or clever (depending\n> on the viewpoint) caller that passes the return value of\n> get_git_work_tree() to this function and expect this to return\n> NULL when no work tree is found.\n\nRight.  I thought I had said that (something along the lines: it is more \nconvenient not having to check the directory), but I probably did not.\n\n> The callers of the is_* functions are much cleaner and in that sense the \n> series is a definite improvement, but this one particular obscurity \n> makes me wonder if it is replacing one unholy mess with a smaller but \n> still unholy mess.\n> \n> Will apply on \"master\" and will be part of -rc4, but we probably would \n> want to have a longer pre-final freeze than usual to really make sure \n> this one is good.\n\nI'll provide a patch which makes the callers of get_relative_cwd() holy, \nand skip the check in get_relative_cwd(), okay?\n\nCiao,\nDscho\n"},{"id":"49302","messageId":"Pine.LNX.4.64.0708011239090.14781@racer.site","threadId":"9300","inReplyTo":"7v3az3deac.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 4/4] Clean up work-tree handling","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T11:46:50Z","receivedAt":"2007-08-01T11:46:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 31 Jul 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > The old version of work-tree support was an unholy mess, barely readable,\n> > and not to the point.\n> >\n> > For example, why do you have to provide a worktree, when it is not used?\n> > As in \"git status\".  Now it works.\n> > ...\n> \n> Without continuing with negatives, let's try to define the new,\n> corrected world order.\n> \n> I do not think the following is exactly what your cleaned-up\n> version tries to perform, but I am writing this down primarily\n> to demonstrate the style and the level of detail I expect to\n> accompany a clean-up patch like this.\n\nAfter reading your description I sink into the ground in shame.  I really \nlike the style this has, and agree that something as nice as this should \nhave been there.\n\n>  - is_inside_git_dir(): this returns true if the $cwd is the git\n>    directory or its subdirectory. [IS THIS STILL NEEDED???]\n\nHmmm.\n\n>  - is_inside_work_tree(): this returns true if the $cwd is\n>    inside work tree (i.e. either at the toplevel of the work\n>    tree or its subdirectory).  [NEEDSHELP: is .git in the usual\n>    layout considered \"is_inside_work_tree()\"?  Should it?]\n\n.git/ is not considered part of the work tree, even if it is _physically_ \nthere.\n\n> After writing the above down, it strikes me odd that we do not\n> have a predicate that says \"we know the work tree is there\".\n>\n> If a command wants a work tree, and if you are outside the work\n> tree, then is_inside_work_tree() returns false and\n> get_git_work_tree() returns non NULL, so that is a good pair of\n> interface that can be mixed and matched (e.g. you can chdir to\n> the former to perform the whole tree operation, or refuse to\n> perform, based on is_inside_work_tree being false, cwd relative\n> operations).\n\nYes.  Builtins which need a working tree expect to start at the toplevel \nof the work tree (which I like to call \"working directory\", because it is \ndescribed as such in the glossary AFAIR), and therefore they chdir() to \nthe toplevel in any case.\n\nI'll be running \"master\"+worktree+branch-newdir for the remainder of the \n1.5.3-rc period, to be sure that all works as intended.\n\nCiao,\nDscho\n"},{"id":"49303","messageId":"Pine.LNX.4.64.0708011250020.14781@racer.site","threadId":"9300","inReplyTo":"7vvebz7hpw.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 4/4] Clean up work-tree handling","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T11:53:56Z","receivedAt":"2007-08-01T11:53:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 1 Aug 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > diff --git a/builtin-ls-files.c b/builtin-ls-files.c\n> > index 61577ea..d36181a 100644\n> > --- a/builtin-ls-files.c\n> > +++ b/builtin-ls-files.c\n> > @@ -469,9 +469,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n> >  \t\tbreak;\n> >  \t}\n> >  \n> > -\tif (require_work_tree &&\n> > -\t\t\t(!is_inside_work_tree() || is_inside_git_dir()))\n> > -\t\tdie(\"This operation must be run in a work tree\");\n> > +\tif (require_work_tree && !is_inside_work_tree()) {\n> > +\t\tconst char *work_tree = get_git_work_tree();\n> > +\t\tif (!work_tree || chdir(work_tree))\n> > +\t\t\tdie(\"This operation must be run in a work tree\");\n> > +\t}\n> >  \n> >  \tpathspec = get_pathspec(prefix, argv + i);\n> >  \n> \n> Similarly to this change, I am wondering if we would want to fix\n> verify_non_filename() in setup.c, which does this:\n> \n> /*\n>  * Verify a filename that we got as an argument for a pathspec\n>  * entry. Note that a filename that begins with \"-\" never verifies\n>  * as true, because even if such a filename were to exist, we want\n>  * it to be preceded by the \"--\" marker (or we want the user to\n>  * use a format like \"./-filename\")\n>  */\n> void verify_filename(const char *prefix, const char *arg)\n> {\n> ...\n> }\n> \n> /*\n>  * Opposite of the above: the command line did not have -- marker\n>  * and we parsed the arg as a refname.  It should not be interpretable\n>  * as a filename.\n>  */\n> void verify_non_filename(const char *prefix, const char *arg)\n> {\n>         const char *name;\n>         struct stat st;\n> \n>         if (!is_inside_work_tree() || is_inside_git_dir())\n>                 return;\n>         if (*arg == '-')\n>                 return; /* flag */\n>         name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;\n>         if (!lstat(name, &st))\n>                 die(\"ambiguous argument '%s': both revision and filename\\n\"\n>                     \"Use '--' to separate filenames from revisions\", arg);\n>         if (errno != ENOENT)\n>                 die(\"'%s': %s\", arg, strerror(errno));\n> }\n> \n> At this point, we are given an ambiguous parameter, that could\n> be naming a path in the work tree.  If we are not in the work\n> tree, then it is understandable that we do not have to barf.\n> The other check (i.e. \"|| is_inside_git_dir()\") does not hurt\n> (iow, it is not an incorrect check per-se), because if you did\n> \"cd .git && git log HEAD\" then the HEAD parameter cannot be\n> naming the path \".git/HEAD\" in the work tree, but (1) that is\n> already covered by .git/ being \"outside of work tree\", and (2)\n> it is not something this function wants to check anyway\n> (i.e. \"can the parameter be naming a file in the work tree?\").\n> \n> Am I mistaken and/or confused?\n\nI think you are completely right.  Inside a bare repository, \"git log \nFETCH_HEAD\" should not need to complain.  And I think that the \n\"is_inside_git_dir()\" could be _replaced_ by \"!is_inside_work_tree()\", \nsince that is the intent of that call.\n\nCiao,\nDscho\n"},{"id":"49315","messageId":"Pine.LNX.4.64.0708011624260.14781@racer.site","threadId":"9300","inReplyTo":"7vwswfbywq.fsf@assigned-by-dhcp.cox.net","subject":"[NOT-SERIOUS PATCH] Make get_relative_cwd() not accept NULL for a directory","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T15:26:04Z","receivedAt":"2007-08-01T15:26:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nEarlier, get_relative_cwd() interpreted \"dir == NULL\" as \"outside of the dir\",\nand therefore returned NULL.  Be more strict now.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tAs promised.\n\n\tOkay, I made up my mind.  Allowing \"dir == NULL\" is not only a matter of\n\tconvenience.  It is the most natural way to say that \"dir\" is an invalid\n\tor non-existing directory.\n\n\tBesides, this patch adds 14.286% more lines than it removes ;-)\n\n\tBut ultimately, it is your decision, Junio, and I am d'accord with \n\twhat you choose.\n\n dir.c   |    3 ---\n setup.c |   10 +++++++---\n 2 files changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex b3329f4..cfcde13 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -646,7 +646,6 @@ file_exists(const char *f)\n /*\n  * get_relative_cwd() gets the prefix of the current working directory\n  * relative to 'dir'.  If we are not inside 'dir', it returns NULL.\n- * As a convenience, it also returns NULL if 'dir' is already NULL.\n  */\n char *get_relative_cwd(char *buffer, int size, const char *dir)\n {\n@@ -656,8 +655,6 @@ char *get_relative_cwd(char *buffer, int size, const char *dir)\n \t * a lazy caller can pass a NULL returned from get_git_work_tree()\n \t * and rely on this function to return NULL.\n \t */\n-\tif (!dir)\n-\t\treturn NULL;\n \tif (!getcwd(buffer, size))\n \t\tdie(\"can't find the current directory: %s\", strerror(errno));\n \ndiff --git a/setup.c b/setup.c\nindex 3653092..2f720f8 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -183,8 +183,10 @@ int is_inside_git_dir(void)\n \n int is_inside_work_tree(void)\n {\n-\tif (inside_work_tree < 0)\n-\t\tinside_work_tree = is_inside_dir(get_git_work_tree());\n+\tif (inside_work_tree < 0) {\n+\t\tconst char *work_tree = get_git_work_tree();\n+\t\tinside_work_tree = work_tree ? is_inside_dir(work_tree) : 0;\n+\t}\n \treturn inside_work_tree;\n }\n \n@@ -370,10 +372,12 @@ const char *setup_git_directory(void)\n \t/* If the work tree is not the default one, recompute prefix */\n \tif (inside_work_tree < 0) {\n \t\tstatic char buffer[PATH_MAX + 1];\n+\t\tconst char *work_tree;\n \t\tchar *rel;\n \t\tif (retval && chdir(retval))\n \t\t\tdie (\"Could not jump back into original cwd\");\n-\t\trel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());\n+\t\twork_tree = get_git_work_tree();\n+\t\trel = work_tree ? get_relative_cwd(buffer, PATH_MAX, work_tree) : NULL;\n \t\treturn rel && *rel ? strcat(rel, \"/\") : NULL;\n \t}\n \n-- \n1.5.3.rc3.112.gf60b6\n"},{"id":"49323","messageId":"7vmyxb5h0g.fsf@assigned-by-dhcp.cox.net","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708011624260.14781@racer.site","subject":"Re: [NOT-SERIOUS PATCH] Make get_relative_cwd() not accept NULL for a directory","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-08-01T16:58:07Z","receivedAt":"2007-08-01T16:58:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> \tOkay, I made up my mind.  Allowing \"dir == NULL\" is not\n> \tonly a matter of convenience.  It is the most natural\n> \tway to say that \"dir\" is an invalid or non-existing\n> \tdirectory.\n\nThat is also fine; it only needs to be clarified somehow to\npeople who might wonder what get_relative_cwd() function is used\nfor and how to use it in their programs.  The comment that says\n\"As a convenience\" may need to become a bit more elaborate to\nsay why it is convenient for the callers to do so (e.g. \"The\ncaller may have called another function that returns a directory\nto obtain the 'dir', which may have returned NULL as a way to\nsay 'there is nothing applicable in your case', and in such a\ncase, your $(cwd) relative to that 'dir' is also something that\ncannot be used, hence a NULL is returned\").\n"},{"id":"49334","messageId":"Pine.LNX.4.64.0708011926200.14781@racer.site","threadId":"9300","inReplyTo":"7vmyxb5h0g.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] get_relative_cwd(): clarify why it handles dir == NULL","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-08-01T18:26:59Z","receivedAt":"2007-08-01T18:26:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThe comment did not make a good case why it makes sense.  It does so now.\n\nPointed out by Junio Hamano.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n dir.c |   11 ++++++++++-\n 1 files changed, 10 insertions(+), 1 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex b3329f4..791c566 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -646,7 +646,16 @@ file_exists(const char *f)\n /*\n  * get_relative_cwd() gets the prefix of the current working directory\n  * relative to 'dir'.  If we are not inside 'dir', it returns NULL.\n- * As a convenience, it also returns NULL if 'dir' is already NULL.\n+ *\n+ * As a convenience, it also returns NULL if 'dir' is already NULL.  The\n+ * reason for this behaviour is that it is natural for functions returning\n+ * directory names to return NULL to say \"this directory does not exist\"\n+ * or \"this directory is invalid\".  These cases are usually handled the\n+ * same as if the cwd is not inside 'dir' at all, so get_relative_cwd()\n+ * returns NULL for both of them.\n+ *\n+ * Most notably, get_relative_cwd(buffer, size, get_git_work_tree())\n+ * unifies the handling of \"outside work tree\" with \"no work tree at all\".\n  */\n char *get_relative_cwd(char *buffer, int size, const char *dir)\n {\n-- \n1.5.3.rc3.112.gf60b6\n"},{"id":"49393","messageId":"7vsl72zaaz.fsf@assigned-by-dhcp.cox.net","threadId":"9300","inReplyTo":"Pine.LNX.4.64.0708011239090.14781@racer.site","subject":"Re: [PATCH 4/4] Clean up work-tree handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-08-02T07:04:52Z","receivedAt":"2007-08-02T07:04:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> I do not think the following is exactly what your cleaned-up\n>> version tries to perform, but I am writing this down primarily\n>> to demonstrate the style and the level of detail I expect to\n>> accompany a clean-up patch like this.\n>\n> After reading your description I sink into the ground in shame.  I really \n> like the style this has, and agree that something as nice as this should \n> have been there.\n\nNot too late.  There always is a room for Documentation/\nimprovements ;-)\n\nSeriously, I think config.txt mentions GIT_WORK_TREE and GIT_DIR\nand git.txt has separate sections for environment and config,\nbut I do not see a good section that gives a comprehensive view\nof how they work together to affect your git experience.\nPerhaps between File/Directory Structure and Terminology\nsections we would want to have a description of how repository\nand worktree relate to each other.\n"}]}