X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=setup.c;h=145eca50f41d811c4c8fcb21ed2604e6b2971aba;hb=edb6ddc53e71f4f959665cbc3180bb21b7ee7634;hp=7b07144af7b0ea96eb2fcd65098331768e46ca3b;hpb=835252272e0192bd26983e22428480c5c89775fb;p=git.git diff --git a/setup.c b/setup.c index 7b07144af..145eca50f 100644 --- a/setup.c +++ b/setup.c @@ -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) { @@ -103,7 +107,7 @@ void verify_non_filename(const char *prefix, const char *arg) if (!lstat(name, &st)) die("ambiguous argument '%s': both revision and filename\n" "Use '--' to separate filenames from revisions", arg); - if (errno != ENOENT) + if (errno != ENOENT && errno != ENOTDIR) die("'%s': %s", arg, strerror(errno)); } @@ -170,100 +174,74 @@ 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) +/* + * set_work_tree() is only ever called if you set GIT_DIR explicitely. + * The old behaviour (which we retain here) is to set the work tree root + * to the cwd, unless overridden by the config, the command line, or + * GIT_WORK_TREE. + */ +static const char *set_work_tree(const char *dir) { - if (!strcmp(var, "core.worktree")) { - if (gitworktree_config) - strlcpy(gitworktree_config, value, PATH_MAX); - return 0; - } - return git_default_config(var, value); + char buffer[PATH_MAX + 1]; + + if (!getcwd(buffer, sizeof(buffer))) + die ("Could not get the current working directory"); + git_work_tree_cfg = xstrdup(buffer); + inside_work_tree = 1; + + return NULL; } +/* + * 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; + if (gitdirenv) { + if (PATH_MAX - 40 < strlen(gitdirenv)) + die("'$%s' too big", GIT_DIR_ENVIRONMENT); + if (is_git_directory(gitdirenv)) { + static char buffer[1024 + 1]; + const char *retval; + + if (!work_tree_env) + return set_work_tree(gitdirenv); + retval = get_relative_cwd(buffer, sizeof(buffer) - 1, + get_git_work_tree()); + if (!retval || !*retval) 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; + set_git_dir(make_absolute_path(gitdirenv)); + if (chdir(work_tree_env) < 0) + die ("Could not chdir to %s", work_tree_env); + strcat(buffer, "/"); + return retval; } - die("$%s too big", GIT_DIR_ENVIRONMENT); - } - if (!is_git_directory(gitdirenv)) { if (nongit_ok) { *nongit_ok = 1; return NULL; @@ -273,92 +251,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) @@ -382,11 +321,21 @@ int git_config_perm(const char *var, const char *value) int check_repository_format_version(const char *var, const char *value) { - if (strcmp(var, "core.repositoryformatversion") == 0) - repository_format_version = git_config_int(var, value); + if (strcmp(var, "core.repositoryformatversion") == 0) + repository_format_version = git_config_int(var, value); else if (strcmp(var, "core.sharedrepository") == 0) shared_repository = git_config_perm(var, value); - return 0; + 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; } int check_repository_format(void) @@ -402,5 +351,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; }