diff options
-rw-r--r-- | Documentation/config.txt | 7 | ||||
-rw-r--r-- | Documentation/git-rev-parse.txt | 4 | ||||
-rw-r--r-- | Documentation/git.txt | 18 | ||||
-rw-r--r-- | builtin-rev-parse.c | 5 | ||||
-rw-r--r-- | cache.h | 2 | ||||
-rw-r--r-- | connect.c | 1 | ||||
-rw-r--r-- | git.c | 12 | ||||
-rw-r--r-- | setup.c | 211 | ||||
-rw-r--r-- | t/test-lib.sh | 1 |
9 files changed, 204 insertions, 57 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt index 5868d587a..4d0bd37af 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -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 diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index c817d1614..6e4d15829 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -93,6 +93,10 @@ OPTIONS 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". diff --git a/Documentation/git.txt b/Documentation/git.txt index 98860af04..4b567d802 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -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 ----------- @@ -101,6 +102,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`. @@ -345,6 +354,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':: diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 71d516259..497903a85 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -352,6 +352,11 @@ 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"); @@ -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); @@ -589,6 +589,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); @@ -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); @@ -174,41 +174,93 @@ 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); + } + + 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 +270,90 @@ 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 (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/test-lib.sh b/t/test-lib.sh index dee3ad762..b61e1d598 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -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 |