This environment variable can be used with GIT_DIR to specify the toplevel working directory. When GIT_DIR is not set this variable is ignored. As for GIT_DIR there is also the option git --work-dir which overrides the environment variable. --- Missing: Documentation update but if this feature should not get accepted.. therefore I'll wait for feedback first. Idea: Add some way to configure tho working directory for one repository and set GIT_WORK_DIR automatically when GIT_DIR is used. I think of: * a subdirectory in the repository directory e.g. .git/work_dir which is supposed to be a symlink (or a textfile containing the path for windows compatibility?) or * a configuration variable Considerations: Without running setup_git_directory_gently is_bare_repository and is_inside_git_dir may return wrong values. Except for cmd_init_db I found no place calling on of the functions without calling setup_git_directory_gently before. To do: git init should probably set bare = false when GIT_WORK_DIR is exported. And if the idea about configurable working directories gets implemented it could also set this option accordingly. --- cache.h | 2 + environment.c | 2 + git.c | 12 +++++++++- setup.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 80 insertions(+), 6 deletions(-) diff --git a/cache.h b/cache.h index ae25759..042734c 100644 --- a/cache.h +++ b/cache.h @@ -144,6 +144,7 @@ enum object_type { }; #define GIT_DIR_ENVIRONMENT "GIT_DIR" +#define GIT_WORKING_DIR_ENVIRONMENT "GIT_WORK_DIR" #define DEFAULT_GIT_DIR_ENVIRONMENT ".git" #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY" #define INDEX_ENVIRONMENT "GIT_INDEX_FILE" @@ -164,6 +165,7 @@ extern char *get_graft_file(void); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" +extern int has_working_directory; extern const char **get_pathspec(const char *prefix, const char **pathspec); extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory(void); diff --git a/environment.c b/environment.c index 0151ad0..5c30c9b 100644 --- a/environment.c +++ b/environment.c @@ -61,6 +61,8 @@ int is_bare_repository(void) const char *dir, *s; if (0 <= is_bare_repository_cfg) return is_bare_repository_cfg; + if (0 <= has_working_directory) + return !has_working_directory; dir = get_git_dir(); if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT)) diff --git a/git.c b/git.c index 04fc99a..3cf7ce2 100644 --- 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-dir=GIT_WORK_DIR] [--help] COMMAND [ARGS]"; static void prepend_to_path(const char *dir, int len) { @@ -68,6 +68,16 @@ static int handle_options(const char*** argv, int* argc) (*argc)--; } else if (!prefixcmp(cmd, "--git-dir=")) { setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1); + } else if (!strcmp(cmd, "--work-dir")) { + if (*argc < 2) { + fprintf(stderr, "No directory given for --work-dir.\n" ); + usage(git_usage_string); + } + setenv(GIT_WORKING_DIR_ENVIRONMENT, (*argv)[1], 1); + (*argv)++; + (*argc)--; + } else if (!prefixcmp(cmd, "--work-dir=")) { + setenv(GIT_WORKING_DIR_ENVIRONMENT, cmd + 11, 1); } else if (!strcmp(cmd, "--bare")) { static char git_dir[PATH_MAX+1]; setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 1); diff --git a/setup.c b/setup.c index dda67d2..7f5d73b 100644 --- a/setup.c +++ b/setup.c @@ -192,6 +192,8 @@ int is_inside_git_dir(void) return inside_git_dir; } +int has_working_directory = -1; + const char *setup_git_directory_gently(int *nongit_ok) { static char cwd[PATH_MAX+1]; @@ -205,15 +207,73 @@ const char *setup_git_directory_gently(int *nongit_ok) */ gitdirenv = getenv(GIT_DIR_ENVIRONMENT); if (gitdirenv) { + struct stat st, st_work, st_git; + const char *gitwd; + char *prefix; + char c; + int len; + if (PATH_MAX - 40 < strlen(gitdirenv)) die("'$%s' too big", GIT_DIR_ENVIRONMENT); - if (is_git_directory(gitdirenv)) - return NULL; - if (nongit_ok) { - *nongit_ok = 1; + if (!is_git_directory(gitdirenv)) { + if (nongit_ok) { + *nongit_ok = 1; + return NULL; + } + die("Not a git repository: '%s'", gitdirenv); + } + + gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT); + if (!gitwd || stat(gitwd, &st_work)) return NULL; + if (inside_git_dir == -1 && stat(gitdirenv, &st_git)) + die("Unable to stat git directory"); + if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/') + die("Unable to read current working directory"); + len = strlen(cwd); + + prefix = cwd+len; + for (;;) { + c = *prefix; + *prefix = '\0'; + if (stat(cwd, &st)) + die("Unable to stat '%s'", cwd); + if (st_work.st_dev == st.st_dev && + st_work.st_ino == st.st_ino) + break; + if (inside_git_dir == -1 && + st_git.st_dev == st.st_dev && + st_git.st_ino == st.st_ino) + inside_git_dir = 1; + *prefix = c; + + if (prefix == cwd+1) { + has_working_directory = 0; + return NULL; + } + while (*(--prefix) != '/') + ; /* do nothing */ + if (prefix == cwd) + prefix++; + } + + if (chdir(cwd)) + die("Cannot change directory to '%s'", cwd); + + if (c) { + *prefix = c; + prefix++; + cwd[len] = '/'; + cwd[len+1] = '\0'; + } else { + prefix = NULL; } - die("Not a git repository: '%s'", gitdirenv); + + has_working_directory = 1; + if (inside_git_dir == -1) + inside_git_dir = 0; + + return prefix; } if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/') -- 1.5.0.3 - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html