aboutsummaryrefslogtreecommitdiffstats
path: root/setup.c
diff options
context:
space:
mode:
authorJeff King <peff@peff.net>2020-07-16 08:25:13 -0400
committerJunio C Hamano <gitster@pobox.com>2020-07-16 10:39:45 -0700
commitec91ffca0455e1a8a0525fb2a10814447d80c456 (patch)
treec3c5b7f3a29ee9c1c30c347e7a0be4c55eb928cd /setup.c
parent62f2eca6065d0c27b0ea3b7806a590466ea04960 (diff)
downloadgit-ec91ffca0455e1a8a0525fb2a10814447d80c456.tar.gz
verify_repository_format(): complain about new extensions in v0 repo
We made the mistake in the past of respecting extensions.* even when the repository format version was set to 0. This is bad because forgetting to bump the repository version means that older versions of Git (which do not know about our extensions) won't complain. I.e., it's not a problem in itself, but it means your repository is in a state which does not give you the protection you think you're getting from older versions. For compatibility reasons, we are stuck with that decision for existing extensions. However, we'd prefer not to extend the damage further. We can do that by catching any newly-added extensions and complaining about the repository format. Note that this is a pretty heavy hammer: we'll refuse to work with the repository at all. A lesser option would be to ignore (possibly with a warning) any new extensions. But because of the way the extensions are handled, that puts the burden on each new extension that is added to remember to "undo" itself (because they are handled before we know for sure whether we are in a v1 repo or not, since we don't insist on a particular ordering of config entries). So one option would be to rewrite that handling to record any new extensions (and their values) during the config parse, and then only after proceed to handle new ones only if we're in a v1 repository. But I'm not sure if it's worth the trouble: - ignoring extensions is likely to end up with broken results anyway (e.g., ignoring a proposed objectformat extension means parsing any object data is likely to encounter errors) - this is a sign that whatever tool wrote the extension field is broken. We may be better off notifying immediately and forcefully so that such tools don't even appear to work accidentally. The only downside is that fixing the situation is a little tricky, because programs like "git config" won't want to work with the repository. But: git config --file=.git/config core.repositoryformatversion 1 should still suffice. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'setup.c')
-rw-r--r--setup.c96
1 files changed, 80 insertions, 16 deletions
diff --git a/setup.c b/setup.c
index e34b073577..e69bd28ed6 100644
--- a/setup.c
+++ b/setup.c
@@ -447,6 +447,54 @@ static int read_worktree_config(const char *var, const char *value, void *vdata)
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;
+ }
+
+ return EXTENSION_UNKNOWN;
+}
+
static int check_repo_format(const char *var, const char *value, void *vdata)
{
struct repository_format *data = vdata;
@@ -455,23 +503,25 @@ static int check_repo_format(const char *var, const char *value, void *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);
@@ -510,6 +560,7 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
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) {
/*
@@ -588,6 +639,7 @@ int read_repository_format(struct repository_format *format, const char *path)
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);
@@ -613,6 +665,18 @@ int verify_repository_format(const struct 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;
}