Merge branch 'ei/worktree+filter'
authorJunio C Hamano <gitster@pobox.com>
Sun, 1 Jul 2007 20:10:42 +0000 (13:10 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 1 Jul 2007 20:10:42 +0000 (13:10 -0700)
* ei/worktree+filter:
filter-branch: always export GIT_DIR if it is set
setup_git_directory: fix segfault if repository is found in cwd
test GIT_WORK_TREE
extend rev-parse test for --is-inside-work-tree
Use new semantics of is_bare/inside_git_dir/inside_work_tree
introduce GIT_WORK_TREE to specify the work tree
test git rev-parse
rev-parse: introduce --is-bare-repository
rev-parse: document --is-inside-git-dir

15 files changed:
Documentation/config.txt
Documentation/git-rev-parse.txt
Documentation/git.txt
builtin-ls-files.c
builtin-rev-parse.c
cache.h
connect.c
git-filter-branch.sh
git-sh-setup.sh
git-svn.perl
git.c
setup.c
t/t1500-rev-parse.sh [new file with mode: 0755]
t/t1501-worktree.sh [new file with mode: 0755]
t/test-lib.sh
index a2057d9d24432c6092fa875454f2ebdd6b5dec89..3dc17a6d787a542d35d41207b9f00e19779ec943 100644 (file)
@@ -172,6 +172,13 @@ repository that ends in "/.git" is assumed to be not bare (bare =
 false), while all other repositories are assumed to be bare (bare
 = true).
 
+core.worktree::
+       Set the path to the working tree.  The value will not be
+       used in combination with repositories found automatically in
+       a .git directory (i.e. $GIT_DIR is not set).
+       This can be overriden by the GIT_WORK_TREE environment
+       variable and the '--work-tree' command line option.
+
 core.logAllRefUpdates::
        Updates to a ref <ref> is logged to the file
        "$GIT_DIR/logs/<ref>", by appending the new and old
index 87771b832bdc71a9b18535f0ab21e68478891e46..eea9c9cfe9ec9320b328c37eefb6c7100edab913 100644 (file)
@@ -90,8 +90,15 @@ OPTIONS
        Show `$GIT_DIR` if defined else show the path to the .git directory.
 
 --is-inside-git-dir::
-       Return "true" if we are in the git directory, otherwise "false".
-       Some commands require to be run in a working directory.
+       When the current working directory is below the repository
+       directory print "true", otherwise "false".
+
+--is-inside-work-tree::
+       When the current working directory is inside the work tree of the
+       repository print "true", otherwise "false".
+
+--is-bare-repository::
+       When the repository is bare print "true", otherwise "false".
 
 --short, --short=number::
        Instead of outputting the full SHA1 values of object names try to
index 826914837bc0a3e1fa5f044f69b837abdd42cf8c..2cc0b214d20e28321c5097e79af741960e30ea19 100644 (file)
@@ -10,7 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate]
-    [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
+    [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
+    [--help] COMMAND [ARGS]
 
 DESCRIPTION
 -----------
@@ -103,6 +104,14 @@ OPTIONS
        Set the path to the repository. This can also be controlled by
        setting the GIT_DIR environment variable.
 
+--work-tree=<path>::
+       Set the path to the working tree.  The value will not be
+       used in combination with repositories found automatically in
+       a .git directory (i.e. $GIT_DIR is not set).
+       This can also be controlled by setting the GIT_WORK_TREE
+       environment variable and the core.worktree configuration
+       variable.
+
 --bare::
        Same as --git-dir=`pwd`.
 
@@ -347,6 +356,13 @@ git so take care if using Cogito etc.
        specifies a path to use instead of the default `.git`
        for the base of the repository.
 
+'GIT_WORK_TREE'::
+       Set the path to the working tree.  The value will not be
+       used in combination with repositories found automatically in
+       a .git directory (i.e. $GIT_DIR is not set).
+       This can also be controlled by the '--work-tree' command line
+       option and the core.worktree configuration variable.
+
 git Commits
 ~~~~~~~~~~~
 'GIT_AUTHOR_NAME'::
index 5398a41415372b07fa1fdb43c26b5570e9d990b8..61577ea13ffdf0143dcb8a13c37ae49c1f8f2018 100644 (file)
@@ -470,7 +470,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
        }
 
        if (require_work_tree &&
-                       (is_bare_repository() || is_inside_git_dir()))
+                       (!is_inside_work_tree() || is_inside_git_dir()))
                die("This operation must be run in a work tree");
 
        pathspec = get_pathspec(prefix, argv + i);
index 37addb25fafbedba9bad6e99746ee65bacdee7d3..497903a85a69288afc291ff49e32f7c80140beae 100644 (file)
@@ -352,6 +352,16 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                                : "false");
                                continue;
                        }
+                       if (!strcmp(arg, "--is-inside-work-tree")) {
+                               printf("%s\n", is_inside_work_tree() ? "true"
+                                               : "false");
+                               continue;
+                       }
+                       if (!strcmp(arg, "--is-bare-repository")) {
+                               printf("%s\n", is_bare_repository() ? "true"
+                                               : "false");
+                               continue;
+                       }
                        if (!prefixcmp(arg, "--since=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
diff --git a/cache.h b/cache.h
index 0525c4ee55a05304a6b2018cc27933baa5510aba..dcadfef929bd0638cfcc9ac42bb43f264d0c0b63 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -192,6 +192,7 @@ enum object_type {
 };
 
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@ -207,6 +208,7 @@ enum object_type {
 extern int is_bare_repository_cfg;
 extern int is_bare_repository(void);
 extern int is_inside_git_dir(void);
+extern int is_inside_work_tree(void);
 extern const char *get_git_dir(void);
 extern char *get_object_directory(void);
 extern char *get_refs_directory(void);
index a5afd2a5361d840347eb64a3a73db9d8dcbd1057..65e79edc77101ba4f195d5ce267c712da52b3b0b 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -587,6 +587,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
                        unsetenv(ALTERNATE_DB_ENVIRONMENT);
                        unsetenv(DB_ENVIRONMENT);
                        unsetenv(GIT_DIR_ENVIRONMENT);
+                       unsetenv(GIT_WORK_TREE_ENVIRONMENT);
                        unsetenv(GRAFT_ENVIRONMENT);
                        unsetenv(INDEX_ENVIRONMENT);
                        execlp("sh", "sh", "-c", command, NULL);
index 8fa5ce6467b12119868a4c3c74c3e3740f6d6b8e..a2fcebc1c63082db03445b65e340286d0c129903 100644 (file)
@@ -312,9 +312,10 @@ case "$GIT_DIR" in
 /*)
        ;;
 *)
-       export GIT_DIR="$(pwd)/../../$GIT_DIR"
+       GIT_DIR="$(pwd)/../../$GIT_DIR"
        ;;
 esac
+export GIT_DIR GIT_WORK_TREE=.
 
 export GIT_INDEX_FILE="$(pwd)/../index"
 git-read-tree # seed the index file
index f24c7f2d23c13e9874308a019f3c0f93225de3c0..0de49e8459a0d00264de442e6e89db846496541d 100755 (executable)
@@ -29,11 +29,7 @@ set_reflog_action() {
 }
 
 is_bare_repository () {
-       git-config --bool --get core.bare ||
-       case "$GIT_DIR" in
-       .git | */.git) echo false ;;
-       *) echo true ;;
-       esac
+       git-rev-parse --is-bare-repository
 }
 
 cd_to_toplevel () {
@@ -48,7 +44,7 @@ cd_to_toplevel () {
 }
 
 require_work_tree () {
-       test $(is_bare_repository) = false &&
+       test $(git-rev-parse --is-inside-work-tree) = true &&
        test $(git-rev-parse --is-inside-git-dir) = false ||
        die "fatal: $0 cannot be used without a working tree."
 }
index 98687f54d79fa2f7212a5cdad80332b4231d50df..03d5e2d979c48159643256634861162d34a470ce 100755 (executable)
@@ -596,8 +596,7 @@ sub post_fetch_checkout {
        my $index = $ENV{GIT_INDEX_FILE} || "$ENV{GIT_DIR}/index";
        return if -f $index;
 
-       chomp(my $bare = `git config --bool --get core.bare`);
-       return if $bare eq 'true';
+       return if command_oneline(qw/rev-parse --is-inside-work-tree/) eq 'false';
        return if command_oneline(qw/rev-parse --is-inside-git-dir/) eq 'true';
        command_noisy(qw/read-tree -m -u -v HEAD HEAD/);
        print STDERR "Checked out HEAD:\n  ",
diff --git a/git.c b/git.c
index cfec5d70ee42852f3819fe46e99ebc70f089ccc7..b6bf5ad5ec97699f0a1f4d6c6616c7c9a240d144 100644 (file)
--- a/git.c
+++ b/git.c
@@ -4,7 +4,7 @@
 #include "quote.h"
 
 const char git_usage_string[] =
-       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]";
+       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
 
 static void prepend_to_path(const char *dir, int len)
 {
@@ -69,6 +69,16 @@ static int handle_options(const char*** argv, int* argc)
                        handled++;
                } else if (!prefixcmp(cmd, "--git-dir=")) {
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
+               } else if (!strcmp(cmd, "--work-tree")) {
+                       if (*argc < 2) {
+                               fprintf(stderr, "No directory given for --work-tree.\n" );
+                               usage(git_usage_string);
+                       }
+                       setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
+                       (*argv)++;
+                       (*argc)--;
+               } else if (!prefixcmp(cmd, "--work-tree=")) {
+                       setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
                } else if (!strcmp(cmd, "--bare")) {
                        static char git_dir[PATH_MAX+1];
                        setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 1);
@@ -214,7 +224,7 @@ const char git_version_string[] = GIT_VERSION;
  * require working tree to be present -- anything uses this needs
  * RUN_SETUP for reading from the configuration file.
  */
-#define NOT_BARE       (1<<2)
+#define NEED_WORK_TREE (1<<2)
 
 struct cmd_struct {
        const char *cmd;
@@ -233,10 +243,9 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)
                prefix = setup_git_directory();
        if (p->option & USE_PAGER)
                setup_pager();
-       if (p->option & NOT_BARE) {
-               if (is_bare_repository() || is_inside_git_dir())
-                       die("%s must be run in a work tree", p->cmd);
-       }
+       if ((p->option & NEED_WORK_TREE) &&
+           (!is_inside_work_tree() || is_inside_git_dir()))
+               die("%s must be run in a work tree", p->cmd);
        trace_argv_printf(argv, argc, "trace: built-in: git");
 
        status = p->fn(argc, argv, prefix);
@@ -264,7 +273,7 @@ static void handle_internal_command(int argc, const char **argv)
 {
        const char *cmd = argv[0];
        static struct cmd_struct commands[] = {
-               { "add", cmd_add, RUN_SETUP | NOT_BARE },
+               { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
                { "annotate", cmd_annotate, RUN_SETUP | USE_PAGER },
                { "apply", cmd_apply },
                { "archive", cmd_archive },
@@ -274,9 +283,9 @@ static void handle_internal_command(int argc, const char **argv)
                { "cat-file", cmd_cat_file, RUN_SETUP },
                { "checkout-index", cmd_checkout_index, RUN_SETUP },
                { "check-ref-format", cmd_check_ref_format },
-               { "check-attr", cmd_check_attr, RUN_SETUP | NOT_BARE },
+               { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
                { "cherry", cmd_cherry, RUN_SETUP },
-               { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NOT_BARE },
+               { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "config", cmd_config },
                { "count-objects", cmd_count_objects, RUN_SETUP },
@@ -304,7 +313,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "mailsplit", cmd_mailsplit },
                { "merge-base", cmd_merge_base, RUN_SETUP },
                { "merge-file", cmd_merge_file },
-               { "mv", cmd_mv, RUN_SETUP | NOT_BARE },
+               { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
                { "name-rev", cmd_name_rev, RUN_SETUP },
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
                { "pickaxe", cmd_blame, RUN_SETUP | USE_PAGER },
@@ -317,9 +326,9 @@ static void handle_internal_command(int argc, const char **argv)
                { "rerere", cmd_rerere, RUN_SETUP },
                { "rev-list", cmd_rev_list, RUN_SETUP },
                { "rev-parse", cmd_rev_parse, RUN_SETUP },
-               { "revert", cmd_revert, RUN_SETUP | NOT_BARE },
-               { "rm", cmd_rm, RUN_SETUP | NOT_BARE },
-               { "runstatus", cmd_runstatus, RUN_SETUP | NOT_BARE },
+               { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
+               { "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE },
+               { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
                { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP | USE_PAGER },
diff --git a/setup.c b/setup.c
index 14f62c42e3ac6ead75d4963ee705b9a110da3de0..01f74d4644c35862b2498ee8534213b2d76bf721 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -95,7 +95,7 @@ void verify_non_filename(const char *prefix, const char *arg)
        const char *name;
        struct stat st;
 
-       if (is_inside_git_dir())
+       if (!is_inside_work_tree() || is_inside_git_dir())
                return;
        if (*arg == '-')
                return; /* flag */
@@ -174,41 +174,96 @@ static int inside_git_dir = -1;
 
 int is_inside_git_dir(void)
 {
-       if (inside_git_dir < 0) {
-               char buffer[1024];
-
-               if (is_bare_repository())
-                       return (inside_git_dir = 1);
-               if (getcwd(buffer, sizeof(buffer))) {
-                       const char *git_dir = get_git_dir(), *cwd = buffer;
-                       while (*git_dir && *git_dir == *cwd) {
-                               git_dir++;
-                               cwd++;
-                       }
-                       inside_git_dir = !*git_dir;
-               } else
-                       inside_git_dir = 0;
+       if (inside_git_dir >= 0)
+               return inside_git_dir;
+       die("BUG: is_inside_git_dir called before setup_git_directory");
+}
+
+static int inside_work_tree = -1;
+
+int is_inside_work_tree(void)
+{
+       if (inside_git_dir >= 0)
+               return inside_work_tree;
+       die("BUG: is_inside_work_tree called before setup_git_directory");
+}
+
+static char *gitworktree_config;
+
+static int git_setup_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "core.worktree")) {
+               if (gitworktree_config)
+                       strlcpy(gitworktree_config, value, PATH_MAX);
+               return 0;
        }
-       return inside_git_dir;
+       return git_default_config(var, value);
 }
 
 const char *setup_git_directory_gently(int *nongit_ok)
 {
        static char cwd[PATH_MAX+1];
-       const char *gitdirenv;
-       int len, offset;
+       char worktree[PATH_MAX+1], gitdir[PATH_MAX+1];
+       const char *gitdirenv, *gitworktree;
+       int wt_rel_gitdir = 0;
 
-       /*
-        * If GIT_DIR is set explicitly, we're not going
-        * to do any discovery, but we still do repository
-        * validation.
-        */
        gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
-       if (gitdirenv) {
-               if (PATH_MAX - 40 < strlen(gitdirenv))
-                       die("'$%s' too big", GIT_DIR_ENVIRONMENT);
-               if (is_git_directory(gitdirenv))
+       if (!gitdirenv) {
+               int len, offset;
+
+               if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
+                       die("Unable to read current working directory");
+
+               offset = len = strlen(cwd);
+               for (;;) {
+                       if (is_git_directory(".git"))
+                               break;
+                       if (offset == 0) {
+                               offset = -1;
+                               break;
+                       }
+                       chdir("..");
+                       while (cwd[--offset] != '/')
+                               ; /* do nothing */
+               }
+
+               if (offset >= 0) {
+                       inside_work_tree = 1;
+                       git_config(git_default_config);
+                       if (offset == len) {
+                               inside_git_dir = 0;
+                               return NULL;
+                       }
+
+                       cwd[len++] = '/';
+                       cwd[len] = '\0';
+                       inside_git_dir = !prefixcmp(cwd + offset + 1, ".git/");
+                       return cwd + offset + 1;
+               }
+
+               if (chdir(cwd))
+                       die("Cannot come back to cwd");
+               if (!is_git_directory(".")) {
+                       if (nongit_ok) {
+                               *nongit_ok = 1;
+                               return NULL;
+                       }
+                       die("Not a git repository");
+               }
+               setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
+               gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
+               if (!gitdirenv)
+                       die("getenv after setenv failed");
+       }
+
+       if (PATH_MAX - 40 < strlen(gitdirenv)) {
+               if (nongit_ok) {
+                       *nongit_ok = 1;
                        return NULL;
+               }
+               die("$%s too big", GIT_DIR_ENVIRONMENT);
+       }
+       if (!is_git_directory(gitdirenv)) {
                if (nongit_ok) {
                        *nongit_ok = 1;
                        return NULL;
@@ -218,41 +273,92 @@ const char *setup_git_directory_gently(int *nongit_ok)
 
        if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
                die("Unable to read current working directory");
+       if (chdir(gitdirenv)) {
+               if (nongit_ok) {
+                       *nongit_ok = 1;
+                       return NULL;
+               }
+               die("Cannot change directory to $%s '%s'",
+                       GIT_DIR_ENVIRONMENT, gitdirenv);
+       }
+       if (!getcwd(gitdir, sizeof(gitdir)-1) || gitdir[0] != '/')
+               die("Unable to read current working directory");
+       if (chdir(cwd))
+               die("Cannot come back to cwd");
 
-       offset = len = strlen(cwd);
-       for (;;) {
-               if (is_git_directory(".git"))
-                       break;
-               chdir("..");
-               do {
-                       if (!offset) {
-                               if (is_git_directory(cwd)) {
-                                       if (chdir(cwd))
-                                               die("Cannot come back to cwd");
-                                       setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
-                                       inside_git_dir = 1;
-                                       return NULL;
-                               }
-                               if (nongit_ok) {
-                                       if (chdir(cwd))
-                                               die("Cannot come back to cwd");
-                                       *nongit_ok = 1;
-                                       return NULL;
-                               }
-                               die("Not a git repository");
+       /*
+        * In case there is a work tree we may change the directory,
+        * therefore make GIT_DIR an absolute path.
+        */
+       if (gitdirenv[0] != '/') {
+               setenv(GIT_DIR_ENVIRONMENT, gitdir, 1);
+               gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
+               if (!gitdirenv)
+                       die("getenv after setenv failed");
+               if (PATH_MAX - 40 < strlen(gitdirenv)) {
+                       if (nongit_ok) {
+                               *nongit_ok = 1;
+                               return NULL;
                        }
-               } while (cwd[--offset] != '/');
+                       die("$%s too big after expansion to absolute path",
+                               GIT_DIR_ENVIRONMENT);
+               }
+       }
+
+       strcat(cwd, "/");
+       strcat(gitdir, "/");
+       inside_git_dir = !prefixcmp(cwd, gitdir);
+
+       gitworktree = getenv(GIT_WORK_TREE_ENVIRONMENT);
+       if (!gitworktree) {
+               gitworktree_config = worktree;
+               worktree[0] = '\0';
+       }
+       git_config(git_setup_config);
+       if (!gitworktree) {
+               gitworktree_config = NULL;
+               if (worktree[0])
+                       gitworktree = worktree;
+               if (gitworktree && gitworktree[0] != '/')
+                       wt_rel_gitdir = 1;
+       }
+
+       if (wt_rel_gitdir && chdir(gitdirenv))
+               die("Cannot change directory to $%s '%s'",
+                       GIT_DIR_ENVIRONMENT, gitdirenv);
+       if (gitworktree && chdir(gitworktree)) {
+               if (nongit_ok) {
+                       if (wt_rel_gitdir && chdir(cwd))
+                               die("Cannot come back to cwd");
+                       *nongit_ok = 1;
+                       return NULL;
+               }
+               if (wt_rel_gitdir)
+                       die("Cannot change directory to working tree '%s'"
+                               " from $%s", gitworktree, GIT_DIR_ENVIRONMENT);
+               else
+                       die("Cannot change directory to working tree '%s'",
+                               gitworktree);
        }
+       if (!getcwd(worktree, sizeof(worktree)-1) || worktree[0] != '/')
+               die("Unable to read current working directory");
+       strcat(worktree, "/");
+       inside_work_tree = !prefixcmp(cwd, worktree);
 
-       if (offset == len)
+       if (gitworktree && inside_work_tree && !prefixcmp(worktree, gitdir) &&
+           strcmp(worktree, gitdir)) {
+               inside_git_dir = 0;
+       }
+
+       if (!inside_work_tree) {
+               if (chdir(cwd))
+                       die("Cannot come back to cwd");
                return NULL;
+       }
 
-       /* Make "offset" point to past the '/', and add a '/' at the end */
-       offset++;
-       cwd[len++] = '/';
-       cwd[len] = 0;
-       inside_git_dir = !prefixcmp(cwd + offset, ".git/");
-       return cwd + offset;
+       if (!strcmp(cwd, worktree))
+               return NULL;
+       return cwd+strlen(worktree);
 }
 
 int git_config_perm(const char *var, const char *value)
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
new file mode 100755 (executable)
index 0000000..ec49966
--- /dev/null
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='test git rev-parse'
+. ./test-lib.sh
+
+test_rev_parse() {
+       name=$1
+       shift
+
+       test_expect_success "$name: is-bare-repository" \
+       "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: is-inside-git-dir" \
+       "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: is-inside-work-tree" \
+       "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: prefix" \
+       "test '$1' = \"\$(git rev-parse --show-prefix)\""
+       shift
+       [ $# -eq 0 ] && return
+}
+
+test_rev_parse toplevel false false true ''
+
+cd .git || exit 1
+test_rev_parse .git/ false true true .git/
+cd objects || exit 1
+test_rev_parse .git/objects/ false true true .git/objects/
+cd ../.. || exit 1
+
+mkdir -p sub/dir || exit 1
+cd sub/dir || exit 1
+test_rev_parse subdirectory false false true sub/dir/
+cd ../.. || exit 1
+
+git config core.bare true
+test_rev_parse 'core.bare = true' true false true
+
+git config --unset core.bare
+test_rev_parse 'core.bare undefined' false false true
+
+mkdir work || exit 1
+cd work || exit 1
+export GIT_DIR=../.git
+export GIT_CONFIG="$GIT_DIR"/config
+
+git config core.bare false
+test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
+
+git config core.bare true
+test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false true ''
+
+git config --unset core.bare
+test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
+
+mv ../.git ../repo.git || exit 1
+export GIT_DIR=../repo.git
+export GIT_CONFIG="$GIT_DIR"/config
+
+git config core.bare false
+test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''
+
+git config core.bare true
+test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false true ''
+
+git config --unset core.bare
+test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' true false true ''
+
+test_done
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
new file mode 100755 (executable)
index 0000000..aadeeab
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='test separate work tree'
+. ./test-lib.sh
+
+test_rev_parse() {
+       name=$1
+       shift
+
+       test_expect_success "$name: is-bare-repository" \
+       "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: is-inside-git-dir" \
+       "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: is-inside-work-tree" \
+       "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: prefix" \
+       "test '$1' = \"\$(git rev-parse --show-prefix)\""
+       shift
+       [ $# -eq 0 ] && return
+}
+
+mkdir -p work/sub/dir || exit 1
+mv .git repo.git || exit 1
+
+say "core.worktree = relative path"
+export GIT_DIR=repo.git
+export GIT_CONFIG=$GIT_DIR/config
+unset GIT_WORK_TREE
+git config core.worktree ../work
+test_rev_parse 'outside'      false false false
+cd work || exit 1
+export GIT_DIR=../repo.git
+export GIT_CONFIG=$GIT_DIR/config
+test_rev_parse 'inside'       false false true ''
+cd sub/dir || exit 1
+export GIT_DIR=../../../repo.git
+export GIT_CONFIG=$GIT_DIR/config
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+say "core.worktree = absolute path"
+export GIT_DIR=$(pwd)/repo.git
+export GIT_CONFIG=$GIT_DIR/config
+git config core.worktree "$(pwd)/work"
+test_rev_parse 'outside'      false false false
+cd work || exit 1
+test_rev_parse 'inside'       false false true ''
+cd sub/dir || exit 1
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+say "GIT_WORK_TREE=relative path (override core.worktree)"
+export GIT_DIR=$(pwd)/repo.git
+export GIT_CONFIG=$GIT_DIR/config
+git config core.worktree non-existent
+export GIT_WORK_TREE=work
+test_rev_parse 'outside'      false false false
+cd work || exit 1
+export GIT_WORK_TREE=.
+test_rev_parse 'inside'       false false true ''
+cd sub/dir || exit 1
+export GIT_WORK_TREE=../..
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+mv work repo.git/work
+
+say "GIT_WORK_TREE=absolute path, work tree below git dir"
+export GIT_DIR=$(pwd)/repo.git
+export GIT_CONFIG=$GIT_DIR/config
+export GIT_WORK_TREE=$(pwd)/repo.git/work
+test_rev_parse 'outside'              false false false
+cd repo.git || exit 1
+test_rev_parse 'in repo.git'              false true  false
+cd objects || exit 1
+test_rev_parse 'in repo.git/objects'      false true  false
+cd ../work || exit 1
+test_rev_parse 'in repo.git/work'         false false true ''
+cd sub/dir || exit 1
+test_rev_parse 'in repo.git/sub/dir' false false true sub/dir/
+cd ../../../.. || exit 1
+
+test_done
index 8bf4cf49a207132d0f517468b6ca1182cca61aeb..78d7e87e86178b90a350714c0b287353a0de20b4 100644 (file)
@@ -26,6 +26,7 @@ GIT_COMMITTER_EMAIL=committer@example.com
 GIT_COMMITTER_NAME='C O Mitter'
 unset GIT_DIFF_OPTS
 unset GIT_DIR
+unset GIT_WORK_TREE
 unset GIT_EXTERNAL_DIFF
 unset GIT_INDEX_FILE
 unset GIT_OBJECT_DIRECTORY