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:
Junio C Hamano 2021-10-11 10:21:47 -07:00
Родитель 97492aacff 67985e4e4a
Коммит f6c075ad71
21 изменённых файлов: 142 добавлений и 116 удалений

Просмотреть файл

@ -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)) {

Просмотреть файл

@ -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);

Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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 &&