#include "config.h"
#include "dir.h"
#include "string-list.h"
+#include "chdir-notify.h"
static int inside_git_dir = -1;
static int inside_work_tree = -1;
off = offset_1st_component(path);
/* check if work tree is already the prefix */
- if (wtlen <= len && !strncmp(path, work_tree, wtlen)) {
+ if (wtlen <= len && !fspathncmp(path, work_tree, wtlen)) {
if (path[wtlen] == '/') {
memmove(path, path + wtlen + 1, len - wtlen);
return 0;
path++;
if (*path == '/') {
*path = '\0';
- if (strcmp(real_path(path0), work_tree) == 0) {
+ if (fspathcmp(real_path(path0), work_tree) == 0) {
memmove(path0, path + 1, len - (path - path0));
return 0;
}
}
/* check whole path */
- if (strcmp(real_path(path0), work_tree) == 0) {
+ if (fspathcmp(real_path(path0), work_tree) == 0) {
*path0 = '\0';
return 0;
}
{
char *r = prefix_path_gently(prefix, len, NULL, path);
if (!r)
- die("'%s' is outside repository", path);
+ die(_("'%s' is outside repository"), path);
return r;
}
free(to_free);
return 0; /* file does not exist */
}
- die_errno("failed to stat '%s'", arg);
+ die_errno(_("failed to stat '%s'"), arg);
}
static void NORETURN die_verify_filename(const char *prefix,
int diagnose_misspelt_rev)
{
if (*arg == '-')
- die("option '%s' must come before non-option arguments", arg);
+ die(_("option '%s' must come before non-option arguments"), arg);
if (looks_like_pathspec(arg) || check_filename(prefix, arg))
return;
die_verify_filename(prefix, arg, diagnose_misspelt_rev);
void setup_work_tree(void)
{
- const char *work_tree, *git_dir;
+ const char *work_tree;
static int initialized = 0;
if (initialized)
return;
if (work_tree_config_is_bogus)
- die("unable to set up work tree using invalid config");
+ die(_("unable to set up work tree using invalid config"));
work_tree = get_git_work_tree();
- git_dir = get_git_dir();
- if (!is_absolute_path(git_dir))
- git_dir = real_path(get_git_dir());
- if (!work_tree || chdir(work_tree))
- die("This operation must be run in a work tree");
+ if (!work_tree || chdir_notify(work_tree))
+ die(_("this operation must be run in a work tree"));
/*
* Make sure subsequent git processes find correct worktree
if (getenv(GIT_WORK_TREE_ENVIRONMENT))
setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
- set_git_dir(remove_leading_path(git_dir, work_tree));
initialized = 1;
}
+static int read_worktree_config(const char *var, const char *value, void *vdata)
+{
+ struct repository_format *data = vdata;
+
+ if (strcmp(var, "core.bare") == 0) {
+ data->is_bare = git_config_bool(var, value);
+ } else if (strcmp(var, "core.worktree") == 0) {
+ if (!value)
+ return config_error_nonbool(var);
+ free(data->work_tree);
+ data->work_tree = xstrdup(value);
+ }
+ return 0;
+}
+
static int check_repo_format(const char *var, const char *value, void *vdata)
{
struct repository_format *data = vdata;
;
else if (!strcmp(ext, "preciousobjects"))
data->precious_objects = git_config_bool(var, value);
+ else if (!strcmp(ext, "partialclone")) {
+ if (!value)
+ return config_error_nonbool(var);
+ data->partial_clone = xstrdup(value);
+ } else if (!strcmp(ext, "worktreeconfig"))
+ data->worktree_config = git_config_bool(var, value);
else
string_list_append(&data->unknown_extensions, ext);
- } else if (strcmp(var, "core.bare") == 0) {
- data->is_bare = git_config_bool(var, value);
- } else if (strcmp(var, "core.worktree") == 0) {
- if (!value)
- return config_error_nonbool(var);
- data->work_tree = xstrdup(value);
}
- return 0;
+
+ return read_worktree_config(var, value, vdata);
}
static int check_repository_format_gently(const char *gitdir, struct repository_format *candidate, int *nongit_ok)
}
repository_format_precious_objects = candidate->precious_objects;
+ repository_format_partial_clone = xstrdup_or_null(candidate->partial_clone);
+ repository_format_worktree_config = candidate->worktree_config;
string_list_clear(&candidate->unknown_extensions, 0);
+
+ if (repository_format_worktree_config) {
+ /*
+ * pick up core.bare and core.worktree from per-worktree
+ * config if present
+ */
+ strbuf_addf(&sb, "%s/config.worktree", gitdir);
+ git_config_from_file(read_worktree_config, sb.buf, candidate);
+ strbuf_release(&sb);
+ has_common = 0;
+ }
+
if (!has_common) {
if (candidate->is_bare != -1) {
is_bare_repository_cfg = candidate->is_bare;
}
if (candidate->work_tree) {
free(git_work_tree_cfg);
- git_work_tree_cfg = candidate->work_tree;
+ git_work_tree_cfg = xstrdup(candidate->work_tree);
inside_work_tree = -1;
}
- } else {
- free(candidate->work_tree);
}
return 0;
}
+static void init_repository_format(struct repository_format *format)
+{
+ const struct repository_format fresh = REPOSITORY_FORMAT_INIT;
+
+ memcpy(format, &fresh, sizeof(fresh));
+}
+
int read_repository_format(struct repository_format *format, const char *path)
{
- memset(format, 0, sizeof(*format));
- format->version = -1;
- format->is_bare = -1;
- format->hash_algo = GIT_HASH_SHA1;
- string_list_init(&format->unknown_extensions, 1);
+ clear_repository_format(format);
git_config_from_file(check_repo_format, path, format);
+ if (format->version == -1)
+ clear_repository_format(format);
return format->version;
}
+void clear_repository_format(struct repository_format *format)
+{
+ string_list_clear(&format->unknown_extensions, 0);
+ free(format->work_tree);
+ free(format->partial_clone);
+ init_repository_format(format);
+}
+
int verify_repository_format(const struct repository_format *format,
struct strbuf *err)
{
/* non-fatal; follow return path */
break;
case READ_GITFILE_ERR_OPEN_FAILED:
- die_errno("Error opening '%s'", path);
+ die_errno(_("error opening '%s'"), path);
case READ_GITFILE_ERR_TOO_LARGE:
- die("Too large to be a .git file: '%s'", path);
+ die(_("too large to be a .git file: '%s'"), path);
case READ_GITFILE_ERR_READ_FAILED:
- die("Error reading %s", path);
+ die(_("error reading %s"), path);
case READ_GITFILE_ERR_INVALID_FORMAT:
- die("Invalid gitfile format: %s", path);
+ die(_("invalid gitfile format: %s"), path);
case READ_GITFILE_ERR_NO_PATH:
- die("No path in gitfile: %s", path);
+ die(_("no path in gitfile: %s"), path);
case READ_GITFILE_ERR_NOT_A_REPO:
- die("Not a git repository: %s", dir);
+ die(_("not a git repository: %s"), dir);
default:
- die("BUG: unknown error code");
+ BUG("unknown error code");
}
}
int offset;
if (PATH_MAX - 40 < strlen(gitdirenv))
- die("'$%s' too big", GIT_DIR_ENVIRONMENT);
+ die(_("'$%s' too big"), GIT_DIR_ENVIRONMENT);
gitfile = (char*)read_gitfile(gitdirenv);
if (gitfile) {
free(gitfile);
return NULL;
}
- die("Not a git repository: '%s'", gitdirenv);
+ die(_("not a git repository: '%s'"), gitdirenv);
}
if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
else {
char *core_worktree;
if (chdir(gitdirenv))
- die_errno("Could not chdir to '%s'", gitdirenv);
+ die_errno(_("cannot chdir to '%s'"), gitdirenv);
if (chdir(git_work_tree_cfg))
- die_errno("Could not chdir to '%s'", git_work_tree_cfg);
+ die_errno(_("cannot chdir to '%s'"), git_work_tree_cfg);
core_worktree = xgetcwd();
if (chdir(cwd->buf))
- die_errno("Could not come back to cwd");
+ die_errno(_("cannot come back to cwd"));
set_git_work_tree(core_worktree);
free(core_worktree);
}
if (offset >= 0) { /* cwd inside worktree? */
set_git_dir(real_path(gitdirenv));
if (chdir(worktree))
- die_errno("Could not chdir to '%s'", worktree);
+ die_errno(_("cannot chdir to '%s'"), worktree);
strbuf_addch(cwd, '/');
free(gitfile);
return cwd->buf + offset;
if (offset != cwd->len && !is_absolute_path(gitdir))
gitdir = to_free = real_pathdup(gitdir, 1);
if (chdir(cwd->buf))
- die_errno("Could not come back to cwd");
+ die_errno(_("cannot come back to cwd"));
ret = setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
free(to_free);
return ret;
if (is_bare_repository_cfg > 0) {
set_git_dir(offset == cwd->len ? gitdir : real_path(gitdir));
if (chdir(cwd->buf))
- die_errno("Could not come back to cwd");
+ die_errno(_("cannot come back to cwd"));
return NULL;
}
gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
if (chdir(cwd->buf))
- die_errno("Could not come back to cwd");
+ die_errno(_("cannot come back to cwd"));
return setup_explicit_git_dir(gitdir, cwd, repo_fmt, nongit_ok);
}
inside_work_tree = 0;
if (offset != cwd->len) {
if (chdir(cwd->buf))
- die_errno("Cannot come back to cwd");
+ die_errno(_("cannot come back to cwd"));
root_len = offset_1st_component(cwd->buf);
strbuf_setlen(cwd, offset > root_len ? offset : root_len);
set_git_dir(cwd->buf);
return NULL;
}
-static const char *setup_nongit(const char *cwd, int *nongit_ok)
-{
- if (!nongit_ok)
- die(_("Not a git repository (or any of the parent directories): %s"), DEFAULT_GIT_DIR_ENVIRONMENT);
- if (chdir(cwd))
- die_errno(_("Cannot come back to cwd"));
- *nongit_ok = 1;
- return NULL;
-}
-
static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
{
struct stat buf;
if (stat(path, &buf)) {
- die_errno("failed to stat '%*s%s%s'",
+ die_errno(_("failed to stat '%*s%s%s'"),
prefix_len,
prefix ? prefix : "",
prefix ? "/" : "", path);
struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
size_t gitdir_offset = gitdir->len, cwd_len;
size_t commondir_offset = commondir->len;
- struct repository_format candidate;
+ struct repository_format candidate = REPOSITORY_FORMAT_INIT;
if (strbuf_getcwd(&dir))
return -1;
strbuf_release(&err);
strbuf_setlen(commondir, commondir_offset);
strbuf_setlen(gitdir, gitdir_offset);
+ clear_repository_format(&candidate);
return -1;
}
+ clear_repository_format(&candidate);
return 0;
}
{
static struct strbuf cwd = STRBUF_INIT;
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT;
- const char *prefix;
- struct repository_format repo_fmt;
+ const char *prefix = NULL;
+ struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
/*
* We may have read an incomplete configuration before
strbuf_addbuf(&dir, &cwd);
switch (setup_git_directory_gently_1(&dir, &gitdir, 1)) {
- case GIT_DIR_NONE:
- prefix = NULL;
- break;
case GIT_DIR_EXPLICIT:
prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok);
break;
case GIT_DIR_DISCOVERED:
if (dir.len < cwd.len && chdir(dir.buf))
- die(_("Cannot change to '%s'"), dir.buf);
+ die(_("cannot change to '%s'"), dir.buf);
prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
&repo_fmt, nongit_ok);
break;
case GIT_DIR_BARE:
if (dir.len < cwd.len && chdir(dir.buf))
- die(_("Cannot change to '%s'"), dir.buf);
+ die(_("cannot change to '%s'"), dir.buf);
prefix = setup_bare_git_dir(&cwd, dir.len, &repo_fmt, nongit_ok);
break;
case GIT_DIR_HIT_CEILING:
- prefix = setup_nongit(cwd.buf, nongit_ok);
+ if (!nongit_ok)
+ die(_("not a git repository (or any of the parent directories): %s"),
+ DEFAULT_GIT_DIR_ENVIRONMENT);
+ *nongit_ok = 1;
break;
case GIT_DIR_HIT_MOUNT_POINT:
- if (nongit_ok) {
- *nongit_ok = 1;
- strbuf_release(&cwd);
- strbuf_release(&dir);
- return NULL;
- }
- die(_("Not a git repository (or any parent up to mount point %s)\n"
- "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
- dir.buf);
+ if (!nongit_ok)
+ die(_("not a git repository (or any parent up to mount point %s)\n"
+ "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
+ dir.buf);
+ *nongit_ok = 1;
+ break;
+ case GIT_DIR_NONE:
+ /*
+ * As a safeguard against setup_git_directory_gently_1 returning
+ * this value, fallthrough to BUG. Otherwise it is possible to
+ * set startup_info->have_repository to 1 when we did nothing to
+ * find a repository.
+ */
default:
- die("BUG: unhandled setup_git_directory_1() result");
+ BUG("unhandled setup_git_directory_1() result");
}
- if (prefix)
- setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
- else
+ /*
+ * At this point, nongit_ok is stable. If it is non-NULL and points
+ * to a non-zero value, then this means that we haven't found a
+ * repository and that the caller expects startup_info to reflect
+ * this.
+ *
+ * Regardless of the state of nongit_ok, startup_info->prefix and
+ * the GIT_PREFIX environment variable must always match. For details
+ * see Documentation/config/alias.txt.
+ */
+ if (nongit_ok && *nongit_ok) {
+ startup_info->have_repository = 0;
+ startup_info->prefix = NULL;
setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
-
- startup_info->have_repository = !nongit_ok || !*nongit_ok;
- startup_info->prefix = prefix;
+ } else {
+ startup_info->have_repository = 1;
+ startup_info->prefix = prefix;
+ if (prefix)
+ setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
+ else
+ setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
+ }
/*
* Not all paths through the setup code will call 'set_git_dir()' (which
* the user has set GIT_DIR. It may be beneficial to disallow bogus
* GIT_DIR values at some point in the future.
*/
- if (startup_info->have_repository || getenv(GIT_DIR_ENVIRONMENT)) {
+ if (/* GIT_DIR_EXPLICIT, GIT_DIR_DISCOVERED, GIT_DIR_BARE */
+ startup_info->have_repository ||
+ /* GIT_DIR_EXPLICIT */
+ getenv(GIT_DIR_ENVIRONMENT)) {
if (!the_repository->gitdir) {
const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
if (!gitdir)
gitdir = DEFAULT_GIT_DIR_ENVIRONMENT;
- repo_set_gitdir(the_repository, gitdir);
- setup_git_env();
+ setup_git_env(gitdir);
}
if (startup_info->have_repository)
repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
strbuf_release(&dir);
strbuf_release(&gitdir);
+ clear_repository_format(&repo_fmt);
return prefix;
}
/* A filemode value was given: 0xxx */
if ((i & 0600) != 0600)
- die(_("Problem with core.sharedRepository filemode value "
+ die(_("problem with core.sharedRepository filemode value "
"(0%.3o).\nThe owner of files must always have "
"read and write permissions."), i);
void check_repository_format(void)
{
- struct repository_format repo_fmt;
+ struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
check_repository_format_gently(get_git_dir(), &repo_fmt, NULL);
startup_info->have_repository = 1;
+ clear_repository_format(&repo_fmt);
}
/*
while (fd != -1 && fd < 2)
fd = dup(fd);
if (fd == -1)
- die_errno("open /dev/null or dup failed");
+ die_errno(_("open /dev/null or dup failed"));
if (fd > 2)
close(fd);
}
case 0:
break;
case -1:
- die_errno("fork failed");
+ die_errno(_("fork failed"));
default:
exit(0);
}
if (setsid() == -1)
- die_errno("setsid failed");
+ die_errno(_("setsid failed"));
close(0);
close(1);
close(2);