mirror of
https://github.com/git/git.git
synced 2024-11-27 12:03:55 +08:00
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>
This commit is contained in:
parent
62f2eca606
commit
ec91ffca04
2
cache.h
2
cache.h
@ -1044,6 +1044,7 @@ struct repository_format {
|
|||||||
int hash_algo;
|
int hash_algo;
|
||||||
char *work_tree;
|
char *work_tree;
|
||||||
struct string_list unknown_extensions;
|
struct string_list unknown_extensions;
|
||||||
|
struct string_list v1_only_extensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1057,6 +1058,7 @@ struct repository_format {
|
|||||||
.is_bare = -1, \
|
.is_bare = -1, \
|
||||||
.hash_algo = GIT_HASH_SHA1, \
|
.hash_algo = GIT_HASH_SHA1, \
|
||||||
.unknown_extensions = STRING_LIST_INIT_DUP, \
|
.unknown_extensions = STRING_LIST_INIT_DUP, \
|
||||||
|
.v1_only_extensions = STRING_LIST_INIT_DUP, \
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
96
setup.c
96
setup.c
@ -447,6 +447,54 @@ static int read_worktree_config(const char *var, const char *value, void *vdata)
|
|||||||
return 0;
|
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)
|
static int check_repo_format(const char *var, const char *value, void *vdata)
|
||||||
{
|
{
|
||||||
struct repository_format *data = 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)
|
if (strcmp(var, "core.repositoryformatversion") == 0)
|
||||||
data->version = git_config_int(var, value);
|
data->version = git_config_int(var, value);
|
||||||
else if (skip_prefix(var, "extensions.", &ext)) {
|
else if (skip_prefix(var, "extensions.", &ext)) {
|
||||||
/*
|
switch (handle_extension_v0(var, value, ext, data)) {
|
||||||
* record any known extensions here; otherwise,
|
case EXTENSION_ERROR:
|
||||||
* we fall through to recording it as unknown, and
|
return -1;
|
||||||
* check_repository_format will complain
|
case EXTENSION_OK:
|
||||||
*/
|
return 0;
|
||||||
if (!strcmp(ext, "noop"))
|
case EXTENSION_UNKNOWN:
|
||||||
;
|
break;
|
||||||
else if (!strcmp(ext, "preciousobjects"))
|
}
|
||||||
data->precious_objects = git_config_bool(var, value);
|
|
||||||
else if (!strcmp(ext, "partialclone")) {
|
switch (handle_extension(var, value, ext, data)) {
|
||||||
if (!value)
|
case EXTENSION_ERROR:
|
||||||
return config_error_nonbool(var);
|
return -1;
|
||||||
data->partial_clone = xstrdup(value);
|
case EXTENSION_OK:
|
||||||
} else if (!strcmp(ext, "worktreeconfig"))
|
string_list_append(&data->v1_only_extensions, ext);
|
||||||
data->worktree_config = git_config_bool(var, value);
|
return 0;
|
||||||
else
|
case EXTENSION_UNKNOWN:
|
||||||
string_list_append(&data->unknown_extensions, ext);
|
string_list_append(&data->unknown_extensions, ext);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return read_worktree_config(var, value, vdata);
|
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);
|
set_repository_format_partial_clone(candidate->partial_clone);
|
||||||
repository_format_worktree_config = candidate->worktree_config;
|
repository_format_worktree_config = candidate->worktree_config;
|
||||||
string_list_clear(&candidate->unknown_extensions, 0);
|
string_list_clear(&candidate->unknown_extensions, 0);
|
||||||
|
string_list_clear(&candidate->v1_only_extensions, 0);
|
||||||
|
|
||||||
if (repository_format_worktree_config) {
|
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)
|
void clear_repository_format(struct repository_format *format)
|
||||||
{
|
{
|
||||||
string_list_clear(&format->unknown_extensions, 0);
|
string_list_clear(&format->unknown_extensions, 0);
|
||||||
|
string_list_clear(&format->v1_only_extensions, 0);
|
||||||
free(format->work_tree);
|
free(format->work_tree);
|
||||||
free(format->partial_clone);
|
free(format->partial_clone);
|
||||||
init_repository_format(format);
|
init_repository_format(format);
|
||||||
@ -613,6 +665,18 @@ int verify_repository_format(const struct repository_format *format,
|
|||||||
return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +83,9 @@ allow 1
|
|||||||
allow 1 noop
|
allow 1 noop
|
||||||
abort 1 no-such-extension
|
abort 1 no-such-extension
|
||||||
allow 0 no-such-extension
|
allow 0 no-such-extension
|
||||||
|
allow 0 noop
|
||||||
|
abort 0 noop-v1
|
||||||
|
allow 1 noop-v1
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
test_expect_success 'precious-objects allowed' '
|
test_expect_success 'precious-objects allowed' '
|
||||||
|
Loading…
Reference in New Issue
Block a user