/* * 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. */ static const char *setup_git_directory_gently_1(int *nongit_ok) { const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; static struct strbuf cwd = STRBUF_INIT; const char *gitdirenv, *ret; char *gitfile; int offset, offset_parent, ceil_offset = -1; dev_t current_device = 0; int one_filesystem = 1; /* * We may have read an incomplete configuration before * setting-up the git directory. If so, clear the cache so * that the next queries to the configuration reload complete * configuration (including the per-repo config file that we * ignored previously). */ git_config_clear(); /* * Let's assume that we are in a git repository. * If it turns out later that we are somewhere else, the value will be * updated accordingly. */ if (nongit_ok) *nongit_ok = 0; if (strbuf_getcwd(&cwd)) die_errno("Unable to read current working directory"); offset = cwd.len; /* * 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) return setup_explicit_git_dir(gitdirenv, &cwd, nongit_ok); if (env_ceiling_dirs) { int empty_entry_found = 0; string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1); filter_string_list(&ceiling_dirs, 0, canonicalize_ceiling_entry, &empty_entry_found); ceil_offset = longest_ancestor_length(cwd.buf, &ceiling_dirs); string_list_clear(&ceiling_dirs, 0); } if (ceil_offset < 0 && has_dos_drive_prefix(cwd.buf)) ceil_offset = 1; /* * Test in the following order (relative to the cwd): * - .git (file containing "gitdir: <path>") * - .git/ * - ./ (bare) * - ../.git * - ../.git/ * - ../ (bare) * - ../../.git/ * etc. */ one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0); if (one_filesystem) current_device = get_device_or_die(".", NULL, 0); for (;;) { gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT); if (gitfile) gitdirenv = gitfile = xstrdup(gitfile); else { if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT; } if (gitdirenv) { ret = setup_discovered_git_dir(gitdirenv, &cwd, offset, nongit_ok); free(gitfile); return ret; } free(gitfile); if (is_git_directory(".")) return setup_bare_git_dir(&cwd, offset, nongit_ok); offset_parent = offset; while (--offset_parent > ceil_offset && cwd.buf[offset_parent] != '/'); if (offset_parent <= ceil_offset) return setup_nongit(cwd.buf, nongit_ok); if (one_filesystem) { dev_t parent_device = get_device_or_die("..", cwd.buf, offset); if (parent_device != current_device) { if (nongit_ok) { if (chdir(cwd.buf)) die_errno("Cannot come back to cwd"); *nongit_ok = 1; return NULL; } strbuf_setlen(&cwd, offset); die("Not a git repository (or any parent up to mount point %s)\n" "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd.buf); } } if (chdir("..")) { strbuf_setlen(&cwd, offset); die_errno("Cannot change to '%s/..'", cwd.buf); } offset = offset_parent; } }
/* * Return the real path (i.e., absolute path, with symlinks resolved * and extra slashes removed) equivalent to the specified path. (If * you want an absolute path but don't mind links, use * absolute_path().) The return value is a pointer to a static * buffer. * * The input and all intermediate paths must be shorter than MAX_PATH. * The directory part of path (i.e., everything up to the last * dir_sep) must denote a valid, existing directory, but the last * component need not exist. If die_on_error is set, then die with an * informative error message if there is a problem. Otherwise, return * NULL on errors (without generating any output). * * If path is our buffer, then return path, as it's already what the * user wants. */ static const char *real_path_internal(const char *path, int die_on_error) { static struct strbuf sb = STRBUF_INIT; char *retval = NULL; /* * If we have to temporarily chdir(), store the original CWD * here so that we can chdir() back to it at the end of the * function: */ struct strbuf cwd = STRBUF_INIT; int depth = MAXDEPTH; char *last_elem = NULL; struct stat st; /* We've already done it */ if (path == sb.buf) return path; if (!*path) { if (die_on_error) die("The empty string is not a valid path"); else goto error_out; } strbuf_reset(&sb); strbuf_addstr(&sb, path); while (depth--) { if (!is_directory(sb.buf)) { char *last_slash = find_last_dir_sep(sb.buf); if (last_slash) { last_elem = xstrdup(last_slash + 1); strbuf_setlen(&sb, last_slash - sb.buf + 1); } else { last_elem = xmemdupz(sb.buf, sb.len); strbuf_reset(&sb); } } if (sb.len) { if (!cwd.len && strbuf_getcwd(&cwd)) { if (die_on_error) die_errno("Could not get current working directory"); else goto error_out; } if (chdir(sb.buf)) { if (die_on_error) die_errno("Could not switch to '%s'", sb.buf); else goto error_out; } } if (strbuf_getcwd(&sb)) { if (die_on_error) die_errno("Could not get current working directory"); else goto error_out; } if (last_elem) { if (sb.len && !is_dir_sep(sb.buf[sb.len - 1])) strbuf_addch(&sb, '/'); strbuf_addstr(&sb, last_elem); free(last_elem); last_elem = NULL; } if (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) { struct strbuf next_sb = STRBUF_INIT; ssize_t len = strbuf_readlink(&next_sb, sb.buf, 0); if (len < 0) { if (die_on_error) die_errno("Invalid symlink '%s'", sb.buf); else goto error_out; } strbuf_swap(&sb, &next_sb); strbuf_release(&next_sb); } else break; } retval = sb.buf; error_out: free(last_elem); if (cwd.len && chdir(cwd.buf)) die_errno("Could not change back to '%s'", cwd.buf); strbuf_release(&cwd); return retval; }