diff options
Diffstat (limited to 'setup.c')
-rw-r--r-- | setup.c | 279 |
1 files changed, 127 insertions, 152 deletions
@@ -1,4 +1,8 @@ #include "cache.h" +#include "dir.h" + +static int inside_git_dir = -1; +static int inside_work_tree = -1; const char *prefix_path(const char *prefix, int len, const char *path) { @@ -170,100 +174,89 @@ static int is_git_directory(const char *suspect) return 1; } -static int inside_git_dir = -1; - int is_inside_git_dir(void) { - if (inside_git_dir >= 0) - return inside_git_dir; - die("BUG: is_inside_git_dir called before setup_git_directory"); + if (inside_git_dir < 0) + inside_git_dir = is_inside_dir(get_git_dir()); + return inside_git_dir; } -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"); + if (inside_work_tree < 0) + inside_work_tree = is_inside_dir(get_git_work_tree()); + return inside_work_tree; } -static char *gitworktree_config; - -static int git_setup_config(const char *var, const char *value) +/* + * If no worktree was given, and we are outside of a default work tree, + * now is the time to set it. + * + * In other words, if the user calls git with something like + * + * git --git-dir=/some/where/else/.git bla + * + * default to /some/where/else as working directory; if the specified + * git-dir does not end in "/.git", the cwd is used as working directory. + */ +const char *set_work_tree(const char *dir) { - if (!strcmp(var, "core.worktree")) { - if (gitworktree_config) - strlcpy(gitworktree_config, value, PATH_MAX); - return 0; + char dir_buffer[PATH_MAX]; + static char buffer[PATH_MAX + 1], *rel = NULL; + int len, postfix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1; + + /* strip the variable 'dir' of the postfix "/.git" if it has it */ + len = strlen(dir); + if (len > postfix_len && !strcmp(dir + len - postfix_len, + "/" DEFAULT_GIT_DIR_ENVIRONMENT)) { + strncpy(dir_buffer, dir, len - postfix_len); + + /* are we inside the default work tree? */ + rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer); + } + /* if rel is set, the cwd is _not_ the current working tree */ + if (rel && *rel) { + if (!is_absolute_path(dir)) + set_git_dir(make_absolute_path(dir)); + dir = dir_buffer; + chdir(dir); + strcat(rel, "/"); + inside_git_dir = 0; + } else { + rel = NULL; + dir = getcwd(buffer, sizeof(buffer)); } - return git_default_config(var, value); + git_work_tree_cfg = xstrdup(dir); + inside_work_tree = 1; + + return rel; } +/* + * We cannot decide in this function whether we are in the work tree or + * not, since the config can only be read _after_ this function was called. + */ const char *setup_git_directory_gently(int *nongit_ok) { + const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); static char cwd[PATH_MAX+1]; - char worktree[PATH_MAX+1], gitdir[PATH_MAX+1]; - const char *gitdirenv, *gitworktree; - int wt_rel_gitdir = 0; + const char *gitdirenv; + int len, offset; + /* + * 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) { - int len, offset; - - if (!getcwd(cwd, sizeof(cwd)-1)) - 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; + if (gitdirenv) { + if (PATH_MAX - 40 < strlen(gitdirenv)) + die("'$%s' too big", GIT_DIR_ENVIRONMENT); + if (is_git_directory(gitdirenv)) { + if (!work_tree_env) + return set_work_tree(gitdirenv); return NULL; } - die("$%s too big", GIT_DIR_ENVIRONMENT); - } - if (!is_git_directory(gitdirenv)) { if (nongit_ok) { *nongit_ok = 1; return NULL; @@ -273,92 +266,53 @@ const char *setup_git_directory_gently(int *nongit_ok) if (!getcwd(cwd, sizeof(cwd)-1)) 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)) - die("Unable to read current working directory"); - if (chdir(cwd)) - die("Cannot come back to cwd"); /* - * In case there is a work tree we may change the directory, - * therefore make GIT_DIR an absolute path. + * Test in the following order (relative to the cwd): + * - .git/ + * - ./ (bare) + * - ../.git/ + * - ../ (bare) + * - ../../.git/ + * etc. */ - 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; - } - 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; + offset = len = strlen(cwd); + for (;;) { + if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) + break; + if (is_git_directory(".")) { + inside_git_dir = 1; + if (!work_tree_env) + inside_work_tree = 0; + setenv(GIT_DIR_ENVIRONMENT, ".", 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)) - die("Unable to read current working directory"); - strcat(worktree, "/"); - inside_work_tree = !prefixcmp(cwd, worktree); - - if (gitworktree && inside_work_tree && !prefixcmp(worktree, gitdir) && - strcmp(worktree, gitdir)) { - inside_git_dir = 0; + chdir(".."); + do { + if (!offset) { + if (nongit_ok) { + if (chdir(cwd)) + die("Cannot come back to cwd"); + *nongit_ok = 1; + return NULL; + } + die("Not a git repository"); + } + } while (cwd[--offset] != '/'); } - if (!inside_work_tree) { - if (chdir(cwd)) - die("Cannot come back to cwd"); + inside_git_dir = 0; + if (!work_tree_env) + inside_work_tree = 1; + git_work_tree_cfg = xstrndup(cwd, offset); + if (offset == len) return NULL; - } - if (!strcmp(cwd, worktree)) - return NULL; - return cwd+strlen(worktree); + /* Make "offset" point to past the '/', and add a '/' at the end */ + offset++; + cwd[len++] = '/'; + cwd[len] = 0; + return cwd + offset; } int git_config_perm(const char *var, const char *value) @@ -386,6 +340,16 @@ int check_repository_format_version(const char *var, const char *value) repository_format_version = git_config_int(var, value); else if (strcmp(var, "core.sharedrepository") == 0) shared_repository = git_config_perm(var, value); + else if (strcmp(var, "core.bare") == 0) { + is_bare_repository_cfg = git_config_bool(var, value); + if (is_bare_repository_cfg == 1) + inside_work_tree = -1; + } else if (strcmp(var, "core.worktree") == 0) { + if (git_work_tree_cfg) + free(git_work_tree_cfg); + git_work_tree_cfg = xstrdup(value); + inside_work_tree = -1; + } return 0; } @@ -402,5 +366,16 @@ const char *setup_git_directory(void) { const char *retval = setup_git_directory_gently(NULL); check_repository_format(); + + /* If the work tree is not the default one, recompute prefix */ + if (inside_work_tree < 0) { + static char buffer[PATH_MAX + 1]; + char *rel; + if (retval && chdir(retval)) + die ("Could not jump back into original cwd"); + rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree()); + return rel && *rel ? strcat(rel, "/") : NULL; + } + return retval; } |