Merge branch 'sj/ref-fsck'
"git fsck" infrastructure has been taught to also check the sanity of the ref database, in addition to the object database. * sj/ref-fsck: fsck: add ref name check for files backend files-backend: add unified interface for refs scanning builtin/refs: add verify subcommand refs: set up ref consistency check infrastructure fsck: add refs report function fsck: add a unified interface for reporting fsck messages fsck: make "fsck_error" callback generic fsck: rename objects-related fsck error functions fsck: rename "skiplist" to "skip_oids"
This commit is contained in:
@ -19,6 +19,12 @@
|
|||||||
`badParentSha1`::
|
`badParentSha1`::
|
||||||
(ERROR) A commit object has a bad parent sha1.
|
(ERROR) A commit object has a bad parent sha1.
|
||||||
|
|
||||||
|
`badRefFiletype`::
|
||||||
|
(ERROR) A ref has a bad file type.
|
||||||
|
|
||||||
|
`badRefName`::
|
||||||
|
(ERROR) A ref has an invalid format.
|
||||||
|
|
||||||
`badTagName`::
|
`badTagName`::
|
||||||
(INFO) A tag has an invalid format.
|
(INFO) A tag has an invalid format.
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ SYNOPSIS
|
|||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'git refs migrate' --ref-format=<format> [--dry-run]
|
'git refs migrate' --ref-format=<format> [--dry-run]
|
||||||
|
'git refs verify' [--strict] [--verbose]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
@ -22,6 +23,9 @@ COMMANDS
|
|||||||
migrate::
|
migrate::
|
||||||
Migrate ref store between different formats.
|
Migrate ref store between different formats.
|
||||||
|
|
||||||
|
verify::
|
||||||
|
Verify reference database consistency.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@ -39,6 +43,15 @@ include::ref-storage-format.txt[]
|
|||||||
can be used to double check that the migration works as expected before
|
can be used to double check that the migration works as expected before
|
||||||
performing the actual migration.
|
performing the actual migration.
|
||||||
|
|
||||||
|
The following options are specific to 'git refs verify':
|
||||||
|
|
||||||
|
--strict::
|
||||||
|
Enable stricter error checking. This will cause warnings to be
|
||||||
|
reported as errors. See linkgit:git-fsck[1].
|
||||||
|
|
||||||
|
--verbose::
|
||||||
|
When verifying the reference database consistency, be chatty.
|
||||||
|
|
||||||
KNOWN LIMITATIONS
|
KNOWN LIMITATIONS
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
@ -89,13 +89,16 @@ static int objerror(struct object *obj, const char *err)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fsck_error_func(struct fsck_options *o UNUSED,
|
static int fsck_objects_error_func(struct fsck_options *o UNUSED,
|
||||||
const struct object_id *oid,
|
void *fsck_report,
|
||||||
enum object_type object_type,
|
enum fsck_msg_type msg_type,
|
||||||
enum fsck_msg_type msg_type,
|
enum fsck_msg_id msg_id UNUSED,
|
||||||
enum fsck_msg_id msg_id UNUSED,
|
const char *message)
|
||||||
const char *message)
|
|
||||||
{
|
{
|
||||||
|
struct fsck_object_report *report = fsck_report;
|
||||||
|
const struct object_id *oid = report->oid;
|
||||||
|
enum object_type object_type = report->object_type;
|
||||||
|
|
||||||
switch (msg_type) {
|
switch (msg_type) {
|
||||||
case FSCK_WARN:
|
case FSCK_WARN:
|
||||||
/* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
|
/* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
|
||||||
@ -938,7 +941,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
|
|||||||
|
|
||||||
fsck_walk_options.walk = mark_object;
|
fsck_walk_options.walk = mark_object;
|
||||||
fsck_obj_options.walk = mark_used;
|
fsck_obj_options.walk = mark_used;
|
||||||
fsck_obj_options.error_func = fsck_error_func;
|
fsck_obj_options.error_func = fsck_objects_error_func;
|
||||||
if (check_strict)
|
if (check_strict)
|
||||||
fsck_obj_options.strict = 1;
|
fsck_obj_options.strict = 1;
|
||||||
|
|
||||||
|
@ -18,8 +18,7 @@ static int option_strict = 1;
|
|||||||
static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
|
static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
|
||||||
|
|
||||||
static int mktag_fsck_error_func(struct fsck_options *o UNUSED,
|
static int mktag_fsck_error_func(struct fsck_options *o UNUSED,
|
||||||
const struct object_id *oid UNUSED,
|
void *fsck_report UNUSED,
|
||||||
enum object_type object_type UNUSED,
|
|
||||||
enum fsck_msg_type msg_type,
|
enum fsck_msg_type msg_type,
|
||||||
enum fsck_msg_id msg_id UNUSED,
|
enum fsck_msg_id msg_id UNUSED,
|
||||||
const char *message)
|
const char *message)
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "fsck.h"
|
||||||
#include "parse-options.h"
|
#include "parse-options.h"
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "repository.h"
|
#include "repository.h"
|
||||||
@ -7,6 +9,9 @@
|
|||||||
#define REFS_MIGRATE_USAGE \
|
#define REFS_MIGRATE_USAGE \
|
||||||
N_("git refs migrate --ref-format=<format> [--dry-run]")
|
N_("git refs migrate --ref-format=<format> [--dry-run]")
|
||||||
|
|
||||||
|
#define REFS_VERIFY_USAGE \
|
||||||
|
N_("git refs verify [--strict] [--verbose]")
|
||||||
|
|
||||||
static int cmd_refs_migrate(int argc, const char **argv, const char *prefix)
|
static int cmd_refs_migrate(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
const char * const migrate_usage[] = {
|
const char * const migrate_usage[] = {
|
||||||
@ -58,15 +63,44 @@ out:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cmd_refs_verify(int argc, const char **argv, const char *prefix)
|
||||||
|
{
|
||||||
|
struct fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT;
|
||||||
|
const char * const verify_usage[] = {
|
||||||
|
REFS_VERIFY_USAGE,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
struct option options[] = {
|
||||||
|
OPT_BOOL(0, "verbose", &fsck_refs_options.verbose, N_("be verbose")),
|
||||||
|
OPT_BOOL(0, "strict", &fsck_refs_options.strict, N_("enable strict checking")),
|
||||||
|
OPT_END(),
|
||||||
|
};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
argc = parse_options(argc, argv, prefix, options, verify_usage, 0);
|
||||||
|
if (argc)
|
||||||
|
usage(_("'git refs verify' takes no arguments"));
|
||||||
|
|
||||||
|
git_config(git_fsck_config, &fsck_refs_options);
|
||||||
|
prepare_repo_settings(the_repository);
|
||||||
|
|
||||||
|
ret = refs_fsck(get_main_ref_store(the_repository), &fsck_refs_options);
|
||||||
|
|
||||||
|
fsck_options_clear(&fsck_refs_options);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int cmd_refs(int argc, const char **argv, const char *prefix)
|
int cmd_refs(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
const char * const refs_usage[] = {
|
const char * const refs_usage[] = {
|
||||||
REFS_MIGRATE_USAGE,
|
REFS_MIGRATE_USAGE,
|
||||||
|
REFS_VERIFY_USAGE,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
parse_opt_subcommand_fn *fn = NULL;
|
parse_opt_subcommand_fn *fn = NULL;
|
||||||
struct option opts[] = {
|
struct option opts[] = {
|
||||||
OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate),
|
OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate),
|
||||||
|
OPT_SUBCOMMAND("verify", &fn, cmd_refs_verify),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
125
fsck.c
125
fsck.c
@ -205,7 +205,7 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)
|
|||||||
if (!strcmp(buf, "skiplist")) {
|
if (!strcmp(buf, "skiplist")) {
|
||||||
if (equal == len)
|
if (equal == len)
|
||||||
die("skiplist requires a path");
|
die("skiplist requires a path");
|
||||||
oidset_parse_file(&options->skiplist, buf + equal + 1,
|
oidset_parse_file(&options->skip_oids, buf + equal + 1,
|
||||||
the_repository->hash_algo);
|
the_repository->hash_algo);
|
||||||
buf += len + 1;
|
buf += len + 1;
|
||||||
continue;
|
continue;
|
||||||
@ -223,15 +223,18 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)
|
|||||||
static int object_on_skiplist(struct fsck_options *opts,
|
static int object_on_skiplist(struct fsck_options *opts,
|
||||||
const struct object_id *oid)
|
const struct object_id *oid)
|
||||||
{
|
{
|
||||||
return opts && oid && oidset_contains(&opts->skiplist, oid);
|
return opts && oid && oidset_contains(&opts->skip_oids, oid);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((format (printf, 5, 6)))
|
/*
|
||||||
static int report(struct fsck_options *options,
|
* Provide the common functionality for either fscking refs or objects.
|
||||||
const struct object_id *oid, enum object_type object_type,
|
* It will get the current msg error type and call the error_func callback
|
||||||
enum fsck_msg_id msg_id, const char *fmt, ...)
|
* which is registered in the "fsck_options" struct.
|
||||||
|
*/
|
||||||
|
static int fsck_vreport(struct fsck_options *options,
|
||||||
|
void *fsck_report,
|
||||||
|
enum fsck_msg_id msg_id, const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
va_list ap;
|
|
||||||
struct strbuf sb = STRBUF_INIT;
|
struct strbuf sb = STRBUF_INIT;
|
||||||
enum fsck_msg_type msg_type = fsck_msg_type(msg_id, options);
|
enum fsck_msg_type msg_type = fsck_msg_type(msg_id, options);
|
||||||
int result;
|
int result;
|
||||||
@ -239,9 +242,6 @@ static int report(struct fsck_options *options,
|
|||||||
if (msg_type == FSCK_IGNORE)
|
if (msg_type == FSCK_IGNORE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (object_on_skiplist(options, oid))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (msg_type == FSCK_FATAL)
|
if (msg_type == FSCK_FATAL)
|
||||||
msg_type = FSCK_ERROR;
|
msg_type = FSCK_ERROR;
|
||||||
else if (msg_type == FSCK_INFO)
|
else if (msg_type == FSCK_INFO)
|
||||||
@ -250,16 +250,49 @@ static int report(struct fsck_options *options,
|
|||||||
prepare_msg_ids();
|
prepare_msg_ids();
|
||||||
strbuf_addf(&sb, "%s: ", msg_id_info[msg_id].camelcased);
|
strbuf_addf(&sb, "%s: ", msg_id_info[msg_id].camelcased);
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
strbuf_vaddf(&sb, fmt, ap);
|
strbuf_vaddf(&sb, fmt, ap);
|
||||||
result = options->error_func(options, oid, object_type,
|
result = options->error_func(options, fsck_report,
|
||||||
msg_type, msg_id, sb.buf);
|
msg_type, msg_id, sb.buf);
|
||||||
strbuf_release(&sb);
|
strbuf_release(&sb);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((format (printf, 5, 6)))
|
||||||
|
static int report(struct fsck_options *options,
|
||||||
|
const struct object_id *oid, enum object_type object_type,
|
||||||
|
enum fsck_msg_id msg_id, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
struct fsck_object_report report = {
|
||||||
|
.oid = oid,
|
||||||
|
.object_type = object_type
|
||||||
|
};
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (object_on_skiplist(options, oid))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
result = fsck_vreport(options, &report, msg_id, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fsck_report_ref(struct fsck_options *options,
|
||||||
|
struct fsck_ref_report *report,
|
||||||
|
enum fsck_msg_id msg_id,
|
||||||
|
const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int result;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
result = fsck_vreport(options, report, msg_id, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void fsck_enable_object_names(struct fsck_options *options)
|
void fsck_enable_object_names(struct fsck_options *options)
|
||||||
{
|
{
|
||||||
if (!options->object_names)
|
if (!options->object_names)
|
||||||
@ -1200,13 +1233,15 @@ int fsck_buffer(const struct object_id *oid, enum object_type type,
|
|||||||
type);
|
type);
|
||||||
}
|
}
|
||||||
|
|
||||||
int fsck_error_function(struct fsck_options *o,
|
int fsck_objects_error_function(struct fsck_options *o,
|
||||||
const struct object_id *oid,
|
void *fsck_report,
|
||||||
enum object_type object_type UNUSED,
|
enum fsck_msg_type msg_type,
|
||||||
enum fsck_msg_type msg_type,
|
enum fsck_msg_id msg_id UNUSED,
|
||||||
enum fsck_msg_id msg_id UNUSED,
|
const char *message)
|
||||||
const char *message)
|
|
||||||
{
|
{
|
||||||
|
struct fsck_object_report *report = fsck_report;
|
||||||
|
const struct object_id *oid = report->oid;
|
||||||
|
|
||||||
if (msg_type == FSCK_WARN) {
|
if (msg_type == FSCK_WARN) {
|
||||||
warning("object %s: %s", fsck_describe_object(o, oid), message);
|
warning("object %s: %s", fsck_describe_object(o, oid), message);
|
||||||
return 0;
|
return 0;
|
||||||
@ -1215,6 +1250,32 @@ int fsck_error_function(struct fsck_options *o,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fsck_refs_error_function(struct fsck_options *options UNUSED,
|
||||||
|
void *fsck_report,
|
||||||
|
enum fsck_msg_type msg_type,
|
||||||
|
enum fsck_msg_id msg_id UNUSED,
|
||||||
|
const char *message)
|
||||||
|
{
|
||||||
|
struct fsck_ref_report *report = fsck_report;
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
strbuf_addstr(&sb, report->path);
|
||||||
|
|
||||||
|
if (report->oid)
|
||||||
|
strbuf_addf(&sb, " -> (%s)", oid_to_hex(report->oid));
|
||||||
|
else if (report->referent)
|
||||||
|
strbuf_addf(&sb, " -> (%s)", report->referent);
|
||||||
|
|
||||||
|
if (msg_type == FSCK_WARN)
|
||||||
|
warning("%s: %s", sb.buf, message);
|
||||||
|
else
|
||||||
|
ret = error("%s: %s", sb.buf, message);
|
||||||
|
|
||||||
|
strbuf_release(&sb);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,
|
static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,
|
||||||
enum fsck_msg_id msg_missing, enum fsck_msg_id msg_type,
|
enum fsck_msg_id msg_missing, enum fsck_msg_id msg_type,
|
||||||
struct fsck_options *options, const char *blob_type)
|
struct fsck_options *options, const char *blob_type)
|
||||||
@ -1270,6 +1331,17 @@ int fsck_finish(struct fsck_options *options)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fsck_options_clear(struct fsck_options *options)
|
||||||
|
{
|
||||||
|
free(options->msg_type);
|
||||||
|
oidset_clear(&options->skip_oids);
|
||||||
|
oidset_clear(&options->gitmodules_found);
|
||||||
|
oidset_clear(&options->gitmodules_done);
|
||||||
|
oidset_clear(&options->gitattributes_found);
|
||||||
|
oidset_clear(&options->gitattributes_done);
|
||||||
|
kh_clear_oid_map(options->object_names);
|
||||||
|
}
|
||||||
|
|
||||||
int git_fsck_config(const char *var, const char *value,
|
int git_fsck_config(const char *var, const char *value,
|
||||||
const struct config_context *ctx, void *cb)
|
const struct config_context *ctx, void *cb)
|
||||||
{
|
{
|
||||||
@ -1303,16 +1375,17 @@ int git_fsck_config(const char *var, const char *value,
|
|||||||
* Custom error callbacks that are used in more than one place.
|
* Custom error callbacks that are used in more than one place.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int fsck_error_cb_print_missing_gitmodules(struct fsck_options *o,
|
int fsck_objects_error_cb_print_missing_gitmodules(struct fsck_options *o,
|
||||||
const struct object_id *oid,
|
void *fsck_report,
|
||||||
enum object_type object_type,
|
enum fsck_msg_type msg_type,
|
||||||
enum fsck_msg_type msg_type,
|
enum fsck_msg_id msg_id,
|
||||||
enum fsck_msg_id msg_id,
|
const char *message)
|
||||||
const char *message)
|
|
||||||
{
|
{
|
||||||
if (msg_id == FSCK_MSG_GITMODULES_MISSING) {
|
if (msg_id == FSCK_MSG_GITMODULES_MISSING) {
|
||||||
puts(oid_to_hex(oid));
|
struct fsck_object_report *report = fsck_report;
|
||||||
|
puts(oid_to_hex(report->oid));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return fsck_error_function(o, oid, object_type, msg_type, msg_id, message);
|
return fsck_objects_error_function(o, fsck_report,
|
||||||
|
msg_type, msg_id, message);
|
||||||
}
|
}
|
||||||
|
76
fsck.h
76
fsck.h
@ -31,6 +31,8 @@ enum fsck_msg_type {
|
|||||||
FUNC(BAD_NAME, ERROR) \
|
FUNC(BAD_NAME, ERROR) \
|
||||||
FUNC(BAD_OBJECT_SHA1, ERROR) \
|
FUNC(BAD_OBJECT_SHA1, ERROR) \
|
||||||
FUNC(BAD_PARENT_SHA1, ERROR) \
|
FUNC(BAD_PARENT_SHA1, ERROR) \
|
||||||
|
FUNC(BAD_REF_FILETYPE, ERROR) \
|
||||||
|
FUNC(BAD_REF_NAME, ERROR) \
|
||||||
FUNC(BAD_TIMEZONE, ERROR) \
|
FUNC(BAD_TIMEZONE, ERROR) \
|
||||||
FUNC(BAD_TREE, ERROR) \
|
FUNC(BAD_TREE, ERROR) \
|
||||||
FUNC(BAD_TREE_SHA1, ERROR) \
|
FUNC(BAD_TREE_SHA1, ERROR) \
|
||||||
@ -114,29 +116,49 @@ int is_valid_msg_type(const char *msg_id, const char *msg_type);
|
|||||||
typedef int (*fsck_walk_func)(struct object *obj, enum object_type object_type,
|
typedef int (*fsck_walk_func)(struct object *obj, enum object_type object_type,
|
||||||
void *data, struct fsck_options *options);
|
void *data, struct fsck_options *options);
|
||||||
|
|
||||||
/* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
|
/*
|
||||||
|
* Callback for reporting errors either for objects or refs. The "fsck_report"
|
||||||
|
* is a generic pointer that can be used to pass any information.
|
||||||
|
*/
|
||||||
typedef int (*fsck_error)(struct fsck_options *o,
|
typedef int (*fsck_error)(struct fsck_options *o,
|
||||||
const struct object_id *oid, enum object_type object_type,
|
void *fsck_report,
|
||||||
enum fsck_msg_type msg_type, enum fsck_msg_id msg_id,
|
enum fsck_msg_type msg_type, enum fsck_msg_id msg_id,
|
||||||
const char *message);
|
const char *message);
|
||||||
|
|
||||||
int fsck_error_function(struct fsck_options *o,
|
int fsck_objects_error_function(struct fsck_options *o,
|
||||||
const struct object_id *oid, enum object_type object_type,
|
void *fsck_report,
|
||||||
enum fsck_msg_type msg_type, enum fsck_msg_id msg_id,
|
enum fsck_msg_type msg_type, enum fsck_msg_id msg_id,
|
||||||
const char *message);
|
const char *message);
|
||||||
int fsck_error_cb_print_missing_gitmodules(struct fsck_options *o,
|
int fsck_objects_error_cb_print_missing_gitmodules(struct fsck_options *o,
|
||||||
const struct object_id *oid,
|
void *fsck_report,
|
||||||
enum object_type object_type,
|
enum fsck_msg_type msg_type,
|
||||||
enum fsck_msg_type msg_type,
|
enum fsck_msg_id msg_id,
|
||||||
enum fsck_msg_id msg_id,
|
const char *message);
|
||||||
const char *message);
|
|
||||||
|
int fsck_refs_error_function(struct fsck_options *options,
|
||||||
|
void *fsck_report,
|
||||||
|
enum fsck_msg_type msg_type,
|
||||||
|
enum fsck_msg_id msg_id,
|
||||||
|
const char *message);
|
||||||
|
|
||||||
|
struct fsck_object_report {
|
||||||
|
const struct object_id *oid;
|
||||||
|
enum object_type object_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fsck_ref_report {
|
||||||
|
const char *path;
|
||||||
|
const struct object_id *oid;
|
||||||
|
const char *referent;
|
||||||
|
};
|
||||||
|
|
||||||
struct fsck_options {
|
struct fsck_options {
|
||||||
fsck_walk_func walk;
|
fsck_walk_func walk;
|
||||||
fsck_error error_func;
|
fsck_error error_func;
|
||||||
unsigned strict:1;
|
unsigned strict;
|
||||||
|
unsigned verbose;
|
||||||
enum fsck_msg_type *msg_type;
|
enum fsck_msg_type *msg_type;
|
||||||
struct oidset skiplist;
|
struct oidset skip_oids;
|
||||||
struct oidset gitmodules_found;
|
struct oidset gitmodules_found;
|
||||||
struct oidset gitmodules_done;
|
struct oidset gitmodules_done;
|
||||||
struct oidset gitattributes_found;
|
struct oidset gitattributes_found;
|
||||||
@ -145,12 +167,12 @@ struct fsck_options {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define FSCK_OPTIONS_DEFAULT { \
|
#define FSCK_OPTIONS_DEFAULT { \
|
||||||
.skiplist = OIDSET_INIT, \
|
.skip_oids = OIDSET_INIT, \
|
||||||
.gitmodules_found = OIDSET_INIT, \
|
.gitmodules_found = OIDSET_INIT, \
|
||||||
.gitmodules_done = OIDSET_INIT, \
|
.gitmodules_done = OIDSET_INIT, \
|
||||||
.gitattributes_found = OIDSET_INIT, \
|
.gitattributes_found = OIDSET_INIT, \
|
||||||
.gitattributes_done = OIDSET_INIT, \
|
.gitattributes_done = OIDSET_INIT, \
|
||||||
.error_func = fsck_error_function \
|
.error_func = fsck_objects_error_function \
|
||||||
}
|
}
|
||||||
#define FSCK_OPTIONS_STRICT { \
|
#define FSCK_OPTIONS_STRICT { \
|
||||||
.strict = 1, \
|
.strict = 1, \
|
||||||
@ -158,7 +180,7 @@ struct fsck_options {
|
|||||||
.gitmodules_done = OIDSET_INIT, \
|
.gitmodules_done = OIDSET_INIT, \
|
||||||
.gitattributes_found = OIDSET_INIT, \
|
.gitattributes_found = OIDSET_INIT, \
|
||||||
.gitattributes_done = OIDSET_INIT, \
|
.gitattributes_done = OIDSET_INIT, \
|
||||||
.error_func = fsck_error_function, \
|
.error_func = fsck_objects_error_function, \
|
||||||
}
|
}
|
||||||
#define FSCK_OPTIONS_MISSING_GITMODULES { \
|
#define FSCK_OPTIONS_MISSING_GITMODULES { \
|
||||||
.strict = 1, \
|
.strict = 1, \
|
||||||
@ -166,7 +188,10 @@ struct fsck_options {
|
|||||||
.gitmodules_done = OIDSET_INIT, \
|
.gitmodules_done = OIDSET_INIT, \
|
||||||
.gitattributes_found = OIDSET_INIT, \
|
.gitattributes_found = OIDSET_INIT, \
|
||||||
.gitattributes_done = OIDSET_INIT, \
|
.gitattributes_done = OIDSET_INIT, \
|
||||||
.error_func = fsck_error_cb_print_missing_gitmodules, \
|
.error_func = fsck_objects_error_cb_print_missing_gitmodules, \
|
||||||
|
}
|
||||||
|
#define FSCK_REFS_OPTIONS_DEFAULT { \
|
||||||
|
.error_func = fsck_refs_error_function, \
|
||||||
}
|
}
|
||||||
|
|
||||||
/* descend in all linked child objects
|
/* descend in all linked child objects
|
||||||
@ -209,6 +234,21 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
|
|||||||
*/
|
*/
|
||||||
int fsck_finish(struct fsck_options *options);
|
int fsck_finish(struct fsck_options *options);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear the fsck_options struct, freeing any allocated memory.
|
||||||
|
*/
|
||||||
|
void fsck_options_clear(struct fsck_options *options);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Report an error or warning for refs.
|
||||||
|
*/
|
||||||
|
__attribute__((format (printf, 4, 5)))
|
||||||
|
int fsck_report_ref(struct fsck_options *options,
|
||||||
|
struct fsck_ref_report *report,
|
||||||
|
enum fsck_msg_id msg_id,
|
||||||
|
const char *fmt, ...);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Subsystem for storing human-readable names for each object.
|
* Subsystem for storing human-readable names for each object.
|
||||||
*
|
*
|
||||||
|
@ -2470,11 +2470,10 @@ int repo_has_object_file(struct repository *r,
|
|||||||
* give more context.
|
* give more context.
|
||||||
*/
|
*/
|
||||||
static int hash_format_check_report(struct fsck_options *opts UNUSED,
|
static int hash_format_check_report(struct fsck_options *opts UNUSED,
|
||||||
const struct object_id *oid UNUSED,
|
void *fsck_report UNUSED,
|
||||||
enum object_type object_type UNUSED,
|
enum fsck_msg_type msg_type UNUSED,
|
||||||
enum fsck_msg_type msg_type UNUSED,
|
enum fsck_msg_id msg_id UNUSED,
|
||||||
enum fsck_msg_id msg_id UNUSED,
|
const char *message)
|
||||||
const char *message)
|
|
||||||
{
|
{
|
||||||
error(_("object fails fsck: %s"), message);
|
error(_("object fails fsck: %s"), message);
|
||||||
return 1;
|
return 1;
|
||||||
|
5
refs.c
5
refs.c
@ -316,6 +316,11 @@ int check_refname_format(const char *refname, int flags)
|
|||||||
return check_or_sanitize_refname(refname, flags, NULL);
|
return check_or_sanitize_refname(refname, flags, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int refs_fsck(struct ref_store *refs, struct fsck_options *o)
|
||||||
|
{
|
||||||
|
return refs->be->fsck(refs, o);
|
||||||
|
}
|
||||||
|
|
||||||
void sanitize_refname_component(const char *refname, struct strbuf *out)
|
void sanitize_refname_component(const char *refname, struct strbuf *out)
|
||||||
{
|
{
|
||||||
if (check_or_sanitize_refname(refname, REFNAME_ALLOW_ONELEVEL, out))
|
if (check_or_sanitize_refname(refname, REFNAME_ALLOW_ONELEVEL, out))
|
||||||
|
8
refs.h
8
refs.h
@ -4,6 +4,7 @@
|
|||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "repository.h"
|
#include "repository.h"
|
||||||
|
|
||||||
|
struct fsck_options;
|
||||||
struct object_id;
|
struct object_id;
|
||||||
struct ref_store;
|
struct ref_store;
|
||||||
struct strbuf;
|
struct strbuf;
|
||||||
@ -541,6 +542,13 @@ int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_dat
|
|||||||
*/
|
*/
|
||||||
int check_refname_format(const char *refname, int flags);
|
int check_refname_format(const char *refname, int flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the reference database for consistency. Return 0 if refs and
|
||||||
|
* reflogs are consistent, and non-zero otherwise. The errors will be
|
||||||
|
* written to stderr.
|
||||||
|
*/
|
||||||
|
int refs_fsck(struct ref_store *refs, struct fsck_options *o);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Apply the rules from check_refname_format, but mutate the result until it
|
* Apply the rules from check_refname_format, but mutate the result until it
|
||||||
* is acceptable, and place the result in "out".
|
* is acceptable, and place the result in "out".
|
||||||
|
11
refs/debug.c
11
refs/debug.c
@ -419,6 +419,15 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int debug_fsck(struct ref_store *ref_store,
|
||||||
|
struct fsck_options *o)
|
||||||
|
{
|
||||||
|
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
|
||||||
|
int res = drefs->refs->be->fsck(drefs->refs, o);
|
||||||
|
trace_printf_key(&trace_refs, "fsck: %d\n", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
struct ref_storage_be refs_be_debug = {
|
struct ref_storage_be refs_be_debug = {
|
||||||
.name = "debug",
|
.name = "debug",
|
||||||
.init = NULL,
|
.init = NULL,
|
||||||
@ -451,4 +460,6 @@ struct ref_storage_be refs_be_debug = {
|
|||||||
.create_reflog = debug_create_reflog,
|
.create_reflog = debug_create_reflog,
|
||||||
.delete_reflog = debug_delete_reflog,
|
.delete_reflog = debug_delete_reflog,
|
||||||
.reflog_expire = debug_reflog_expire,
|
.reflog_expire = debug_reflog_expire,
|
||||||
|
|
||||||
|
.fsck = debug_fsck,
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "../gettext.h"
|
#include "../gettext.h"
|
||||||
#include "../hash.h"
|
#include "../hash.h"
|
||||||
#include "../hex.h"
|
#include "../hex.h"
|
||||||
|
#include "../fsck.h"
|
||||||
#include "../refs.h"
|
#include "../refs.h"
|
||||||
#include "refs-internal.h"
|
#include "refs-internal.h"
|
||||||
#include "ref-cache.h"
|
#include "ref-cache.h"
|
||||||
@ -3419,6 +3420,116 @@ static int files_ref_store_remove_on_disk(struct ref_store *ref_store,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For refs and reflogs, they share a unified interface when scanning
|
||||||
|
* the whole directory. This function is used as the callback for each
|
||||||
|
* regular file or symlink in the directory.
|
||||||
|
*/
|
||||||
|
typedef int (*files_fsck_refs_fn)(struct ref_store *ref_store,
|
||||||
|
struct fsck_options *o,
|
||||||
|
const char *refs_check_dir,
|
||||||
|
struct dir_iterator *iter);
|
||||||
|
|
||||||
|
static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
|
||||||
|
struct fsck_options *o,
|
||||||
|
const char *refs_check_dir,
|
||||||
|
struct dir_iterator *iter)
|
||||||
|
{
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore the files ending with ".lock" as they may be lock files
|
||||||
|
* However, do not allow bare ".lock" files.
|
||||||
|
*/
|
||||||
|
if (iter->basename[0] != '.' && ends_with(iter->basename, ".lock"))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (check_refname_format(iter->basename, REFNAME_ALLOW_ONELEVEL)) {
|
||||||
|
struct fsck_ref_report report = { .path = NULL };
|
||||||
|
|
||||||
|
strbuf_addf(&sb, "%s/%s", refs_check_dir, iter->relative_path);
|
||||||
|
report.path = sb.buf;
|
||||||
|
ret = fsck_report_ref(o, &report,
|
||||||
|
FSCK_MSG_BAD_REF_NAME,
|
||||||
|
"invalid refname format");
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
strbuf_release(&sb);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int files_fsck_refs_dir(struct ref_store *ref_store,
|
||||||
|
struct fsck_options *o,
|
||||||
|
const char *refs_check_dir,
|
||||||
|
files_fsck_refs_fn *fsck_refs_fn)
|
||||||
|
{
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
struct dir_iterator *iter;
|
||||||
|
int iter_status;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
strbuf_addf(&sb, "%s/%s", ref_store->gitdir, refs_check_dir);
|
||||||
|
|
||||||
|
iter = dir_iterator_begin(sb.buf, 0);
|
||||||
|
if (!iter) {
|
||||||
|
ret = error_errno(_("cannot open directory %s"), sb.buf);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) {
|
||||||
|
if (S_ISDIR(iter->st.st_mode)) {
|
||||||
|
continue;
|
||||||
|
} else if (S_ISREG(iter->st.st_mode) ||
|
||||||
|
S_ISLNK(iter->st.st_mode)) {
|
||||||
|
if (o->verbose)
|
||||||
|
fprintf_ln(stderr, "Checking %s/%s",
|
||||||
|
refs_check_dir, iter->relative_path);
|
||||||
|
for (size_t i = 0; fsck_refs_fn[i]; i++) {
|
||||||
|
if (fsck_refs_fn[i](ref_store, o, refs_check_dir, iter))
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
struct fsck_ref_report report = { .path = iter->basename };
|
||||||
|
if (fsck_report_ref(o, &report,
|
||||||
|
FSCK_MSG_BAD_REF_FILETYPE,
|
||||||
|
"unexpected file type"))
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iter_status != ITER_DONE)
|
||||||
|
ret = error(_("failed to iterate over '%s'"), sb.buf);
|
||||||
|
|
||||||
|
out:
|
||||||
|
strbuf_release(&sb);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int files_fsck_refs(struct ref_store *ref_store,
|
||||||
|
struct fsck_options *o)
|
||||||
|
{
|
||||||
|
files_fsck_refs_fn fsck_refs_fn[]= {
|
||||||
|
files_fsck_refs_name,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (o->verbose)
|
||||||
|
fprintf_ln(stderr, _("Checking references consistency"));
|
||||||
|
return files_fsck_refs_dir(ref_store, o, "refs", fsck_refs_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int files_fsck(struct ref_store *ref_store,
|
||||||
|
struct fsck_options *o)
|
||||||
|
{
|
||||||
|
struct files_ref_store *refs =
|
||||||
|
files_downcast(ref_store, REF_STORE_READ, "fsck");
|
||||||
|
|
||||||
|
return files_fsck_refs(ref_store, o) |
|
||||||
|
refs->packed_ref_store->be->fsck(refs->packed_ref_store, o);
|
||||||
|
}
|
||||||
|
|
||||||
struct ref_storage_be refs_be_files = {
|
struct ref_storage_be refs_be_files = {
|
||||||
.name = "files",
|
.name = "files",
|
||||||
.init = files_ref_store_init,
|
.init = files_ref_store_init,
|
||||||
@ -3445,5 +3556,7 @@ struct ref_storage_be refs_be_files = {
|
|||||||
.reflog_exists = files_reflog_exists,
|
.reflog_exists = files_reflog_exists,
|
||||||
.create_reflog = files_create_reflog,
|
.create_reflog = files_create_reflog,
|
||||||
.delete_reflog = files_delete_reflog,
|
.delete_reflog = files_delete_reflog,
|
||||||
.reflog_expire = files_reflog_expire
|
.reflog_expire = files_reflog_expire,
|
||||||
|
|
||||||
|
.fsck = files_fsck,
|
||||||
};
|
};
|
||||||
|
@ -1733,6 +1733,12 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
|
|||||||
return empty_ref_iterator_begin();
|
return empty_ref_iterator_begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int packed_fsck(struct ref_store *ref_store,
|
||||||
|
struct fsck_options *o)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct ref_storage_be refs_be_packed = {
|
struct ref_storage_be refs_be_packed = {
|
||||||
.name = "packed",
|
.name = "packed",
|
||||||
.init = packed_ref_store_init,
|
.init = packed_ref_store_init,
|
||||||
@ -1760,4 +1766,6 @@ struct ref_storage_be refs_be_packed = {
|
|||||||
.create_reflog = NULL,
|
.create_reflog = NULL,
|
||||||
.delete_reflog = NULL,
|
.delete_reflog = NULL,
|
||||||
.reflog_expire = NULL,
|
.reflog_expire = NULL,
|
||||||
|
|
||||||
|
.fsck = packed_fsck,
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "iterator.h"
|
#include "iterator.h"
|
||||||
|
|
||||||
|
struct fsck_options;
|
||||||
struct ref_transaction;
|
struct ref_transaction;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -651,6 +652,9 @@ typedef int read_raw_ref_fn(struct ref_store *ref_store, const char *refname,
|
|||||||
typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refname,
|
typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refname,
|
||||||
struct strbuf *referent);
|
struct strbuf *referent);
|
||||||
|
|
||||||
|
typedef int fsck_fn(struct ref_store *ref_store,
|
||||||
|
struct fsck_options *o);
|
||||||
|
|
||||||
struct ref_storage_be {
|
struct ref_storage_be {
|
||||||
const char *name;
|
const char *name;
|
||||||
ref_store_init_fn *init;
|
ref_store_init_fn *init;
|
||||||
@ -678,6 +682,8 @@ struct ref_storage_be {
|
|||||||
create_reflog_fn *create_reflog;
|
create_reflog_fn *create_reflog;
|
||||||
delete_reflog_fn *delete_reflog;
|
delete_reflog_fn *delete_reflog;
|
||||||
reflog_expire_fn *reflog_expire;
|
reflog_expire_fn *reflog_expire;
|
||||||
|
|
||||||
|
fsck_fn *fsck;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct ref_storage_be refs_be_files;
|
extern struct ref_storage_be refs_be_files;
|
||||||
|
@ -2309,6 +2309,12 @@ done:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int reftable_be_fsck(struct ref_store *ref_store,
|
||||||
|
struct fsck_options *o)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct ref_storage_be refs_be_reftable = {
|
struct ref_storage_be refs_be_reftable = {
|
||||||
.name = "reftable",
|
.name = "reftable",
|
||||||
.init = reftable_be_init,
|
.init = reftable_be_init,
|
||||||
@ -2336,4 +2342,6 @@ struct ref_storage_be refs_be_reftable = {
|
|||||||
.create_reflog = reftable_be_create_reflog,
|
.create_reflog = reftable_be_create_reflog,
|
||||||
.delete_reflog = reftable_be_delete_reflog,
|
.delete_reflog = reftable_be_delete_reflog,
|
||||||
.reflog_expire = reftable_be_reflog_expire,
|
.reflog_expire = reftable_be_reflog_expire,
|
||||||
|
|
||||||
|
.fsck = reftable_be_fsck,
|
||||||
};
|
};
|
||||||
|
92
t/t0602-reffiles-fsck.sh
Executable file
92
t/t0602-reffiles-fsck.sh
Executable file
@ -0,0 +1,92 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='Test reffiles backend consistency check'
|
||||||
|
|
||||||
|
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||||
|
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||||
|
GIT_TEST_DEFAULT_REF_FORMAT=files
|
||||||
|
export GIT_TEST_DEFAULT_REF_FORMAT
|
||||||
|
TEST_PASSES_SANITIZE_LEAK=true
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success 'ref name should be checked' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
branch_dir_prefix=.git/refs/heads &&
|
||||||
|
tag_dir_prefix=.git/refs/tags &&
|
||||||
|
cd repo &&
|
||||||
|
|
||||||
|
git commit --allow-empty -m initial &&
|
||||||
|
git checkout -b branch-1 &&
|
||||||
|
git tag tag-1 &&
|
||||||
|
git commit --allow-empty -m second &&
|
||||||
|
git checkout -b branch-2 &&
|
||||||
|
git tag tag-2 &&
|
||||||
|
git tag multi_hierarchy/tag-2 &&
|
||||||
|
|
||||||
|
cp $branch_dir_prefix/branch-1 $branch_dir_prefix/.branch-1 &&
|
||||||
|
test_must_fail git refs verify 2>err &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
error: refs/heads/.branch-1: badRefName: invalid refname format
|
||||||
|
EOF
|
||||||
|
rm $branch_dir_prefix/.branch-1 &&
|
||||||
|
test_cmp expect err &&
|
||||||
|
|
||||||
|
cp $branch_dir_prefix/branch-1 $branch_dir_prefix/@ &&
|
||||||
|
test_must_fail git refs verify 2>err &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
error: refs/heads/@: badRefName: invalid refname format
|
||||||
|
EOF
|
||||||
|
rm $branch_dir_prefix/@ &&
|
||||||
|
test_cmp expect err &&
|
||||||
|
|
||||||
|
cp $tag_dir_prefix/multi_hierarchy/tag-2 $tag_dir_prefix/multi_hierarchy/@ &&
|
||||||
|
test_must_fail git refs verify 2>err &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
error: refs/tags/multi_hierarchy/@: badRefName: invalid refname format
|
||||||
|
EOF
|
||||||
|
rm $tag_dir_prefix/multi_hierarchy/@ &&
|
||||||
|
test_cmp expect err &&
|
||||||
|
|
||||||
|
cp $tag_dir_prefix/tag-1 $tag_dir_prefix/tag-1.lock &&
|
||||||
|
git refs verify 2>err &&
|
||||||
|
rm $tag_dir_prefix/tag-1.lock &&
|
||||||
|
test_must_be_empty err &&
|
||||||
|
|
||||||
|
cp $tag_dir_prefix/tag-1 $tag_dir_prefix/.lock &&
|
||||||
|
test_must_fail git refs verify 2>err &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
error: refs/tags/.lock: badRefName: invalid refname format
|
||||||
|
EOF
|
||||||
|
rm $tag_dir_prefix/.lock &&
|
||||||
|
test_cmp expect err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'ref name check should be adapted into fsck messages' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
branch_dir_prefix=.git/refs/heads &&
|
||||||
|
tag_dir_prefix=.git/refs/tags &&
|
||||||
|
cd repo &&
|
||||||
|
git commit --allow-empty -m initial &&
|
||||||
|
git checkout -b branch-1 &&
|
||||||
|
git tag tag-1 &&
|
||||||
|
git commit --allow-empty -m second &&
|
||||||
|
git checkout -b branch-2 &&
|
||||||
|
git tag tag-2 &&
|
||||||
|
|
||||||
|
cp $branch_dir_prefix/branch-1 $branch_dir_prefix/.branch-1 &&
|
||||||
|
git -c fsck.badRefName=warn refs verify 2>err &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
warning: refs/heads/.branch-1: badRefName: invalid refname format
|
||||||
|
EOF
|
||||||
|
rm $branch_dir_prefix/.branch-1 &&
|
||||||
|
test_cmp expect err &&
|
||||||
|
|
||||||
|
cp $branch_dir_prefix/branch-1 $branch_dir_prefix/@ &&
|
||||||
|
git -c fsck.badRefName=ignore refs verify 2>err &&
|
||||||
|
test_must_be_empty err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
Reference in New Issue
Block a user