char *path0;
int off;
const char *work_tree = get_git_work_tree();
+ struct strbuf realpath = STRBUF_INIT;
if (!work_tree)
return -1;
path++;
if (*path == '/') {
*path = '\0';
- if (fspathcmp(real_path(path0), work_tree) == 0) {
+ strbuf_realpath(&realpath, path0, 1);
+ if (fspathcmp(realpath.buf, work_tree) == 0) {
memmove(path0, path + 1, len - (path - path0));
+ strbuf_release(&realpath);
return 0;
}
*path = '/';
}
/* check whole path */
- if (fspathcmp(real_path(path0), work_tree) == 0) {
+ strbuf_realpath(&realpath, path0, 1);
+ if (fspathcmp(realpath.buf, work_tree) == 0) {
*path0 = '\0';
+ strbuf_release(&realpath);
return 0;
}
+ strbuf_release(&realpath);
return -1;
}
char *prefix_path(const char *prefix, int len, const char *path)
{
char *r = prefix_path_gently(prefix, len, NULL, path);
- if (!r)
- die(_("'%s' is outside repository"), path);
+ if (!r) {
+ const char *hint_path = get_git_work_tree();
+ if (!hint_path)
+ hint_path = get_git_dir();
+ die(_("'%s' is outside repository at '%s'"), path,
+ absolute_path(hint_path));
+ }
return r;
}
*/
static int looks_like_pathspec(const char *arg)
{
- /* anything with a wildcard character */
- if (!no_wildcard(arg))
- return 1;
+ const char *p;
+ int escaped = 0;
+
+ /*
+ * Wildcard characters imply the user is looking to match pathspecs
+ * that aren't in the filesystem. Note that this doesn't include
+ * backslash even though it's a glob special; by itself it doesn't
+ * cause any increase in the match. Likewise ignore backslash-escaped
+ * wildcard characters.
+ */
+ for (p = arg; *p; p++) {
+ if (escaped) {
+ escaped = 0;
+ } else if (is_glob_special(*p)) {
+ if (*p == '\\')
+ escaped = 1;
+ else
+ return 1;
+ }
+ }
/* long-form pathspec magic */
if (starts_with(arg, ":("))
return 0;
}
+enum extension_result {
+ EXTENSION_ERROR = -1, /* compatible with error(), etc */
+ EXTENSION_UNKNOWN = 0,
+ EXTENSION_OK = 1
+};
+
+/*
+ * Do not add new extensions to this function. It handles extensions which are
+ * respected even in v0-format repositories for historical compatibility.
+ */
+static enum extension_result handle_extension_v0(const char *var,
+ const char *value,
+ const char *ext,
+ struct repository_format *data)
+{
+ if (!strcmp(ext, "noop")) {
+ return EXTENSION_OK;
+ } else if (!strcmp(ext, "preciousobjects")) {
+ data->precious_objects = git_config_bool(var, value);
+ return EXTENSION_OK;
+ } else if (!strcmp(ext, "partialclone")) {
+ if (!value)
+ return config_error_nonbool(var);
+ data->partial_clone = xstrdup(value);
+ return EXTENSION_OK;
+ } else if (!strcmp(ext, "worktreeconfig")) {
+ data->worktree_config = git_config_bool(var, value);
+ return EXTENSION_OK;
+ }
+
+ return EXTENSION_UNKNOWN;
+}
+
+/*
+ * Record any new extensions in this function.
+ */
+static enum extension_result handle_extension(const char *var,
+ const char *value,
+ const char *ext,
+ struct repository_format *data)
+{
+ if (!strcmp(ext, "noop-v1")) {
+ return EXTENSION_OK;
+ } else if (!strcmp(ext, "objectformat")) {
+ int format;
+
+ if (!value)
+ return config_error_nonbool(var);
+ format = hash_algo_by_name(value);
+ if (format == GIT_HASH_UNKNOWN)
+ return error("invalid value for 'extensions.objectformat'");
+ data->hash_algo = format;
+ return EXTENSION_OK;
+ }
+ return EXTENSION_UNKNOWN;
+}
+
static int check_repo_format(const char *var, const char *value, void *vdata)
{
struct repository_format *data = vdata;
if (strcmp(var, "core.repositoryformatversion") == 0)
data->version = git_config_int(var, value);
else if (skip_prefix(var, "extensions.", &ext)) {
- /*
- * record any known extensions here; otherwise,
- * we fall through to recording it as unknown, and
- * check_repository_format will complain
- */
- if (!strcmp(ext, "noop"))
- ;
- 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
+ switch (handle_extension_v0(var, value, ext, data)) {
+ case EXTENSION_ERROR:
+ return -1;
+ case EXTENSION_OK:
+ return 0;
+ case EXTENSION_UNKNOWN:
+ break;
+ }
+
+ switch (handle_extension(var, value, ext, data)) {
+ case EXTENSION_ERROR:
+ return -1;
+ case EXTENSION_OK:
+ string_list_append(&data->v1_only_extensions, ext);
+ return 0;
+ case EXTENSION_UNKNOWN:
string_list_append(&data->unknown_extensions, ext);
+ return 0;
+ }
}
return read_worktree_config(var, value, vdata);
set_repository_format_partial_clone(candidate->partial_clone);
repository_format_worktree_config = candidate->worktree_config;
string_list_clear(&candidate->unknown_extensions, 0);
+ string_list_clear(&candidate->v1_only_extensions, 0);
if (repository_format_worktree_config) {
/*
return 0;
}
+int upgrade_repository_format(int target_version)
+{
+ struct strbuf sb = STRBUF_INIT;
+ struct strbuf err = STRBUF_INIT;
+ struct strbuf repo_version = STRBUF_INIT;
+ struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
+
+ strbuf_git_common_path(&sb, the_repository, "config");
+ read_repository_format(&repo_fmt, sb.buf);
+ strbuf_release(&sb);
+
+ if (repo_fmt.version >= target_version)
+ return 0;
+
+ if (verify_repository_format(&repo_fmt, &err) < 0) {
+ error("cannot upgrade repository format from %d to %d: %s",
+ repo_fmt.version, target_version, err.buf);
+ strbuf_release(&err);
+ return -1;
+ }
+ if (!repo_fmt.version && repo_fmt.unknown_extensions.nr)
+ return error("cannot upgrade repository format: "
+ "unknown extension %s",
+ repo_fmt.unknown_extensions.items[0].string);
+
+ strbuf_addf(&repo_version, "%d", target_version);
+ git_config_set("core.repositoryformatversion", repo_version.buf);
+ strbuf_release(&repo_version);
+ return 1;
+}
+
static void init_repository_format(struct repository_format *format)
{
const struct repository_format fresh = REPOSITORY_FORMAT_INIT;
void clear_repository_format(struct repository_format *format)
{
string_list_clear(&format->unknown_extensions, 0);
+ string_list_clear(&format->v1_only_extensions, 0);
free(format->work_tree);
free(format->partial_clone);
init_repository_format(format);
return -1;
}
+ if (format->version == 0 && format->v1_only_extensions.nr) {
+ int i;
+
+ strbuf_addstr(err,
+ _("repo version is 0, but v1-only extensions found:"));
+
+ for (i = 0; i < format->v1_only_extensions.nr; i++)
+ strbuf_addf(err, "\n\t%s",
+ format->v1_only_extensions.items[i].string);
+ return -1;
+ }
+
return 0;
}
struct stat st;
int fd;
ssize_t len;
+ static struct strbuf realpath = STRBUF_INIT;
if (stat(path, &st)) {
/* NEEDSWORK: discern between ENOENT vs other errors */
error_code = READ_GITFILE_ERR_NOT_A_REPO;
goto cleanup_return;
}
- path = real_path(dir);
+
+ strbuf_realpath(&realpath, dir, 1);
+ path = realpath.buf;
cleanup_return:
if (return_error_code)
}
/* #18, #26 */
- set_git_dir(gitdirenv);
+ set_git_dir(gitdirenv, 0);
free(gitfile);
return NULL;
}
}
else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) {
/* #16d */
- set_git_dir(gitdirenv);
+ set_git_dir(gitdirenv, 0);
free(gitfile);
return NULL;
}
/* both get_git_work_tree() and cwd are already normalized */
if (!strcmp(cwd->buf, worktree)) { /* cwd == worktree */
- set_git_dir(gitdirenv);
+ set_git_dir(gitdirenv, 0);
free(gitfile);
return NULL;
}
offset = dir_inside_of(cwd->buf, worktree);
if (offset >= 0) { /* cwd inside worktree? */
- set_git_dir(real_path(gitdirenv));
+ set_git_dir(gitdirenv, 1);
if (chdir(worktree))
die_errno(_("cannot chdir to '%s'"), worktree);
strbuf_addch(cwd, '/');
}
/* cwd outside worktree */
- set_git_dir(gitdirenv);
+ set_git_dir(gitdirenv, 0);
free(gitfile);
return NULL;
}
/* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
if (is_bare_repository_cfg > 0) {
- set_git_dir(offset == cwd->len ? gitdir : real_path(gitdir));
+ set_git_dir(gitdir, (offset != cwd->len));
if (chdir(cwd->buf))
die_errno(_("cannot come back to cwd"));
return NULL;
/* #0, #1, #5, #8, #9, #12, #13 */
set_git_work_tree(".");
if (strcmp(gitdir, DEFAULT_GIT_DIR_ENVIRONMENT))
- set_git_dir(gitdir);
+ set_git_dir(gitdir, 0);
inside_git_dir = 0;
inside_work_tree = 1;
if (offset >= cwd->len)
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);
+ set_git_dir(cwd->buf, 0);
}
else
- set_git_dir(".");
+ set_git_dir(".", 0);
return NULL;
}
/*
* A "string_list_each_func_t" function that canonicalizes an entry
- * from GIT_CEILING_DIRECTORIES using real_path_if_valid(), or
+ * from GIT_CEILING_DIRECTORIES using real_pathdup(), or
* discards it if unusable. The presence of an empty entry in
* GIT_CEILING_DIRECTORIES turns off canonicalization for all
* subsequent entries.
return -(i & 0666);
}
-void check_repository_format(void)
+void check_repository_format(struct repository_format *fmt)
{
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
- check_repository_format_gently(get_git_dir(), &repo_fmt, NULL);
+ if (!fmt)
+ fmt = &repo_fmt;
+ check_repository_format_gently(get_git_dir(), fmt, NULL);
startup_info->have_repository = 1;
+ repo_set_hash_algo(the_repository, fmt->hash_algo);
clear_repository_format(&repo_fmt);
}