зеркало из https://github.com/microsoft/git.git
Merge branch 'jk/ref-paranoia'
The ref iteration code used to optionally allow dangling refs to be shown, which has been tightened up. * jk/ref-paranoia: refs: drop "broken" flag from for_each_fullref_in() ref-filter: drop broken-ref code entirely ref-filter: stop setting FILTER_REFS_INCLUDE_BROKEN repack, prune: drop GIT_REF_PARANOIA settings refs: turn on GIT_REF_PARANOIA by default refs: omit dangling symrefs when using GIT_REF_PARANOIA refs: add DO_FOR_EACH_OMIT_DANGLING_SYMREFS flag refs-internal.h: reorganize DO_FOR_EACH_* flag documentation refs-internal.h: move DO_FOR_EACH_* flags next to each other t5312: be more assertive about command failure t5312: test non-destructive repack t5312: create bogus ref as necessary t5312: drop "verbose" helper t5600: provide detached HEAD for corruption failures t5516: don't use HEAD ref for invalid ref-deletion tests t7900: clean up some more broken refs
This commit is contained in:
Коммит
f6c075ad71
|
@ -867,15 +867,16 @@ for full details.
|
||||||
end user, to be recorded in the body of the reflog.
|
end user, to be recorded in the body of the reflog.
|
||||||
|
|
||||||
`GIT_REF_PARANOIA`::
|
`GIT_REF_PARANOIA`::
|
||||||
If set to `1`, include broken or badly named refs when iterating
|
If set to `0`, ignore broken or badly named refs when iterating
|
||||||
over lists of refs. In a normal, non-corrupted repository, this
|
over lists of refs. Normally Git will try to include any such
|
||||||
does nothing. However, enabling it may help git to detect and
|
refs, which may cause some operations to fail. This is usually
|
||||||
abort some operations in the presence of broken refs. Git sets
|
preferable, as potentially destructive operations (e.g.,
|
||||||
this variable automatically when performing destructive
|
linkgit:git-prune[1]) are better off aborting rather than
|
||||||
operations like linkgit:git-prune[1]. You should not need to set
|
ignoring broken refs (and thus considering the history they
|
||||||
it yourself unless you want to be paranoid about making sure
|
point to as not worth saving). The default value is `1` (i.e.,
|
||||||
an operation has touched every ref (e.g., because you are
|
be paranoid about detecting and aborting all operations). You
|
||||||
cloning a repository to make a backup).
|
should not normally need to set this to `0`, but it may be
|
||||||
|
useful when trying to salvage data from a corrupted repository.
|
||||||
|
|
||||||
`GIT_ALLOW_PROTOCOL`::
|
`GIT_ALLOW_PROTOCOL`::
|
||||||
If set to a colon-separated list of protocols, behave as if
|
If set to a colon-separated list of protocols, behave as if
|
||||||
|
|
|
@ -427,7 +427,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
|
||||||
|
|
||||||
memset(&array, 0, sizeof(array));
|
memset(&array, 0, sizeof(array));
|
||||||
|
|
||||||
filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
|
filter_refs(&array, filter, filter->kind);
|
||||||
|
|
||||||
if (filter->verbose)
|
if (filter->verbose)
|
||||||
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
|
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
|
||||||
|
|
|
@ -77,7 +77,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
|
||||||
|
|
||||||
filter.name_patterns = argv;
|
filter.name_patterns = argv;
|
||||||
filter.match_as_path = 1;
|
filter.match_as_path = 1;
|
||||||
filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
|
filter_refs(&array, &filter, FILTER_REFS_ALL);
|
||||||
ref_array_sort(sorting, &array);
|
ref_array_sort(sorting, &array);
|
||||||
|
|
||||||
if (!maxcount || array.nr < maxcount)
|
if (!maxcount || array.nr < maxcount)
|
||||||
|
|
|
@ -143,7 +143,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
|
||||||
expire = TIME_MAX;
|
expire = TIME_MAX;
|
||||||
save_commit_buffer = 0;
|
save_commit_buffer = 0;
|
||||||
read_replace_refs = 0;
|
read_replace_refs = 0;
|
||||||
ref_paranoia = 1;
|
|
||||||
repo_init_revisions(the_repository, &revs, prefix);
|
repo_init_revisions(the_repository, &revs, prefix);
|
||||||
|
|
||||||
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
|
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
|
||||||
|
|
|
@ -586,15 +586,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
|
||||||
strvec_pushf(&cmd.args,
|
strvec_pushf(&cmd.args,
|
||||||
"--unpack-unreachable=%s",
|
"--unpack-unreachable=%s",
|
||||||
unpack_unreachable);
|
unpack_unreachable);
|
||||||
strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
|
|
||||||
} else if (pack_everything & LOOSEN_UNREACHABLE) {
|
} else if (pack_everything & LOOSEN_UNREACHABLE) {
|
||||||
strvec_push(&cmd.args,
|
strvec_push(&cmd.args,
|
||||||
"--unpack-unreachable");
|
"--unpack-unreachable");
|
||||||
} else if (keep_unreachable) {
|
} else if (keep_unreachable) {
|
||||||
strvec_push(&cmd.args, "--keep-unreachable");
|
strvec_push(&cmd.args, "--keep-unreachable");
|
||||||
strvec_push(&cmd.args, "--pack-loose-unreachable");
|
strvec_push(&cmd.args, "--pack-loose-unreachable");
|
||||||
} else {
|
|
||||||
strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (geometry) {
|
} else if (geometry) {
|
||||||
|
|
|
@ -863,8 +863,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "--bisect")) {
|
if (!strcmp(arg, "--bisect")) {
|
||||||
for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0);
|
for_each_fullref_in("refs/bisect/bad", show_reference, NULL);
|
||||||
for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0);
|
for_each_fullref_in("refs/bisect/good", anti_reference, NULL);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (opt_with_value(arg, "--branches", &arg)) {
|
if (opt_with_value(arg, "--branches", &arg)) {
|
||||||
|
|
8
cache.h
8
cache.h
|
@ -994,14 +994,6 @@ extern const char *core_fsmonitor;
|
||||||
extern int core_apply_sparse_checkout;
|
extern int core_apply_sparse_checkout;
|
||||||
extern int core_sparse_checkout_cone;
|
extern int core_sparse_checkout_cone;
|
||||||
|
|
||||||
/*
|
|
||||||
* Include broken refs in all ref iterations, which will
|
|
||||||
* generally choke dangerous operations rather than letting
|
|
||||||
* them silently proceed without taking the broken ref into
|
|
||||||
* account.
|
|
||||||
*/
|
|
||||||
extern int ref_paranoia;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
|
* Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -31,7 +31,6 @@ int prefer_symlink_refs;
|
||||||
int is_bare_repository_cfg = -1; /* unspecified */
|
int is_bare_repository_cfg = -1; /* unspecified */
|
||||||
int warn_ambiguous_refs = 1;
|
int warn_ambiguous_refs = 1;
|
||||||
int warn_on_object_refname_ambiguity = 1;
|
int warn_on_object_refname_ambiguity = 1;
|
||||||
int ref_paranoia = -1;
|
|
||||||
int repository_format_precious_objects;
|
int repository_format_precious_objects;
|
||||||
int repository_format_worktree_config;
|
int repository_format_worktree_config;
|
||||||
const char *git_commit_encoding;
|
const char *git_commit_encoding;
|
||||||
|
|
|
@ -189,7 +189,7 @@ int ls_refs(struct repository *r, struct packet_reader *request)
|
||||||
if (!data.prefixes.nr)
|
if (!data.prefixes.nr)
|
||||||
strvec_push(&data.prefixes, "");
|
strvec_push(&data.prefixes, "");
|
||||||
for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
|
for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
|
||||||
send_ref, &data, 0);
|
send_ref, &data);
|
||||||
packet_fflush(stdout);
|
packet_fflush(stdout);
|
||||||
strvec_clear(&data.prefixes);
|
strvec_clear(&data.prefixes);
|
||||||
strbuf_release(&data.buf);
|
strbuf_release(&data.buf);
|
||||||
|
|
22
ref-filter.c
22
ref-filter.c
|
@ -2100,8 +2100,7 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
|
||||||
*/
|
*/
|
||||||
static int for_each_fullref_in_pattern(struct ref_filter *filter,
|
static int for_each_fullref_in_pattern(struct ref_filter *filter,
|
||||||
each_ref_fn cb,
|
each_ref_fn cb,
|
||||||
void *cb_data,
|
void *cb_data)
|
||||||
int broken)
|
|
||||||
{
|
{
|
||||||
if (!filter->match_as_path) {
|
if (!filter->match_as_path) {
|
||||||
/*
|
/*
|
||||||
|
@ -2109,7 +2108,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
|
||||||
* prefixes like "refs/heads/" etc. are stripped off,
|
* prefixes like "refs/heads/" etc. are stripped off,
|
||||||
* so we have to look at everything:
|
* so we have to look at everything:
|
||||||
*/
|
*/
|
||||||
return for_each_fullref_in("", cb, cb_data, broken);
|
return for_each_fullref_in("", cb, cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter->ignore_case) {
|
if (filter->ignore_case) {
|
||||||
|
@ -2118,16 +2117,16 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
|
||||||
* so just return everything and let the caller
|
* so just return everything and let the caller
|
||||||
* sort it out.
|
* sort it out.
|
||||||
*/
|
*/
|
||||||
return for_each_fullref_in("", cb, cb_data, broken);
|
return for_each_fullref_in("", cb, cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!filter->name_patterns[0]) {
|
if (!filter->name_patterns[0]) {
|
||||||
/* no patterns; we have to look at everything */
|
/* no patterns; we have to look at everything */
|
||||||
return for_each_fullref_in("", cb, cb_data, broken);
|
return for_each_fullref_in("", cb, cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return for_each_fullref_in_prefixes(NULL, filter->name_patterns,
|
return for_each_fullref_in_prefixes(NULL, filter->name_patterns,
|
||||||
cb, cb_data, broken);
|
cb, cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2405,13 +2404,10 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
|
||||||
{
|
{
|
||||||
struct ref_filter_cbdata ref_cbdata;
|
struct ref_filter_cbdata ref_cbdata;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
unsigned int broken = 0;
|
|
||||||
|
|
||||||
ref_cbdata.array = array;
|
ref_cbdata.array = array;
|
||||||
ref_cbdata.filter = filter;
|
ref_cbdata.filter = filter;
|
||||||
|
|
||||||
if (type & FILTER_REFS_INCLUDE_BROKEN)
|
|
||||||
broken = 1;
|
|
||||||
filter->kind = type & FILTER_REFS_KIND_MASK;
|
filter->kind = type & FILTER_REFS_KIND_MASK;
|
||||||
|
|
||||||
init_contains_cache(&ref_cbdata.contains_cache);
|
init_contains_cache(&ref_cbdata.contains_cache);
|
||||||
|
@ -2428,13 +2424,13 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
|
||||||
* of filter_ref_kind().
|
* of filter_ref_kind().
|
||||||
*/
|
*/
|
||||||
if (filter->kind == FILTER_REFS_BRANCHES)
|
if (filter->kind == FILTER_REFS_BRANCHES)
|
||||||
ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata, broken);
|
ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata);
|
||||||
else if (filter->kind == FILTER_REFS_REMOTES)
|
else if (filter->kind == FILTER_REFS_REMOTES)
|
||||||
ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata, broken);
|
ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata);
|
||||||
else if (filter->kind == FILTER_REFS_TAGS)
|
else if (filter->kind == FILTER_REFS_TAGS)
|
||||||
ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, broken);
|
ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata);
|
||||||
else if (filter->kind & FILTER_REFS_ALL)
|
else if (filter->kind & FILTER_REFS_ALL)
|
||||||
ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata, broken);
|
ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata);
|
||||||
if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
|
if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
|
||||||
head_ref(ref_filter_handler, &ref_cbdata);
|
head_ref(ref_filter_handler, &ref_cbdata);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#define QUOTE_PYTHON 4
|
#define QUOTE_PYTHON 4
|
||||||
#define QUOTE_TCL 8
|
#define QUOTE_TCL 8
|
||||||
|
|
||||||
#define FILTER_REFS_INCLUDE_BROKEN 0x0001
|
|
||||||
#define FILTER_REFS_TAGS 0x0002
|
#define FILTER_REFS_TAGS 0x0002
|
||||||
#define FILTER_REFS_BRANCHES 0x0004
|
#define FILTER_REFS_BRANCHES 0x0004
|
||||||
#define FILTER_REFS_REMOTES 0x0008
|
#define FILTER_REFS_REMOTES 0x0008
|
||||||
|
|
42
refs.c
42
refs.c
|
@ -1409,14 +1409,21 @@ int head_ref(each_ref_fn fn, void *cb_data)
|
||||||
|
|
||||||
struct ref_iterator *refs_ref_iterator_begin(
|
struct ref_iterator *refs_ref_iterator_begin(
|
||||||
struct ref_store *refs,
|
struct ref_store *refs,
|
||||||
const char *prefix, int trim, int flags)
|
const char *prefix, int trim,
|
||||||
|
enum do_for_each_ref_flags flags)
|
||||||
{
|
{
|
||||||
struct ref_iterator *iter;
|
struct ref_iterator *iter;
|
||||||
|
|
||||||
if (ref_paranoia < 0)
|
if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
|
||||||
ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
|
static int ref_paranoia = -1;
|
||||||
if (ref_paranoia)
|
|
||||||
flags |= DO_FOR_EACH_INCLUDE_BROKEN;
|
if (ref_paranoia < 0)
|
||||||
|
ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 1);
|
||||||
|
if (ref_paranoia) {
|
||||||
|
flags |= DO_FOR_EACH_INCLUDE_BROKEN;
|
||||||
|
flags |= DO_FOR_EACH_OMIT_DANGLING_SYMREFS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
iter = refs->be->iterator_begin(refs, prefix, flags);
|
iter = refs->be->iterator_begin(refs, prefix, flags);
|
||||||
|
|
||||||
|
@ -1475,7 +1482,8 @@ static int do_for_each_ref_helper(struct repository *r,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
|
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
|
||||||
each_ref_fn fn, int trim, int flags, void *cb_data)
|
each_ref_fn fn, int trim,
|
||||||
|
enum do_for_each_ref_flags flags, void *cb_data)
|
||||||
{
|
{
|
||||||
struct ref_iterator *iter;
|
struct ref_iterator *iter;
|
||||||
struct do_for_each_ref_help hp = { fn, cb_data };
|
struct do_for_each_ref_help hp = { fn, cb_data };
|
||||||
|
@ -1510,25 +1518,16 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
|
||||||
return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, cb_data);
|
return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
|
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data)
|
||||||
{
|
{
|
||||||
unsigned int flag = 0;
|
|
||||||
|
|
||||||
if (broken)
|
|
||||||
flag = DO_FOR_EACH_INCLUDE_BROKEN;
|
|
||||||
return do_for_each_ref(get_main_ref_store(the_repository),
|
return do_for_each_ref(get_main_ref_store(the_repository),
|
||||||
prefix, fn, 0, flag, cb_data);
|
prefix, fn, 0, 0, cb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
|
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
|
||||||
each_ref_fn fn, void *cb_data,
|
each_ref_fn fn, void *cb_data)
|
||||||
unsigned int broken)
|
|
||||||
{
|
{
|
||||||
unsigned int flag = 0;
|
return do_for_each_ref(refs, prefix, fn, 0, 0, cb_data);
|
||||||
|
|
||||||
if (broken)
|
|
||||||
flag = DO_FOR_EACH_INCLUDE_BROKEN;
|
|
||||||
return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
|
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
|
||||||
|
@ -1620,8 +1619,7 @@ static void find_longest_prefixes(struct string_list *out,
|
||||||
|
|
||||||
int for_each_fullref_in_prefixes(const char *namespace,
|
int for_each_fullref_in_prefixes(const char *namespace,
|
||||||
const char **patterns,
|
const char **patterns,
|
||||||
each_ref_fn fn, void *cb_data,
|
each_ref_fn fn, void *cb_data)
|
||||||
unsigned int broken)
|
|
||||||
{
|
{
|
||||||
struct string_list prefixes = STRING_LIST_INIT_DUP;
|
struct string_list prefixes = STRING_LIST_INIT_DUP;
|
||||||
struct string_list_item *prefix;
|
struct string_list_item *prefix;
|
||||||
|
@ -1636,7 +1634,7 @@ int for_each_fullref_in_prefixes(const char *namespace,
|
||||||
|
|
||||||
for_each_string_list_item(prefix, &prefixes) {
|
for_each_string_list_item(prefix, &prefixes) {
|
||||||
strbuf_addstr(&buf, prefix->string);
|
strbuf_addstr(&buf, prefix->string);
|
||||||
ret = for_each_fullref_in(buf.buf, fn, cb_data, broken);
|
ret = for_each_fullref_in(buf.buf, fn, cb_data);
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
strbuf_setlen(&buf, namespace_len);
|
strbuf_setlen(&buf, namespace_len);
|
||||||
|
|
9
refs.h
9
refs.h
|
@ -342,10 +342,8 @@ int for_each_ref(each_ref_fn fn, void *cb_data);
|
||||||
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
|
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
|
||||||
|
|
||||||
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
|
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
|
||||||
each_ref_fn fn, void *cb_data,
|
each_ref_fn fn, void *cb_data);
|
||||||
unsigned int broken);
|
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data);
|
||||||
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
|
|
||||||
unsigned int broken);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iterate all refs in "patterns" by partitioning patterns into disjoint sets
|
* iterate all refs in "patterns" by partitioning patterns into disjoint sets
|
||||||
|
@ -354,8 +352,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
|
||||||
* callers should be prepared to ignore references that they did not ask for.
|
* callers should be prepared to ignore references that they did not ask for.
|
||||||
*/
|
*/
|
||||||
int for_each_fullref_in_prefixes(const char *namespace, const char **patterns,
|
int for_each_fullref_in_prefixes(const char *namespace, const char **patterns,
|
||||||
each_ref_fn fn, void *cb_data,
|
each_ref_fn fn, void *cb_data);
|
||||||
unsigned int broken);
|
|
||||||
/**
|
/**
|
||||||
* iterate refs from the respective area.
|
* iterate refs from the respective area.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -744,6 +744,11 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
|
||||||
ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
|
ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
|
||||||
|
(iter->iter0->flags & REF_ISSYMREF) &&
|
||||||
|
(iter->iter0->flags & REF_ISBROKEN))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
|
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
|
||||||
!ref_resolves_to_object(iter->iter0->refname,
|
!ref_resolves_to_object(iter->iter0->refname,
|
||||||
iter->iter0->oid,
|
iter->iter0->oid,
|
||||||
|
|
|
@ -245,8 +245,36 @@ int refs_rename_ref_available(struct ref_store *refs,
|
||||||
/* We allow "recursive" symbolic refs. Only within reason, though */
|
/* We allow "recursive" symbolic refs. Only within reason, though */
|
||||||
#define SYMREF_MAXDEPTH 5
|
#define SYMREF_MAXDEPTH 5
|
||||||
|
|
||||||
/* Include broken references in a do_for_each_ref*() iteration: */
|
/*
|
||||||
#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
|
* These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(),
|
||||||
|
* which feeds it).
|
||||||
|
*/
|
||||||
|
enum do_for_each_ref_flags {
|
||||||
|
/*
|
||||||
|
* Include broken references in a do_for_each_ref*() iteration, which
|
||||||
|
* would normally be omitted. This includes both refs that point to
|
||||||
|
* missing objects (a true repository corruption), ones with illegal
|
||||||
|
* names (which we prefer not to expose to callers), as well as
|
||||||
|
* dangling symbolic refs (i.e., those that point to a non-existent
|
||||||
|
* ref; this is not a corruption, but as they have no valid oid, we
|
||||||
|
* omit them from normal iteration results).
|
||||||
|
*/
|
||||||
|
DO_FOR_EACH_INCLUDE_BROKEN = (1 << 0),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only include per-worktree refs in a do_for_each_ref*() iteration.
|
||||||
|
* Normally this will be used with a files ref_store, since that's
|
||||||
|
* where all reference backends will presumably store their
|
||||||
|
* per-worktree refs.
|
||||||
|
*/
|
||||||
|
DO_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Omit dangling symrefs from output; this only has an effect with
|
||||||
|
* INCLUDE_BROKEN, since they are otherwise not included at all.
|
||||||
|
*/
|
||||||
|
DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reference iterators
|
* Reference iterators
|
||||||
|
@ -349,16 +377,12 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator);
|
||||||
* Return an iterator that goes over each reference in `refs` for
|
* Return an iterator that goes over each reference in `refs` for
|
||||||
* which the refname begins with prefix. If trim is non-zero, then
|
* which the refname begins with prefix. If trim is non-zero, then
|
||||||
* trim that many characters off the beginning of each refname.
|
* trim that many characters off the beginning of each refname.
|
||||||
* The output is ordered by refname. The following flags are supported:
|
* The output is ordered by refname.
|
||||||
*
|
|
||||||
* DO_FOR_EACH_INCLUDE_BROKEN: include broken references in
|
|
||||||
* the iteration.
|
|
||||||
*
|
|
||||||
* DO_FOR_EACH_PER_WORKTREE_ONLY: only produce REF_TYPE_PER_WORKTREE refs.
|
|
||||||
*/
|
*/
|
||||||
struct ref_iterator *refs_ref_iterator_begin(
|
struct ref_iterator *refs_ref_iterator_begin(
|
||||||
struct ref_store *refs,
|
struct ref_store *refs,
|
||||||
const char *prefix, int trim, int flags);
|
const char *prefix, int trim,
|
||||||
|
enum do_for_each_ref_flags flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A callback function used to instruct merge_ref_iterator how to
|
* A callback function used to instruct merge_ref_iterator how to
|
||||||
|
@ -446,10 +470,8 @@ void base_ref_iterator_free(struct ref_iterator *iter);
|
||||||
/*
|
/*
|
||||||
* backend-specific implementation of ref_iterator_advance. For symrefs, the
|
* backend-specific implementation of ref_iterator_advance. For symrefs, the
|
||||||
* function should set REF_ISSYMREF, and it should also dereference the symref
|
* function should set REF_ISSYMREF, and it should also dereference the symref
|
||||||
* to provide the OID referent. If DO_FOR_EACH_INCLUDE_BROKEN is set, symrefs
|
* to provide the OID referent. It should respect do_for_each_ref_flags
|
||||||
* with non-existent referents and refs pointing to non-existent object names
|
* that were passed to refs_ref_iterator_begin().
|
||||||
* should also be returned. If DO_FOR_EACH_PER_WORKTREE_ONLY, only
|
|
||||||
* REF_TYPE_PER_WORKTREE refs should be returned.
|
|
||||||
*/
|
*/
|
||||||
typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
|
typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
|
||||||
|
|
||||||
|
@ -498,14 +520,6 @@ int do_for_each_repo_ref_iterator(struct repository *r,
|
||||||
struct ref_iterator *iter,
|
struct ref_iterator *iter,
|
||||||
each_repo_ref_fn fn, void *cb_data);
|
each_repo_ref_fn fn, void *cb_data);
|
||||||
|
|
||||||
/*
|
|
||||||
* Only include per-worktree refs in a do_for_each_ref*() iteration.
|
|
||||||
* Normally this will be used with a files ref_store, since that's
|
|
||||||
* where all reference backends will presumably store their
|
|
||||||
* per-worktree refs.
|
|
||||||
*/
|
|
||||||
#define DO_FOR_EACH_PER_WORKTREE_ONLY 0x02
|
|
||||||
|
|
||||||
struct ref_store;
|
struct ref_store;
|
||||||
|
|
||||||
/* refs backends */
|
/* refs backends */
|
||||||
|
|
|
@ -2548,7 +2548,7 @@ static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
|
||||||
struct strbuf bisect_refs = STRBUF_INIT;
|
struct strbuf bisect_refs = STRBUF_INIT;
|
||||||
int status;
|
int status;
|
||||||
strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
|
strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
|
||||||
status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data, 0);
|
status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data);
|
||||||
strbuf_release(&bisect_refs);
|
strbuf_release(&bisect_refs);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,7 @@ test_expect_success 'for-each-ref emits warnings for broken names' '
|
||||||
! grep -e "badname" output &&
|
! grep -e "badname" output &&
|
||||||
! grep -e "broken\.\.\.symref" output &&
|
! grep -e "broken\.\.\.symref" output &&
|
||||||
test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
|
test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
|
||||||
test_i18ngrep "ignoring broken ref refs/heads/badname" error &&
|
test_i18ngrep ! "ignoring broken ref refs/heads/badname" error &&
|
||||||
test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
|
test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
|
||||||
'
|
'
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@ if we see, for example, a ref with a bogus name, it is OK either to
|
||||||
bail out or to proceed using it as a reachable tip, but it is _not_
|
bail out or to proceed using it as a reachable tip, but it is _not_
|
||||||
OK to proceed as if it did not exist. Otherwise we might silently
|
OK to proceed as if it did not exist. Otherwise we might silently
|
||||||
delete objects that cannot be recovered.
|
delete objects that cannot be recovered.
|
||||||
|
|
||||||
|
Note that we do assert command failure in these cases, because that is
|
||||||
|
what currently happens. If that changes, these tests should be revisited.
|
||||||
'
|
'
|
||||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||||
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||||
|
@ -18,39 +21,58 @@ test_expect_success 'disable reflogs' '
|
||||||
git reflog expire --expire=all --all
|
git reflog expire --expire=all --all
|
||||||
'
|
'
|
||||||
|
|
||||||
|
create_bogus_ref () {
|
||||||
|
test_when_finished 'rm -f .git/refs/heads/bogus..name' &&
|
||||||
|
echo $bogus >.git/refs/heads/bogus..name
|
||||||
|
}
|
||||||
|
|
||||||
test_expect_success 'create history reachable only from a bogus-named ref' '
|
test_expect_success 'create history reachable only from a bogus-named ref' '
|
||||||
test_tick && git commit --allow-empty -m main &&
|
test_tick && git commit --allow-empty -m main &&
|
||||||
base=$(git rev-parse HEAD) &&
|
base=$(git rev-parse HEAD) &&
|
||||||
test_tick && git commit --allow-empty -m bogus &&
|
test_tick && git commit --allow-empty -m bogus &&
|
||||||
bogus=$(git rev-parse HEAD) &&
|
bogus=$(git rev-parse HEAD) &&
|
||||||
git cat-file commit $bogus >saved &&
|
git cat-file commit $bogus >saved &&
|
||||||
echo $bogus >.git/refs/heads/bogus..name &&
|
|
||||||
git reset --hard HEAD^
|
git reset --hard HEAD^
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'pruning does not drop bogus object' '
|
test_expect_success 'pruning does not drop bogus object' '
|
||||||
test_when_finished "git hash-object -w -t commit saved" &&
|
test_when_finished "git hash-object -w -t commit saved" &&
|
||||||
test_might_fail git prune --expire=now &&
|
create_bogus_ref &&
|
||||||
verbose git cat-file -e $bogus
|
test_must_fail git prune --expire=now &&
|
||||||
|
git cat-file -e $bogus
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'put bogus object into pack' '
|
test_expect_success 'put bogus object into pack' '
|
||||||
git tag reachable $bogus &&
|
git tag reachable $bogus &&
|
||||||
git repack -ad &&
|
git repack -ad &&
|
||||||
git tag -d reachable &&
|
git tag -d reachable &&
|
||||||
verbose git cat-file -e $bogus
|
git cat-file -e $bogus
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'non-destructive repack bails on bogus ref' '
|
||||||
|
create_bogus_ref &&
|
||||||
|
test_must_fail git repack -adk
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'GIT_REF_PARANOIA=0 overrides safety' '
|
||||||
|
create_bogus_ref &&
|
||||||
|
GIT_REF_PARANOIA=0 git repack -adk
|
||||||
|
'
|
||||||
|
|
||||||
|
|
||||||
test_expect_success 'destructive repack keeps packed object' '
|
test_expect_success 'destructive repack keeps packed object' '
|
||||||
test_might_fail git repack -Ad --unpack-unreachable=now &&
|
create_bogus_ref &&
|
||||||
verbose git cat-file -e $bogus &&
|
test_must_fail git repack -Ad --unpack-unreachable=now &&
|
||||||
test_might_fail git repack -ad &&
|
git cat-file -e $bogus &&
|
||||||
verbose git cat-file -e $bogus
|
test_must_fail git repack -ad &&
|
||||||
|
git cat-file -e $bogus
|
||||||
'
|
'
|
||||||
|
|
||||||
# subsequent tests will have different corruptions
|
test_expect_success 'destructive repack not confused by dangling symref' '
|
||||||
test_expect_success 'clean up bogus ref' '
|
test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
|
||||||
rm .git/refs/heads/bogus..name
|
git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
|
||||||
|
git repack -ad &&
|
||||||
|
test_must_fail git cat-file -e $bogus
|
||||||
'
|
'
|
||||||
|
|
||||||
# We create two new objects here, "one" and "two". Our
|
# We create two new objects here, "one" and "two". Our
|
||||||
|
@ -77,8 +99,8 @@ test_expect_success 'create history with missing tip commit' '
|
||||||
|
|
||||||
test_expect_success 'pruning with a corrupted tip does not drop history' '
|
test_expect_success 'pruning with a corrupted tip does not drop history' '
|
||||||
test_when_finished "git hash-object -w -t commit saved" &&
|
test_when_finished "git hash-object -w -t commit saved" &&
|
||||||
test_might_fail git prune --expire=now &&
|
test_must_fail git prune --expire=now &&
|
||||||
verbose git cat-file -e $recoverable
|
git cat-file -e $recoverable
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'pack-refs does not silently delete broken loose ref' '
|
test_expect_success 'pack-refs does not silently delete broken loose ref' '
|
||||||
|
|
|
@ -662,10 +662,10 @@ test_expect_success 'push does not update local refs on failure' '
|
||||||
|
|
||||||
test_expect_success 'allow deleting an invalid remote ref' '
|
test_expect_success 'allow deleting an invalid remote ref' '
|
||||||
|
|
||||||
mk_test testrepo heads/main &&
|
mk_test testrepo heads/branch &&
|
||||||
rm -f testrepo/.git/objects/??/* &&
|
rm -f testrepo/.git/objects/??/* &&
|
||||||
git push testrepo :refs/heads/main &&
|
git push testrepo :refs/heads/branch &&
|
||||||
(cd testrepo && test_must_fail git rev-parse --verify refs/heads/main)
|
(cd testrepo && test_must_fail git rev-parse --verify refs/heads/branch)
|
||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
|
@ -706,25 +706,26 @@ test_expect_success 'pushing valid refs triggers post-receive and post-update ho
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'deleting dangling ref triggers hooks with correct args' '
|
test_expect_success 'deleting dangling ref triggers hooks with correct args' '
|
||||||
mk_test_with_hooks testrepo heads/main &&
|
mk_test_with_hooks testrepo heads/branch &&
|
||||||
|
orig=$(git -C testrepo rev-parse refs/heads/branch) &&
|
||||||
rm -f testrepo/.git/objects/??/* &&
|
rm -f testrepo/.git/objects/??/* &&
|
||||||
git push testrepo :refs/heads/main &&
|
git push testrepo :refs/heads/branch &&
|
||||||
(
|
(
|
||||||
cd testrepo/.git &&
|
cd testrepo/.git &&
|
||||||
cat >pre-receive.expect <<-EOF &&
|
cat >pre-receive.expect <<-EOF &&
|
||||||
$ZERO_OID $ZERO_OID refs/heads/main
|
$orig $ZERO_OID refs/heads/branch
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat >update.expect <<-EOF &&
|
cat >update.expect <<-EOF &&
|
||||||
refs/heads/main $ZERO_OID $ZERO_OID
|
refs/heads/branch $orig $ZERO_OID
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat >post-receive.expect <<-EOF &&
|
cat >post-receive.expect <<-EOF &&
|
||||||
$ZERO_OID $ZERO_OID refs/heads/main
|
$orig $ZERO_OID refs/heads/branch
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat >post-update.expect <<-EOF &&
|
cat >post-update.expect <<-EOF &&
|
||||||
refs/heads/main
|
refs/heads/branch
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
test_cmp pre-receive.expect pre-receive.actual &&
|
test_cmp pre-receive.expect pre-receive.actual &&
|
||||||
|
|
|
@ -35,7 +35,9 @@ test_expect_success 'create a repo to clone' '
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'create objects in repo for later corruption' '
|
test_expect_success 'create objects in repo for later corruption' '
|
||||||
test_commit -C foo file
|
test_commit -C foo file &&
|
||||||
|
git -C foo checkout --detach &&
|
||||||
|
test_commit -C foo detached
|
||||||
'
|
'
|
||||||
|
|
||||||
# source repository given to git clone should be relative to the
|
# source repository given to git clone should be relative to the
|
||||||
|
|
|
@ -276,7 +276,7 @@ test_expect_success 'incremental-repack task' '
|
||||||
|
|
||||||
# Delete refs that have not been repacked in these packs.
|
# Delete refs that have not been repacked in these packs.
|
||||||
git for-each-ref --format="delete %(refname)" \
|
git for-each-ref --format="delete %(refname)" \
|
||||||
refs/prefetch refs/tags >refs &&
|
refs/prefetch refs/tags refs/remotes >refs &&
|
||||||
git update-ref --stdin <refs &&
|
git update-ref --stdin <refs &&
|
||||||
|
|
||||||
# Replace the object directory with this pack layout.
|
# Replace the object directory with this pack layout.
|
||||||
|
@ -285,6 +285,10 @@ test_expect_success 'incremental-repack task' '
|
||||||
ls $packDir/*.pack >packs-before &&
|
ls $packDir/*.pack >packs-before &&
|
||||||
test_line_count = 3 packs-before &&
|
test_line_count = 3 packs-before &&
|
||||||
|
|
||||||
|
# make sure we do not have any broken refs that were
|
||||||
|
# missed in the deletion above
|
||||||
|
git for-each-ref &&
|
||||||
|
|
||||||
# the job repacks the two into a new pack, but does not
|
# the job repacks the two into a new pack, but does not
|
||||||
# delete the old ones.
|
# delete the old ones.
|
||||||
git maintenance run --task=incremental-repack &&
|
git maintenance run --task=incremental-repack &&
|
||||||
|
|
Загрузка…
Ссылка в новой задаче