Skip to content

Commit ec91ffc

Browse files
peffgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 62f2eca commit ec91ffc

File tree

3 files changed

+85
-16
lines changed

3 files changed

+85
-16
lines changed

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,7 @@ struct repository_format {
10441044
int hash_algo;
10451045
char *work_tree;
10461046
struct string_list unknown_extensions;
1047+
struct string_list v1_only_extensions;
10471048
};
10481049

10491050
/*
@@ -1057,6 +1058,7 @@ struct repository_format {
10571058
.is_bare = -1, \
10581059
.hash_algo = GIT_HASH_SHA1, \
10591060
.unknown_extensions = STRING_LIST_INIT_DUP, \
1061+
.v1_only_extensions = STRING_LIST_INIT_DUP, \
10601062
}
10611063

10621064
/*

setup.c

Lines changed: 80 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,54 @@ static int read_worktree_config(const char *var, const char *value, void *vdata)
447447
return 0;
448448
}
449449

450+
enum extension_result {
451+
EXTENSION_ERROR = -1, /* compatible with error(), etc */
452+
EXTENSION_UNKNOWN = 0,
453+
EXTENSION_OK = 1
454+
};
455+
456+
/*
457+
* Do not add new extensions to this function. It handles extensions which are
458+
* respected even in v0-format repositories for historical compatibility.
459+
*/
460+
static enum extension_result handle_extension_v0(const char *var,
461+
const char *value,
462+
const char *ext,
463+
struct repository_format *data)
464+
{
465+
if (!strcmp(ext, "noop")) {
466+
return EXTENSION_OK;
467+
} else if (!strcmp(ext, "preciousobjects")) {
468+
data->precious_objects = git_config_bool(var, value);
469+
return EXTENSION_OK;
470+
} else if (!strcmp(ext, "partialclone")) {
471+
if (!value)
472+
return config_error_nonbool(var);
473+
data->partial_clone = xstrdup(value);
474+
return EXTENSION_OK;
475+
} else if (!strcmp(ext, "worktreeconfig")) {
476+
data->worktree_config = git_config_bool(var, value);
477+
return EXTENSION_OK;
478+
}
479+
480+
return EXTENSION_UNKNOWN;
481+
}
482+
483+
/*
484+
* Record any new extensions in this function.
485+
*/
486+
static enum extension_result handle_extension(const char *var,
487+
const char *value,
488+
const char *ext,
489+
struct repository_format *data)
490+
{
491+
if (!strcmp(ext, "noop-v1")) {
492+
return EXTENSION_OK;
493+
}
494+
495+
return EXTENSION_UNKNOWN;
496+
}
497+
450498
static int check_repo_format(const char *var, const char *value, void *vdata)
451499
{
452500
struct repository_format *data = vdata;
@@ -455,23 +503,25 @@ static int check_repo_format(const char *var, const char *value, void *vdata)
455503
if (strcmp(var, "core.repositoryformatversion") == 0)
456504
data->version = git_config_int(var, value);
457505
else if (skip_prefix(var, "extensions.", &ext)) {
458-
/*
459-
* record any known extensions here; otherwise,
460-
* we fall through to recording it as unknown, and
461-
* check_repository_format will complain
462-
*/
463-
if (!strcmp(ext, "noop"))
464-
;
465-
else if (!strcmp(ext, "preciousobjects"))
466-
data->precious_objects = git_config_bool(var, value);
467-
else if (!strcmp(ext, "partialclone")) {
468-
if (!value)
469-
return config_error_nonbool(var);
470-
data->partial_clone = xstrdup(value);
471-
} else if (!strcmp(ext, "worktreeconfig"))
472-
data->worktree_config = git_config_bool(var, value);
473-
else
506+
switch (handle_extension_v0(var, value, ext, data)) {
507+
case EXTENSION_ERROR:
508+
return -1;
509+
case EXTENSION_OK:
510+
return 0;
511+
case EXTENSION_UNKNOWN:
512+
break;
513+
}
514+
515+
switch (handle_extension(var, value, ext, data)) {
516+
case EXTENSION_ERROR:
517+
return -1;
518+
case EXTENSION_OK:
519+
string_list_append(&data->v1_only_extensions, ext);
520+
return 0;
521+
case EXTENSION_UNKNOWN:
474522
string_list_append(&data->unknown_extensions, ext);
523+
return 0;
524+
}
475525
}
476526

477527
return read_worktree_config(var, value, vdata);
@@ -510,6 +560,7 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
510560
set_repository_format_partial_clone(candidate->partial_clone);
511561
repository_format_worktree_config = candidate->worktree_config;
512562
string_list_clear(&candidate->unknown_extensions, 0);
563+
string_list_clear(&candidate->v1_only_extensions, 0);
513564

514565
if (repository_format_worktree_config) {
515566
/*
@@ -588,6 +639,7 @@ int read_repository_format(struct repository_format *format, const char *path)
588639
void clear_repository_format(struct repository_format *format)
589640
{
590641
string_list_clear(&format->unknown_extensions, 0);
642+
string_list_clear(&format->v1_only_extensions, 0);
591643
free(format->work_tree);
592644
free(format->partial_clone);
593645
init_repository_format(format);
@@ -613,6 +665,18 @@ int verify_repository_format(const struct repository_format *format,
613665
return -1;
614666
}
615667

668+
if (format->version == 0 && format->v1_only_extensions.nr) {
669+
int i;
670+
671+
strbuf_addstr(err,
672+
_("repo version is 0, but v1-only extensions found:"));
673+
674+
for (i = 0; i < format->v1_only_extensions.nr; i++)
675+
strbuf_addf(err, "\n\t%s",
676+
format->v1_only_extensions.items[i].string);
677+
return -1;
678+
}
679+
616680
return 0;
617681
}
618682

t/t1302-repo-version.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ allow 1
8383
allow 1 noop
8484
abort 1 no-such-extension
8585
allow 0 no-such-extension
86+
allow 0 noop
87+
abort 0 noop-v1
88+
allow 1 noop-v1
8689
EOF
8790

8891
test_expect_success 'precious-objects allowed' '

0 commit comments

Comments
 (0)