aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/fsck-msgids.adoc14
-rw-r--r--Documentation/git-fsck.adoc7
-rw-r--r--builtin/fsck.c33
-rw-r--r--builtin/refs.c2
-rw-r--r--fsck.h4
-rw-r--r--refs/packed-backend.c363
-rwxr-xr-xt/t0602-reffiles-fsck.sh1209
-rw-r--r--worktree.c5
-rw-r--r--worktree.h8
9 files changed, 1162 insertions, 483 deletions
diff --git a/Documentation/fsck-msgids.adoc b/Documentation/fsck-msgids.adoc
index b14bc44ca4..9601fff228 100644
--- a/Documentation/fsck-msgids.adoc
+++ b/Documentation/fsck-msgids.adoc
@@ -16,6 +16,13 @@
`badObjectSha1`::
(ERROR) An object has a bad sha1.
+`badPackedRefEntry`::
+ (ERROR) The "packed-refs" file contains an invalid entry.
+
+`badPackedRefHeader`::
+ (ERROR) The "packed-refs" file contains an invalid
+ header.
+
`badParentSha1`::
(ERROR) A commit object has a bad parent sha1.
@@ -176,6 +183,13 @@
`nullSha1`::
(WARN) Tree contains entries pointing to a null sha1.
+`packedRefEntryNotTerminated`::
+ (ERROR) The "packed-refs" file contains an entry that is
+ not terminated by a newline.
+
+`packedRefUnsorted`::
+ (ERROR) The "packed-refs" file is not sorted.
+
`refMissingNewline`::
(INFO) A loose ref that does not end with newline(LF). As
valid implementations of Git never created such a loose ref
diff --git a/Documentation/git-fsck.adoc b/Documentation/git-fsck.adoc
index 8f32800a83..11203ba925 100644
--- a/Documentation/git-fsck.adoc
+++ b/Documentation/git-fsck.adoc
@@ -12,7 +12,7 @@ SYNOPSIS
'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
[--[no-]full] [--strict] [--verbose] [--lost-found]
[--[no-]dangling] [--[no-]progress] [--connectivity-only]
- [--[no-]name-objects] [<object>...]
+ [--[no-]name-objects] [--[no-]references] [<object>...]
DESCRIPTION
-----------
@@ -104,6 +104,11 @@ care about this output and want to speed it up further.
progress status even if the standard error stream is not
directed to a terminal.
+--[no-]references::
+ Control whether to check the references database consistency
+ via 'git refs verify'. See linkgit:git-refs[1] for details.
+ The default is to check the references database.
+
CONFIGURATION
-------------
diff --git a/builtin/fsck.c b/builtin/fsck.c
index eea1d43647..8fbd1d6334 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -50,6 +50,7 @@ static int verbose;
static int show_progress = -1;
static int show_dangling = 1;
static int name_objects;
+static int check_references = 1;
#define ERROR_OBJECT 01
#define ERROR_REACHABLE 02
#define ERROR_PACK 04
@@ -905,11 +906,37 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress)
return res;
}
+static void fsck_refs(struct repository *r)
+{
+ struct child_process refs_verify = CHILD_PROCESS_INIT;
+ struct progress *progress = NULL;
+
+ if (show_progress)
+ progress = start_progress(r, _("Checking ref database"), 1);
+
+ if (verbose)
+ fprintf_ln(stderr, _("Checking ref database"));
+
+ child_process_init(&refs_verify);
+ refs_verify.git_cmd = 1;
+ strvec_pushl(&refs_verify.args, "refs", "verify", NULL);
+ if (verbose)
+ strvec_push(&refs_verify.args, "--verbose");
+ if (check_strict)
+ strvec_push(&refs_verify.args, "--strict");
+
+ if (run_command(&refs_verify))
+ errors_found |= ERROR_REFS;
+
+ display_progress(progress, 1);
+ stop_progress(&progress);
+}
+
static char const * const fsck_usage[] = {
N_("git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
" [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
" [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
- " [--[no-]name-objects] [<object>...]"),
+ " [--[no-]name-objects] [--[no-]references] [<object>...]"),
NULL
};
@@ -928,6 +955,7 @@ static struct option fsck_opts[] = {
N_("write dangling objects in .git/lost-found")),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")),
+ OPT_BOOL(0, "references", &check_references, N_("check reference database consistency")),
OPT_END(),
};
@@ -970,6 +998,9 @@ int cmd_fsck(int argc,
git_config(git_fsck_config, &fsck_obj_options);
prepare_repo_settings(the_repository);
+ if (check_references)
+ fsck_refs(the_repository);
+
if (connectivity_only) {
for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
for_each_packed_object(the_repository,
diff --git a/builtin/refs.c b/builtin/refs.c
index 44d592a94c..998d2a2c1c 100644
--- a/builtin/refs.c
+++ b/builtin/refs.c
@@ -91,7 +91,7 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
git_config(git_fsck_config, &fsck_refs_options);
prepare_repo_settings(the_repository);
- worktrees = get_worktrees();
+ worktrees = get_worktrees_without_reading_head();
for (size_t i = 0; worktrees[i]; i++)
ret |= refs_fsck(get_worktree_ref_store(worktrees[i]),
&fsck_refs_options, worktrees[i]);
diff --git a/fsck.h b/fsck.h
index a95ae7eb84..b1deae61ee 100644
--- a/fsck.h
+++ b/fsck.h
@@ -30,6 +30,8 @@ enum fsck_msg_type {
FUNC(BAD_EMAIL, ERROR) \
FUNC(BAD_NAME, ERROR) \
FUNC(BAD_OBJECT_SHA1, ERROR) \
+ FUNC(BAD_PACKED_REF_ENTRY, ERROR) \
+ FUNC(BAD_PACKED_REF_HEADER, ERROR) \
FUNC(BAD_PARENT_SHA1, ERROR) \
FUNC(BAD_REF_CONTENT, ERROR) \
FUNC(BAD_REF_FILETYPE, ERROR) \
@@ -53,6 +55,8 @@ enum fsck_msg_type {
FUNC(MISSING_TYPE, ERROR) \
FUNC(MISSING_TYPE_ENTRY, ERROR) \
FUNC(MULTIPLE_AUTHORS, ERROR) \
+ FUNC(PACKED_REF_ENTRY_NOT_TERMINATED, ERROR) \
+ FUNC(PACKED_REF_UNSORTED, ERROR) \
FUNC(TREE_NOT_SORTED, ERROR) \
FUNC(UNKNOWN_TYPE, ERROR) \
FUNC(ZERO_PADDED_DATE, ERROR) \
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index a7b6f74b6e..813e5020e4 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -4,6 +4,7 @@
#include "../git-compat-util.h"
#include "../config.h"
#include "../dir.h"
+#include "../fsck.h"
#include "../gettext.h"
#include "../hash.h"
#include "../hex.h"
@@ -299,14 +300,9 @@ struct snapshot_record {
size_t len;
};
-static int cmp_packed_ref_records(const void *v1, const void *v2,
- void *cb_data)
-{
- const struct snapshot *snapshot = cb_data;
- const struct snapshot_record *e1 = v1, *e2 = v2;
- const char *r1 = e1->start + snapshot_hexsz(snapshot) + 1;
- const char *r2 = e2->start + snapshot_hexsz(snapshot) + 1;
+static int cmp_packed_refname(const char *r1, const char *r2)
+{
while (1) {
if (*r1 == '\n')
return *r2 == '\n' ? 0 : -1;
@@ -321,6 +317,17 @@ static int cmp_packed_ref_records(const void *v1, const void *v2,
}
}
+static int cmp_packed_ref_records(const void *v1, const void *v2,
+ void *cb_data)
+{
+ const struct snapshot *snapshot = cb_data;
+ const struct snapshot_record *e1 = v1, *e2 = v2;
+ const char *r1 = e1->start + snapshot_hexsz(snapshot) + 1;
+ const char *r2 = e2->start + snapshot_hexsz(snapshot) + 1;
+
+ return cmp_packed_refname(r1, r2);
+}
+
/*
* Compare a snapshot record at `rec` to the specified NUL-terminated
* refname.
@@ -493,6 +500,21 @@ static void verify_buffer_safe(struct snapshot *snapshot)
last_line, eof - last_line);
}
+/*
+ * When parsing the "packed-refs" file, we will parse it line by line.
+ * Because we know the start pointer of the refname and the next
+ * newline pointer, we could calculate the length of the refname by
+ * subtracting the two pointers. However, there is a corner case where
+ * the refname contains corrupted embedded NUL characters. And
+ * `check_refname_format()` will not catch this when the truncated
+ * refname is still a valid refname. To prevent this, we need to check
+ * whether the refname contains the NUL characters.
+ */
+static int refname_contains_nul(struct strbuf *refname)
+{
+ return !!memchr(refname->buf, '\0', refname->len);
+}
+
#define SMALL_FILE_SIZE (32*1024)
/*
@@ -693,7 +715,7 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
tmp = xmemdupz(snapshot->buf, eol - snapshot->buf);
- if (!skip_prefix(tmp, "# pack-refs with:", (const char **)&p))
+ if (!skip_prefix(tmp, "# pack-refs with: ", (const char **)&p))
die_invalid_line(refs->path,
snapshot->buf,
snapshot->eof - snapshot->buf);
@@ -894,6 +916,9 @@ static int next_record(struct packed_ref_iterator *iter)
strbuf_add(&iter->refname_buf, p, eol - p);
iter->base.refname = iter->refname_buf.buf;
+ if (refname_contains_nul(&iter->refname_buf))
+ die("packed refname contains embedded NULL: %s", iter->base.refname);
+
if (check_refname_format(iter->base.refname, REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(iter->base.refname))
die("packed refname is dangerous: %s",
@@ -1748,15 +1773,329 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
return empty_ref_iterator_begin();
}
-static int packed_fsck(struct ref_store *ref_store UNUSED,
- struct fsck_options *o UNUSED,
+static int packed_fsck_ref_next_line(struct fsck_options *o,
+ unsigned long line_number, const char *start,
+ const char *eof, const char **eol)
+{
+ int ret = 0;
+
+ *eol = memchr(start, '\n', eof - start);
+ if (!*eol) {
+ struct strbuf packed_entry = STRBUF_INIT;
+ struct fsck_ref_report report = { 0 };
+
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_PACKED_REF_ENTRY_NOT_TERMINATED,
+ "'%.*s' is not terminated with a newline",
+ (int)(eof - start), start);
+
+ /*
+ * There is no newline but we still want to parse it to the end of
+ * the buffer.
+ */
+ *eol = eof;
+ strbuf_release(&packed_entry);
+ }
+
+ return ret;
+}
+
+static int packed_fsck_ref_header(struct fsck_options *o,
+ const char *start, const char *eol,
+ unsigned int *sorted)
+{
+ struct string_list traits = STRING_LIST_INIT_NODUP;
+ char *tmp_line;
+ int ret = 0;
+ char *p;
+
+ tmp_line = xmemdupz(start, eol - start);
+ if (!skip_prefix(tmp_line, "# pack-refs with: ", (const char **)&p)) {
+ struct fsck_ref_report report = { 0 };
+ report.path = "packed-refs.header";
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_HEADER,
+ "'%.*s' does not start with '# pack-refs with: '",
+ (int)(eol - start), start);
+ goto cleanup;
+ }
+
+ string_list_split_in_place(&traits, p, " ", -1);
+ *sorted = unsorted_string_list_has_string(&traits, "sorted");
+
+cleanup:
+ free(tmp_line);
+ string_list_clear(&traits, 0);
+ return ret;
+}
+
+static int packed_fsck_ref_peeled_line(struct fsck_options *o,
+ struct ref_store *ref_store,
+ unsigned long line_number,
+ const char *start, const char *eol)
+{
+ struct strbuf packed_entry = STRBUF_INIT;
+ struct fsck_ref_report report = { 0 };
+ struct object_id peeled;
+ const char *p;
+ int ret = 0;
+
+ /*
+ * Skip the '^' and parse the peeled oid.
+ */
+ start++;
+ if (parse_oid_hex_algop(start, &peeled, &p, ref_store->repo->hash_algo)) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_ENTRY,
+ "'%.*s' has invalid peeled oid",
+ (int)(eol - start), start);
+ goto cleanup;
+ }
+
+ if (p != eol) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_ENTRY,
+ "has trailing garbage after peeled oid '%.*s'",
+ (int)(eol - p), p);
+ goto cleanup;
+ }
+
+cleanup:
+ strbuf_release(&packed_entry);
+ return ret;
+}
+
+static int packed_fsck_ref_main_line(struct fsck_options *o,
+ struct ref_store *ref_store,
+ unsigned long line_number,
+ struct strbuf *refname,
+ const char *start, const char *eol)
+{
+ struct strbuf packed_entry = STRBUF_INIT;
+ struct fsck_ref_report report = { 0 };
+ struct object_id oid;
+ const char *p;
+ int ret = 0;
+
+ if (parse_oid_hex_algop(start, &oid, &p, ref_store->repo->hash_algo)) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_ENTRY,
+ "'%.*s' has invalid oid",
+ (int)(eol - start), start);
+ goto cleanup;
+ }
+
+ if (p == eol || !isspace(*p)) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_ENTRY,
+ "has no space after oid '%s' but with '%.*s'",
+ oid_to_hex(&oid), (int)(eol - p), p);
+ goto cleanup;
+ }
+
+ p++;
+ strbuf_reset(refname);
+ strbuf_add(refname, p, eol - p);
+ if (refname_contains_nul(refname)) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_ENTRY,
+ "refname '%s' contains NULL binaries",
+ refname->buf);
+ }
+
+ if (check_refname_format(refname->buf, 0)) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_REF_NAME,
+ "has bad refname '%s'", refname->buf);
+ }
+
+cleanup:
+ strbuf_release(&packed_entry);
+ return ret;
+}
+
+static int packed_fsck_ref_sorted(struct fsck_options *o,
+ struct ref_store *ref_store,
+ const char *start, const char *eof)
+{
+ size_t hexsz = ref_store->repo->hash_algo->hexsz;
+ struct strbuf packed_entry = STRBUF_INIT;
+ struct fsck_ref_report report = { 0 };
+ struct strbuf refname1 = STRBUF_INIT;
+ struct strbuf refname2 = STRBUF_INIT;
+ unsigned long line_number = 1;
+ const char *former = NULL;
+ const char *current;
+ const char *eol;
+ int ret = 0;
+
+ if (*start == '#') {
+ eol = memchr(start, '\n', eof - start);
+ start = eol + 1;
+ line_number++;
+ }
+
+ for (; start < eof; line_number++, start = eol + 1) {
+ eol = memchr(start, '\n', eof - start);
+
+ if (*start == '^')
+ continue;
+
+ if (!former) {
+ former = start + hexsz + 1;
+ continue;
+ }
+
+ current = start + hexsz + 1;
+ if (cmp_packed_refname(former, current) >= 0) {
+ const char *err_fmt =
+ "refname '%s' is less than previous refname '%s'";
+
+ eol = memchr(former, '\n', eof - former);
+ strbuf_add(&refname1, former, eol - former);
+ eol = memchr(current, '\n', eof - current);
+ strbuf_add(&refname2, current, eol - current);
+
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_PACKED_REF_UNSORTED,
+ err_fmt, refname2.buf, refname1.buf);
+ goto cleanup;
+ }
+ former = current;
+ }
+
+cleanup:
+ strbuf_release(&packed_entry);
+ strbuf_release(&refname1);
+ strbuf_release(&refname2);
+ return ret;
+}
+
+static int packed_fsck_ref_content(struct fsck_options *o,
+ struct ref_store *ref_store,
+ unsigned int *sorted,
+ const char *start, const char *eof)
+{
+ struct strbuf refname = STRBUF_INIT;
+ unsigned long line_number = 1;
+ const char *eol;
+ int ret = 0;
+
+ ret |= packed_fsck_ref_next_line(o, line_number, start, eof, &eol);
+ if (*start == '#') {
+ ret |= packed_fsck_ref_header(o, start, eol, sorted);
+
+ start = eol + 1;
+ line_number++;
+ }
+
+ while (start < eof) {
+ ret |= packed_fsck_ref_next_line(o, line_number, start, eof, &eol);
+ ret |= packed_fsck_ref_main_line(o, ref_store, line_number, &refname, start, eol);
+ start = eol + 1;
+ line_number++;
+ if (start < eof && *start == '^') {
+ ret |= packed_fsck_ref_next_line(o, line_number, start, eof, &eol);
+ ret |= packed_fsck_ref_peeled_line(o, ref_store, line_number,
+ start, eol);
+ start = eol + 1;
+ line_number++;
+ }
+ }
+
+ strbuf_release(&refname);
+ return ret;
+}
+
+static int packed_fsck(struct ref_store *ref_store,
+ struct fsck_options *o,
struct worktree *wt)
{
+ struct packed_ref_store *refs = packed_downcast(ref_store,
+ REF_STORE_READ, "fsck");
+ struct strbuf packed_ref_content = STRBUF_INIT;
+ unsigned int sorted = 0;
+ struct stat st;
+ int ret = 0;
+ int fd = -1;
if (!is_main_worktree(wt))
- return 0;
+ goto cleanup;
- return 0;
+ if (o->verbose)
+ fprintf_ln(stderr, "Checking packed-refs file %s", refs->path);
+
+ fd = open_nofollow(refs->path, O_RDONLY);
+ if (fd < 0) {
+ /*
+ * If the packed-refs file doesn't exist, there's nothing
+ * to check.
+ */
+ if (errno == ENOENT)
+ goto cleanup;
+
+ if (errno == ELOOP) {
+ struct fsck_ref_report report = { 0 };
+ report.path = "packed-refs";
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_REF_FILETYPE,
+ "not a regular file but a symlink");
+ goto cleanup;
+ }
+
+ ret = error_errno(_("unable to open '%s'"), refs->path);
+ goto cleanup;
+ } else if (fstat(fd, &st) < 0) {
+ ret = error_errno(_("unable to stat '%s'"), refs->path);
+ goto cleanup;
+ } else if (!S_ISREG(st.st_mode)) {
+ struct fsck_ref_report report = { 0 };
+ report.path = "packed-refs";
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_REF_FILETYPE,
+ "not a regular file");
+ goto cleanup;
+ }
+
+ if (strbuf_read(&packed_ref_content, fd, 0) < 0) {
+ ret = error_errno(_("unable to read '%s'"), refs->path);
+ goto cleanup;
+ }
+
+ ret = packed_fsck_ref_content(o, ref_store, &sorted, packed_ref_content.buf,
+ packed_ref_content.buf + packed_ref_content.len);
+ if (!ret && sorted)
+ ret = packed_fsck_ref_sorted(o, ref_store, packed_ref_content.buf,
+ packed_ref_content.buf + packed_ref_content.len);
+
+cleanup:
+ if (fd >= 0)
+ close(fd);
+ strbuf_release(&packed_ref_content);
+ return ret;
}
struct ref_storage_be refs_be_packed = {
diff --git a/t/t0602-reffiles-fsck.sh b/t/t0602-reffiles-fsck.sh
index d4a08b823b..9d1dc2144c 100755
--- a/t/t0602-reffiles-fsck.sh
+++ b/t/t0602-reffiles-fsck.sh
@@ -14,222 +14,229 @@ test_expect_success 'ref name should be checked' '
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 default-branch &&
- git tag default-tag &&
- git tag multi_hierarchy/default-tag &&
-
- cp $branch_dir_prefix/default-branch $branch_dir_prefix/@ &&
- git refs verify 2>err &&
- test_must_be_empty err &&
- rm $branch_dir_prefix/@ &&
-
- cp $tag_dir_prefix/default-tag $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/default-tag $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 &&
-
- for refname in ".refname-starts-with-dot" "~refname-has-stride"
- do
- cp $branch_dir_prefix/default-branch "$branch_dir_prefix/$refname" &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/heads/$refname: badRefName: invalid refname format
- EOF
- rm "$branch_dir_prefix/$refname" &&
- test_cmp expect err || return 1
- done &&
+ (
+ cd repo &&
- for refname in ".refname-starts-with-dot" "~refname-has-stride"
- do
- cp $tag_dir_prefix/default-tag "$tag_dir_prefix/$refname" &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/tags/$refname: badRefName: invalid refname format
- EOF
- rm "$tag_dir_prefix/$refname" &&
- test_cmp expect err || return 1
- done &&
+ git commit --allow-empty -m initial &&
+ git checkout -b default-branch &&
+ git tag default-tag &&
+ git tag multi_hierarchy/default-tag &&
- for refname in ".refname-starts-with-dot" "~refname-has-stride"
- do
- cp $tag_dir_prefix/multi_hierarchy/default-tag "$tag_dir_prefix/multi_hierarchy/$refname" &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/tags/multi_hierarchy/$refname: badRefName: invalid refname format
- EOF
- rm "$tag_dir_prefix/multi_hierarchy/$refname" &&
- test_cmp expect err || return 1
- done &&
+ cp $branch_dir_prefix/default-branch $branch_dir_prefix/@ &&
+ git refs verify 2>err &&
+ test_must_be_empty err &&
+ rm $branch_dir_prefix/@ &&
+
+ cp $tag_dir_prefix/default-tag $tag_dir_prefix/tag-1.lock &&
+ git refs verify 2>err &&
+ rm $tag_dir_prefix/tag-1.lock &&
+ test_must_be_empty err &&
- for refname in ".refname-starts-with-dot" "~refname-has-stride"
- do
- mkdir "$branch_dir_prefix/$refname" &&
- cp $branch_dir_prefix/default-branch "$branch_dir_prefix/$refname/default-branch" &&
+ cp $tag_dir_prefix/default-tag $tag_dir_prefix/.lock &&
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
- error: refs/heads/$refname/default-branch: badRefName: invalid refname format
+ error: refs/tags/.lock: badRefName: invalid refname format
EOF
- rm -r "$branch_dir_prefix/$refname" &&
- test_cmp expect err || return 1
- done
+ rm $tag_dir_prefix/.lock &&
+ test_cmp expect err &&
+
+ for refname in ".refname-starts-with-dot" "~refname-has-stride"
+ do
+ cp $branch_dir_prefix/default-branch "$branch_dir_prefix/$refname" &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/$refname: badRefName: invalid refname format
+ EOF
+ rm "$branch_dir_prefix/$refname" &&
+ test_cmp expect err || return 1
+ done &&
+
+ for refname in ".refname-starts-with-dot" "~refname-has-stride"
+ do
+ cp $tag_dir_prefix/default-tag "$tag_dir_prefix/$refname" &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/tags/$refname: badRefName: invalid refname format
+ EOF
+ rm "$tag_dir_prefix/$refname" &&
+ test_cmp expect err || return 1
+ done &&
+
+ for refname in ".refname-starts-with-dot" "~refname-has-stride"
+ do
+ cp $tag_dir_prefix/multi_hierarchy/default-tag "$tag_dir_prefix/multi_hierarchy/$refname" &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/tags/multi_hierarchy/$refname: badRefName: invalid refname format
+ EOF
+ rm "$tag_dir_prefix/multi_hierarchy/$refname" &&
+ test_cmp expect err || return 1
+ done &&
+
+ for refname in ".refname-starts-with-dot" "~refname-has-stride"
+ do
+ mkdir "$branch_dir_prefix/$refname" &&
+ cp $branch_dir_prefix/default-branch "$branch_dir_prefix/$refname/default-branch" &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/$refname/default-branch: badRefName: invalid refname format
+ EOF
+ rm -r "$branch_dir_prefix/$refname" &&
+ test_cmp expect err || return 1
+ done
+ )
'
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 &&
- cd repo &&
- git commit --allow-empty -m initial &&
- git checkout -b branch-1 &&
-
- 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/.branch-1 &&
- git -c fsck.badRefName=ignore refs verify 2>err &&
- test_must_be_empty err
+ (
+ cd repo &&
+ git commit --allow-empty -m initial &&
+ git checkout -b branch-1 &&
+
+ 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/.branch-1 &&
+ git -c fsck.badRefName=ignore refs verify 2>err &&
+ test_must_be_empty err
+ )
'
test_expect_success 'ref name check should work for multiple worktrees' '
test_when_finished "rm -rf repo" &&
git init repo &&
-
- cd repo &&
- test_commit initial &&
- git checkout -b branch-1 &&
- test_commit second &&
- git checkout -b branch-2 &&
- test_commit third &&
- git checkout -b branch-3 &&
- git worktree add ./worktree-1 branch-1 &&
- git worktree add ./worktree-2 branch-2 &&
- worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
- worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
-
(
- cd worktree-1 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-3
- ) &&
- (
- cd worktree-2 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-3
- ) &&
-
- cp $worktree1_refdir_prefix/branch-4 $worktree1_refdir_prefix/'\'' branch-5'\'' &&
- cp $worktree2_refdir_prefix/branch-4 $worktree2_refdir_prefix/'\''~branch-6'\'' &&
-
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
- error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
- EOF
- sort err >sorted_err &&
- test_cmp expect sorted_err &&
-
- for worktree in "worktree-1" "worktree-2"
- do
+ cd repo &&
+ test_commit initial &&
+ git checkout -b branch-1 &&
+ test_commit second &&
+ git checkout -b branch-2 &&
+ test_commit third &&
+ git checkout -b branch-3 &&
+ git worktree add ./worktree-1 branch-1 &&
+ git worktree add ./worktree-2 branch-2 &&
+ worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
+ worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
+
(
- cd $worktree &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
- error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
- EOF
- sort err >sorted_err &&
- test_cmp expect sorted_err || return 1
- )
- done
+ cd worktree-1 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-3
+ ) &&
+ (
+ cd worktree-2 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-3
+ ) &&
+
+ cp $worktree1_refdir_prefix/branch-4 $worktree1_refdir_prefix/'\'' branch-5'\'' &&
+ cp $worktree2_refdir_prefix/branch-4 $worktree2_refdir_prefix/'\''~branch-6'\'' &&
+
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
+ error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
+ EOF
+ sort err >sorted_err &&
+ test_cmp expect sorted_err &&
+
+ for worktree in "worktree-1" "worktree-2"
+ do
+ (
+ cd $worktree &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
+ error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
+ EOF
+ sort err >sorted_err &&
+ test_cmp expect sorted_err || return 1
+ )
+ done
+ )
'
test_expect_success 'regular ref content should be checked (individual)' '
test_when_finished "rm -rf repo" &&
git init repo &&
branch_dir_prefix=.git/refs/heads &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
- git refs verify 2>err &&
- test_must_be_empty err &&
+ git refs verify 2>err &&
+ test_must_be_empty err &&
- for bad_content in "$(git rev-parse main)x" "xfsazqfxcadas" "Xfsazqfxcadas"
- do
- printf "%s" $bad_content >$branch_dir_prefix/branch-bad &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/heads/branch-bad: badRefContent: $bad_content
- EOF
- rm $branch_dir_prefix/branch-bad &&
- test_cmp expect err || return 1
- done &&
+ for bad_content in "$(git rev-parse main)x" "xfsazqfxcadas" "Xfsazqfxcadas"
+ do
+ printf "%s" $bad_content >$branch_dir_prefix/branch-bad &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/branch-bad: badRefContent: $bad_content
+ EOF
+ rm $branch_dir_prefix/branch-bad &&
+ test_cmp expect err || return 1
+ done &&
- for bad_content in "$(git rev-parse main)x" "xfsazqfxcadas" "Xfsazqfxcadas"
- do
- printf "%s" $bad_content >$branch_dir_prefix/a/b/branch-bad &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/heads/a/b/branch-bad: badRefContent: $bad_content
- EOF
- rm $branch_dir_prefix/a/b/branch-bad &&
- test_cmp expect err || return 1
- done &&
-
- printf "%s" "$(git rev-parse main)" >$branch_dir_prefix/branch-no-newline &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
- EOF
- rm $branch_dir_prefix/branch-no-newline &&
- test_cmp expect err &&
-
- for trailing_content in " garbage" " more garbage"
- do
- printf "%s" "$(git rev-parse main)$trailing_content" >$branch_dir_prefix/branch-garbage &&
+ for bad_content in "$(git rev-parse main)x" "xfsazqfxcadas" "Xfsazqfxcadas"
+ do
+ printf "%s" $bad_content >$branch_dir_prefix/a/b/branch-bad &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/a/b/branch-bad: badRefContent: $bad_content
+ EOF
+ rm $branch_dir_prefix/a/b/branch-bad &&
+ test_cmp expect err || return 1
+ done &&
+
+ printf "%s" "$(git rev-parse main)" >$branch_dir_prefix/branch-no-newline &&
git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\''$trailing_content'\''
+ warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
EOF
- rm $branch_dir_prefix/branch-garbage &&
- test_cmp expect err || return 1
- done &&
+ rm $branch_dir_prefix/branch-no-newline &&
+ test_cmp expect err &&
- printf "%s\n\n\n" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage-special &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-garbage-special: trailingRefContent: has trailing garbage: '\''
+ for trailing_content in " garbage" " more garbage"
+ do
+ printf "%s" "$(git rev-parse main)$trailing_content" >$branch_dir_prefix/branch-garbage &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\''$trailing_content'\''
+ EOF
+ rm $branch_dir_prefix/branch-garbage &&
+ test_cmp expect err || return 1
+ done &&
+ printf "%s\n\n\n" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage-special &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-garbage-special: trailingRefContent: has trailing garbage: '\''
- '\''
- EOF
- rm $branch_dir_prefix/branch-garbage-special &&
- test_cmp expect err &&
- printf "%s\n\n\n garbage" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage-special &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-garbage-special: trailingRefContent: has trailing garbage: '\''
+ '\''
+ EOF
+ rm $branch_dir_prefix/branch-garbage-special &&
+ test_cmp expect err &&
+
+ printf "%s\n\n\n garbage" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage-special &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-garbage-special: trailingRefContent: has trailing garbage: '\''
- garbage'\''
- EOF
- rm $branch_dir_prefix/branch-garbage-special &&
- test_cmp expect err
+ garbage'\''
+ EOF
+ rm $branch_dir_prefix/branch-garbage-special &&
+ test_cmp expect err
+ )
'
test_expect_success 'regular ref content should be checked (aggregate)' '
@@ -237,99 +244,103 @@ test_expect_success 'regular ref content should be checked (aggregate)' '
git init repo &&
branch_dir_prefix=.git/refs/heads &&
tag_dir_prefix=.git/refs/tags &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
-
- bad_content_1=$(git rev-parse main)x &&
- bad_content_2=xfsazqfxcadas &&
- bad_content_3=Xfsazqfxcadas &&
- printf "%s" $bad_content_1 >$tag_dir_prefix/tag-bad-1 &&
- printf "%s" $bad_content_2 >$tag_dir_prefix/tag-bad-2 &&
- printf "%s" $bad_content_3 >$branch_dir_prefix/a/b/branch-bad &&
- printf "%s" "$(git rev-parse main)" >$branch_dir_prefix/branch-no-newline &&
- printf "%s garbage" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage &&
-
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/heads/a/b/branch-bad: badRefContent: $bad_content_3
- error: refs/tags/tag-bad-1: badRefContent: $bad_content_1
- error: refs/tags/tag-bad-2: badRefContent: $bad_content_2
- warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\'' garbage'\''
- warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
- EOF
- sort err >sorted_err &&
- test_cmp expect sorted_err
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
+
+ bad_content_1=$(git rev-parse main)x &&
+ bad_content_2=xfsazqfxcadas &&
+ bad_content_3=Xfsazqfxcadas &&
+ printf "%s" $bad_content_1 >$tag_dir_prefix/tag-bad-1 &&
+ printf "%s" $bad_content_2 >$tag_dir_prefix/tag-bad-2 &&
+ printf "%s" $bad_content_3 >$branch_dir_prefix/a/b/branch-bad &&
+ printf "%s" "$(git rev-parse main)" >$branch_dir_prefix/branch-no-newline &&
+ printf "%s garbage" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage &&
+
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/a/b/branch-bad: badRefContent: $bad_content_3
+ error: refs/tags/tag-bad-1: badRefContent: $bad_content_1
+ error: refs/tags/tag-bad-2: badRefContent: $bad_content_2
+ warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\'' garbage'\''
+ warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
+ EOF
+ sort err >sorted_err &&
+ test_cmp expect sorted_err
+ )
'
test_expect_success 'textual symref content should be checked (individual)' '
test_when_finished "rm -rf repo" &&
git init repo &&
branch_dir_prefix=.git/refs/heads &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
+
+ for good_referent in "refs/heads/branch" "HEAD"
+ do
+ printf "ref: %s\n" $good_referent >$branch_dir_prefix/branch-good &&
+ git refs verify 2>err &&
+ rm $branch_dir_prefix/branch-good &&
+ test_must_be_empty err || return 1
+ done &&
+
+ for bad_referent in "refs/heads/.branch" "refs/heads/~branch" "refs/heads/?branch"
+ do
+ printf "ref: %s\n" $bad_referent >$branch_dir_prefix/branch-bad &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/branch-bad: badReferentName: points to invalid refname '\''$bad_referent'\''
+ EOF
+ rm $branch_dir_prefix/branch-bad &&
+ test_cmp expect err || return 1
+ done &&
- for good_referent in "refs/heads/branch" "HEAD"
- do
- printf "ref: %s\n" $good_referent >$branch_dir_prefix/branch-good &&
+ printf "ref: refs/heads/branch" >$branch_dir_prefix/branch-no-newline &&
git refs verify 2>err &&
- rm $branch_dir_prefix/branch-good &&
- test_must_be_empty err || return 1
- done &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
+ EOF
+ rm $branch_dir_prefix/branch-no-newline &&
+ test_cmp expect err &&
- for bad_referent in "refs/heads/.branch" "refs/heads/~branch" "refs/heads/?branch"
- do
- printf "ref: %s\n" $bad_referent >$branch_dir_prefix/branch-bad &&
- test_must_fail git refs verify 2>err &&
+ printf "ref: refs/heads/branch " >$branch_dir_prefix/a/b/branch-trailing-1 &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/a/b/branch-trailing-1: refMissingNewline: misses LF at the end
+ warning: refs/heads/a/b/branch-trailing-1: trailingRefContent: has trailing whitespaces or newlines
+ EOF
+ rm $branch_dir_prefix/a/b/branch-trailing-1 &&
+ test_cmp expect err &&
+
+ printf "ref: refs/heads/branch\n\n" >$branch_dir_prefix/a/b/branch-trailing-2 &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/a/b/branch-trailing-2: trailingRefContent: has trailing whitespaces or newlines
+ EOF
+ rm $branch_dir_prefix/a/b/branch-trailing-2 &&
+ test_cmp expect err &&
+
+ printf "ref: refs/heads/branch \n" >$branch_dir_prefix/a/b/branch-trailing-3 &&
+ git refs verify 2>err &&
cat >expect <<-EOF &&
- error: refs/heads/branch-bad: badReferentName: points to invalid refname '\''$bad_referent'\''
- EOF
- rm $branch_dir_prefix/branch-bad &&
- test_cmp expect err || return 1
- done &&
-
- printf "ref: refs/heads/branch" >$branch_dir_prefix/branch-no-newline &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
- EOF
- rm $branch_dir_prefix/branch-no-newline &&
- test_cmp expect err &&
-
- printf "ref: refs/heads/branch " >$branch_dir_prefix/a/b/branch-trailing-1 &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/a/b/branch-trailing-1: refMissingNewline: misses LF at the end
- warning: refs/heads/a/b/branch-trailing-1: trailingRefContent: has trailing whitespaces or newlines
- EOF
- rm $branch_dir_prefix/a/b/branch-trailing-1 &&
- test_cmp expect err &&
-
- printf "ref: refs/heads/branch\n\n" >$branch_dir_prefix/a/b/branch-trailing-2 &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/a/b/branch-trailing-2: trailingRefContent: has trailing whitespaces or newlines
- EOF
- rm $branch_dir_prefix/a/b/branch-trailing-2 &&
- test_cmp expect err &&
-
- printf "ref: refs/heads/branch \n" >$branch_dir_prefix/a/b/branch-trailing-3 &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/a/b/branch-trailing-3: trailingRefContent: has trailing whitespaces or newlines
- EOF
- rm $branch_dir_prefix/a/b/branch-trailing-3 &&
- test_cmp expect err &&
-
- printf "ref: refs/heads/branch \n " >$branch_dir_prefix/a/b/branch-complicated &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/a/b/branch-complicated: refMissingNewline: misses LF at the end
- warning: refs/heads/a/b/branch-complicated: trailingRefContent: has trailing whitespaces or newlines
- EOF
- rm $branch_dir_prefix/a/b/branch-complicated &&
- test_cmp expect err
+ warning: refs/heads/a/b/branch-trailing-3: trailingRefContent: has trailing whitespaces or newlines
+ EOF
+ rm $branch_dir_prefix/a/b/branch-trailing-3 &&
+ test_cmp expect err &&
+
+ printf "ref: refs/heads/branch \n " >$branch_dir_prefix/a/b/branch-complicated &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/a/b/branch-complicated: refMissingNewline: misses LF at the end
+ warning: refs/heads/a/b/branch-complicated: trailingRefContent: has trailing whitespaces or newlines
+ EOF
+ rm $branch_dir_prefix/a/b/branch-complicated &&
+ test_cmp expect err
+ )
'
test_expect_success 'textual symref content should be checked (aggregate)' '
@@ -337,32 +348,34 @@ test_expect_success 'textual symref content should be checked (aggregate)' '
git init repo &&
branch_dir_prefix=.git/refs/heads &&
tag_dir_prefix=.git/refs/tags &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
-
- printf "ref: refs/heads/branch\n" >$branch_dir_prefix/branch-good &&
- printf "ref: HEAD\n" >$branch_dir_prefix/branch-head &&
- printf "ref: refs/heads/branch" >$branch_dir_prefix/branch-no-newline-1 &&
- printf "ref: refs/heads/branch " >$branch_dir_prefix/a/b/branch-trailing-1 &&
- printf "ref: refs/heads/branch\n\n" >$branch_dir_prefix/a/b/branch-trailing-2 &&
- printf "ref: refs/heads/branch \n" >$branch_dir_prefix/a/b/branch-trailing-3 &&
- printf "ref: refs/heads/branch \n " >$branch_dir_prefix/a/b/branch-complicated &&
- printf "ref: refs/heads/.branch\n" >$branch_dir_prefix/branch-bad-1 &&
-
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/heads/branch-bad-1: badReferentName: points to invalid refname '\''refs/heads/.branch'\''
- warning: refs/heads/a/b/branch-complicated: refMissingNewline: misses LF at the end
- warning: refs/heads/a/b/branch-complicated: trailingRefContent: has trailing whitespaces or newlines
- warning: refs/heads/a/b/branch-trailing-1: refMissingNewline: misses LF at the end
- warning: refs/heads/a/b/branch-trailing-1: trailingRefContent: has trailing whitespaces or newlines
- warning: refs/heads/a/b/branch-trailing-2: trailingRefContent: has trailing whitespaces or newlines
- warning: refs/heads/a/b/branch-trailing-3: trailingRefContent: has trailing whitespaces or newlines
- warning: refs/heads/branch-no-newline-1: refMissingNewline: misses LF at the end
- EOF
- sort err >sorted_err &&
- test_cmp expect sorted_err
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
+
+ printf "ref: refs/heads/branch\n" >$branch_dir_prefix/branch-good &&
+ printf "ref: HEAD\n" >$branch_dir_prefix/branch-head &&
+ printf "ref: refs/heads/branch" >$branch_dir_prefix/branch-no-newline-1 &&
+ printf "ref: refs/heads/branch " >$branch_dir_prefix/a/b/branch-trailing-1 &&
+ printf "ref: refs/heads/branch\n\n" >$branch_dir_prefix/a/b/branch-trailing-2 &&
+ printf "ref: refs/heads/branch \n" >$branch_dir_prefix/a/b/branch-trailing-3 &&
+ printf "ref: refs/heads/branch \n " >$branch_dir_prefix/a/b/branch-complicated &&
+ printf "ref: refs/heads/.branch\n" >$branch_dir_prefix/branch-bad-1 &&
+
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/branch-bad-1: badReferentName: points to invalid refname '\''refs/heads/.branch'\''
+ warning: refs/heads/a/b/branch-complicated: refMissingNewline: misses LF at the end
+ warning: refs/heads/a/b/branch-complicated: trailingRefContent: has trailing whitespaces or newlines
+ warning: refs/heads/a/b/branch-trailing-1: refMissingNewline: misses LF at the end
+ warning: refs/heads/a/b/branch-trailing-1: trailingRefContent: has trailing whitespaces or newlines
+ warning: refs/heads/a/b/branch-trailing-2: trailingRefContent: has trailing whitespaces or newlines
+ warning: refs/heads/a/b/branch-trailing-3: trailingRefContent: has trailing whitespaces or newlines
+ warning: refs/heads/branch-no-newline-1: refMissingNewline: misses LF at the end
+ EOF
+ sort err >sorted_err &&
+ test_cmp expect sorted_err
+ )
'
test_expect_success 'the target of the textual symref should be checked' '
@@ -370,230 +383,490 @@ test_expect_success 'the target of the textual symref should be checked' '
git init repo &&
branch_dir_prefix=.git/refs/heads &&
tag_dir_prefix=.git/refs/tags &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
+
+ for good_referent in "refs/heads/branch" "HEAD" "refs/tags/tag"
+ do
+ printf "ref: %s\n" $good_referent >$branch_dir_prefix/branch-good &&
+ git refs verify 2>err &&
+ rm $branch_dir_prefix/branch-good &&
+ test_must_be_empty err || return 1
+ done &&
+
+ for nonref_referent in "refs-back/heads/branch" "refs-back/tags/tag" "reflogs/refs/heads/branch"
+ do
+ printf "ref: %s\n" $nonref_referent >$branch_dir_prefix/branch-bad-1 &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-bad-1: symrefTargetIsNotARef: points to non-ref target '\''$nonref_referent'\''
+ EOF
+ rm $branch_dir_prefix/branch-bad-1 &&
+ test_cmp expect err || return 1
+ done
+ )
+'
+
+test_expect_success SYMLINKS 'symlink symref content 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 &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
- for good_referent in "refs/heads/branch" "HEAD" "refs/tags/tag"
- do
- printf "ref: %s\n" $good_referent >$branch_dir_prefix/branch-good &&
+ ln -sf ./main $branch_dir_prefix/branch-symbolic-good &&
git refs verify 2>err &&
- rm $branch_dir_prefix/branch-good &&
- test_must_be_empty err || return 1
- done &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
+ EOF
+ rm $branch_dir_prefix/branch-symbolic-good &&
+ test_cmp expect err &&
- for nonref_referent in "refs-back/heads/branch" "refs-back/tags/tag" "reflogs/refs/heads/branch"
- do
- printf "ref: %s\n" $nonref_referent >$branch_dir_prefix/branch-bad-1 &&
+ ln -sf ../../logs/branch-escape $branch_dir_prefix/branch-symbolic &&
git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: refs/heads/branch-bad-1: symrefTargetIsNotARef: points to non-ref target '\''$nonref_referent'\''
+ warning: refs/heads/branch-symbolic: symlinkRef: use deprecated symbolic link for symref
+ warning: refs/heads/branch-symbolic: symrefTargetIsNotARef: points to non-ref target '\''logs/branch-escape'\''
+ EOF
+ rm $branch_dir_prefix/branch-symbolic &&
+ test_cmp expect err &&
+
+ ln -sf ./"branch " $branch_dir_prefix/branch-symbolic-bad &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-symbolic-bad: symlinkRef: use deprecated symbolic link for symref
+ error: refs/heads/branch-symbolic-bad: badReferentName: points to invalid refname '\''refs/heads/branch '\''
+ EOF
+ rm $branch_dir_prefix/branch-symbolic-bad &&
+ test_cmp expect err &&
+
+ ln -sf ./".tag" $tag_dir_prefix/tag-symbolic-1 &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/tags/tag-symbolic-1: symlinkRef: use deprecated symbolic link for symref
+ error: refs/tags/tag-symbolic-1: badReferentName: points to invalid refname '\''refs/tags/.tag'\''
EOF
- rm $branch_dir_prefix/branch-bad-1 &&
- test_cmp expect err || return 1
- done
+ rm $tag_dir_prefix/tag-symbolic-1 &&
+ test_cmp expect err
+ )
'
-test_expect_success SYMLINKS 'symlink symref content should be checked' '
+test_expect_success SYMLINKS 'symlink symref content should be checked (worktree)' '
test_when_finished "rm -rf repo" &&
git init repo &&
- branch_dir_prefix=.git/refs/heads &&
- tag_dir_prefix=.git/refs/tags &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
-
- ln -sf ./main $branch_dir_prefix/branch-symbolic-good &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
- EOF
- rm $branch_dir_prefix/branch-symbolic-good &&
- test_cmp expect err &&
-
- ln -sf ../../logs/branch-escape $branch_dir_prefix/branch-symbolic &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-symbolic: symlinkRef: use deprecated symbolic link for symref
- warning: refs/heads/branch-symbolic: symrefTargetIsNotARef: points to non-ref target '\''logs/branch-escape'\''
- EOF
- rm $branch_dir_prefix/branch-symbolic &&
- test_cmp expect err &&
-
- ln -sf ./"branch " $branch_dir_prefix/branch-symbolic-bad &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-symbolic-bad: symlinkRef: use deprecated symbolic link for symref
- error: refs/heads/branch-symbolic-bad: badReferentName: points to invalid refname '\''refs/heads/branch '\''
- EOF
- rm $branch_dir_prefix/branch-symbolic-bad &&
- test_cmp expect err &&
-
- ln -sf ./".tag" $tag_dir_prefix/tag-symbolic-1 &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/tags/tag-symbolic-1: symlinkRef: use deprecated symbolic link for symref
- error: refs/tags/tag-symbolic-1: badReferentName: points to invalid refname '\''refs/tags/.tag'\''
- EOF
- rm $tag_dir_prefix/tag-symbolic-1 &&
- test_cmp expect err
+ (
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git branch branch-3 &&
+ git worktree add ./worktree-1 branch-2 &&
+ git worktree add ./worktree-2 branch-3 &&
+ main_worktree_refdir_prefix=.git/refs/heads &&
+ worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
+ worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
+
+ (
+ cd worktree-1 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-1
+ ) &&
+ (
+ cd worktree-2 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-1
+ ) &&
+
+ ln -sf ../../../../refs/heads/good-branch $worktree1_refdir_prefix/branch-symbolic-good &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-1/refs/worktree/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
+ EOF
+ rm $worktree1_refdir_prefix/branch-symbolic-good &&
+ test_cmp expect err &&
+
+ ln -sf ../../../../worktrees/worktree-1/good-branch $worktree2_refdir_prefix/branch-symbolic-good &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-2/refs/worktree/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
+ EOF
+ rm $worktree2_refdir_prefix/branch-symbolic-good &&
+ test_cmp expect err &&
+
+ ln -sf ../../worktrees/worktree-2/good-branch $main_worktree_refdir_prefix/branch-symbolic-good &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
+ EOF
+ rm $main_worktree_refdir_prefix/branch-symbolic-good &&
+ test_cmp expect err &&
+
+ ln -sf ../../../../logs/branch-escape $worktree1_refdir_prefix/branch-symbolic &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-1/refs/worktree/branch-symbolic: symlinkRef: use deprecated symbolic link for symref
+ warning: worktrees/worktree-1/refs/worktree/branch-symbolic: symrefTargetIsNotARef: points to non-ref target '\''logs/branch-escape'\''
+ EOF
+ rm $worktree1_refdir_prefix/branch-symbolic &&
+ test_cmp expect err &&
+
+ for bad_referent_name in ".tag" "branch "
+ do
+ ln -sf ./"$bad_referent_name" $worktree1_refdir_prefix/bad-symbolic &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-1/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
+ error: worktrees/worktree-1/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''worktrees/worktree-1/refs/worktree/$bad_referent_name'\''
+ EOF
+ rm $worktree1_refdir_prefix/bad-symbolic &&
+ test_cmp expect err &&
+
+ ln -sf ../../../../refs/heads/"$bad_referent_name" $worktree1_refdir_prefix/bad-symbolic &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-1/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
+ error: worktrees/worktree-1/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''refs/heads/$bad_referent_name'\''
+ EOF
+ rm $worktree1_refdir_prefix/bad-symbolic &&
+ test_cmp expect err &&
+
+ ln -sf ./"$bad_referent_name" $worktree2_refdir_prefix/bad-symbolic &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-2/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
+ error: worktrees/worktree-2/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''worktrees/worktree-2/refs/worktree/$bad_referent_name'\''
+ EOF
+ rm $worktree2_refdir_prefix/bad-symbolic &&
+ test_cmp expect err &&
+
+ ln -sf ../../../../refs/heads/"$bad_referent_name" $worktree2_refdir_prefix/bad-symbolic &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-2/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
+ error: worktrees/worktree-2/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''refs/heads/$bad_referent_name'\''
+ EOF
+ rm $worktree2_refdir_prefix/bad-symbolic &&
+ test_cmp expect err || return 1
+ done
+ )
'
-test_expect_success SYMLINKS 'symlink symref content should be checked (worktree)' '
+test_expect_success 'ref content checks should work with worktrees' '
test_when_finished "rm -rf repo" &&
git init repo &&
- cd repo &&
- test_commit default &&
- git branch branch-1 &&
- git branch branch-2 &&
- git branch branch-3 &&
- git worktree add ./worktree-1 branch-2 &&
- git worktree add ./worktree-2 branch-3 &&
- main_worktree_refdir_prefix=.git/refs/heads &&
- worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
- worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
-
- (
- cd worktree-1 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-1
- ) &&
(
- cd worktree-2 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-1
- ) &&
-
- ln -sf ../../../../refs/heads/good-branch $worktree1_refdir_prefix/branch-symbolic-good &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
- EOF
- rm $worktree1_refdir_prefix/branch-symbolic-good &&
- test_cmp expect err &&
-
- ln -sf ../../../../worktrees/worktree-1/good-branch $worktree2_refdir_prefix/branch-symbolic-good &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: worktrees/worktree-2/refs/worktree/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
- EOF
- rm $worktree2_refdir_prefix/branch-symbolic-good &&
- test_cmp expect err &&
-
- ln -sf ../../worktrees/worktree-2/good-branch $main_worktree_refdir_prefix/branch-symbolic-good &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
- EOF
- rm $main_worktree_refdir_prefix/branch-symbolic-good &&
- test_cmp expect err &&
-
- ln -sf ../../../../logs/branch-escape $worktree1_refdir_prefix/branch-symbolic &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/branch-symbolic: symlinkRef: use deprecated symbolic link for symref
- warning: worktrees/worktree-1/refs/worktree/branch-symbolic: symrefTargetIsNotARef: points to non-ref target '\''logs/branch-escape'\''
- EOF
- rm $worktree1_refdir_prefix/branch-symbolic &&
- test_cmp expect err &&
-
- for bad_referent_name in ".tag" "branch "
- do
- ln -sf ./"$bad_referent_name" $worktree1_refdir_prefix/bad-symbolic &&
- test_must_fail git refs verify 2>err &&
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git branch branch-3 &&
+ git worktree add ./worktree-1 branch-2 &&
+ git worktree add ./worktree-2 branch-3 &&
+ worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
+ worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
+
+ (
+ cd worktree-1 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-1
+ ) &&
+ (
+ cd worktree-2 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-1
+ ) &&
+
+ for bad_content in "$(git rev-parse HEAD)x" "xfsazqfxcadas" "Xfsazqfxcadas"
+ do
+ printf "%s" $bad_content >$worktree1_refdir_prefix/bad-branch-1 &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: worktrees/worktree-1/refs/worktree/bad-branch-1: badRefContent: $bad_content
+ EOF
+ rm $worktree1_refdir_prefix/bad-branch-1 &&
+ test_cmp expect err || return 1
+ done &&
+
+ for bad_content in "$(git rev-parse HEAD)x" "xfsazqfxcadas" "Xfsazqfxcadas"
+ do
+ printf "%s" $bad_content >$worktree2_refdir_prefix/bad-branch-2 &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: worktrees/worktree-2/refs/worktree/bad-branch-2: badRefContent: $bad_content
+ EOF
+ rm $worktree2_refdir_prefix/bad-branch-2 &&
+ test_cmp expect err || return 1
+ done &&
+
+ printf "%s" "$(git rev-parse HEAD)" >$worktree1_refdir_prefix/branch-no-newline &&
+ git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
- error: worktrees/worktree-1/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''worktrees/worktree-1/refs/worktree/$bad_referent_name'\''
+ warning: worktrees/worktree-1/refs/worktree/branch-no-newline: refMissingNewline: misses LF at the end
EOF
- rm $worktree1_refdir_prefix/bad-symbolic &&
+ rm $worktree1_refdir_prefix/branch-no-newline &&
test_cmp expect err &&
- ln -sf ../../../../refs/heads/"$bad_referent_name" $worktree1_refdir_prefix/bad-symbolic &&
- test_must_fail git refs verify 2>err &&
+ printf "%s garbage" "$(git rev-parse HEAD)" >$worktree1_refdir_prefix/branch-garbage &&
+ git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
- error: worktrees/worktree-1/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''refs/heads/$bad_referent_name'\''
+ warning: worktrees/worktree-1/refs/worktree/branch-garbage: trailingRefContent: has trailing garbage: '\'' garbage'\''
EOF
- rm $worktree1_refdir_prefix/bad-symbolic &&
- test_cmp expect err &&
+ rm $worktree1_refdir_prefix/branch-garbage &&
+ test_cmp expect err
+ )
+'
- ln -sf ./"$bad_referent_name" $worktree2_refdir_prefix/bad-symbolic &&
+test_expect_success SYMLINKS 'the filetype of packed-refs should be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git branch branch-3 &&
+ git pack-refs --all &&
+
+ mv .git/packed-refs .git/packed-refs-back &&
+ ln -sf packed-refs-back .git/packed-refs &&
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: worktrees/worktree-2/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
- error: worktrees/worktree-2/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''worktrees/worktree-2/refs/worktree/$bad_referent_name'\''
+ error: packed-refs: badRefFiletype: not a regular file but a symlink
EOF
- rm $worktree2_refdir_prefix/bad-symbolic &&
+ rm .git/packed-refs &&
test_cmp expect err &&
- ln -sf ../../../../refs/heads/"$bad_referent_name" $worktree2_refdir_prefix/bad-symbolic &&
+ mkdir .git/packed-refs &&
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: worktrees/worktree-2/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
- error: worktrees/worktree-2/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''refs/heads/$bad_referent_name'\''
+ error: packed-refs: badRefFiletype: not a regular file
EOF
- rm $worktree2_refdir_prefix/bad-symbolic &&
- test_cmp expect err || return 1
- done
+ rm -r .git/packed-refs &&
+ test_cmp expect err
+ )
'
-test_expect_success 'ref content checks should work with worktrees' '
+test_expect_success 'packed-refs header should be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit default &&
+
+ git refs verify 2>err &&
+ test_must_be_empty err &&
+
+ for bad_header in "# pack-refs wit: peeled fully-peeled sorted " \
+ "# pack-refs with traits: peeled fully-peeled sorted " \
+ "# pack-refs with a: peeled fully-peeled" \
+ "# pack-refs with:peeled fully-peeled sorted"
+ do
+ printf "%s\n" "$bad_header" >.git/packed-refs &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: packed-refs.header: badPackedRefHeader: '\''$bad_header'\'' does not start with '\''# pack-refs with: '\''
+ EOF
+ rm .git/packed-refs &&
+ test_cmp expect err || return 1
+ done
+ )
+'
+
+test_expect_success 'packed-refs missing header should not be reported' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit default &&
+
+ printf "$(git rev-parse HEAD) refs/heads/main\n" >.git/packed-refs &&
+ git refs verify 2>err &&
+ test_must_be_empty err
+ )
+'
+
+test_expect_success 'packed-refs unknown traits should not be reported' '
test_when_finished "rm -rf repo" &&
git init repo &&
- cd repo &&
- test_commit default &&
- git branch branch-1 &&
- git branch branch-2 &&
- git branch branch-3 &&
- git worktree add ./worktree-1 branch-2 &&
- git worktree add ./worktree-2 branch-3 &&
- worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
- worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
+ (
+ cd repo &&
+ test_commit default &&
+
+ printf "# pack-refs with: peeled fully-peeled sorted foo\n" >.git/packed-refs &&
+ git refs verify 2>err &&
+ test_must_be_empty err
+ )
+'
+test_expect_success 'packed-refs content should be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
(
- cd worktree-1 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-1
- ) &&
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git tag -a annotated-tag-1 -m tag-1 &&
+ git tag -a annotated-tag-2 -m tag-2 &&
+
+ branch_1_oid=$(git rev-parse branch-1) &&
+ branch_2_oid=$(git rev-parse branch-2) &&
+ tag_1_oid=$(git rev-parse annotated-tag-1) &&
+ tag_2_oid=$(git rev-parse annotated-tag-2) &&
+ tag_1_peeled_oid=$(git rev-parse annotated-tag-1^{}) &&
+ tag_2_peeled_oid=$(git rev-parse annotated-tag-2^{}) &&
+ short_oid=$(printf "%s" $tag_1_peeled_oid | cut -c 1-4) &&
+
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled sorted
+ $short_oid refs/heads/branch-1
+ ${branch_1_oid}x
+ $branch_2_oid refs/heads/bad-branch
+ $branch_2_oid refs/heads/branch.
+ $tag_1_oid refs/tags/annotated-tag-3
+ ^$short_oid
+ $tag_2_oid refs/tags/annotated-tag-4.
+ ^$tag_2_peeled_oid garbage
+ EOF
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: packed-refs line 2: badPackedRefEntry: '\''$short_oid refs/heads/branch-1'\'' has invalid oid
+ error: packed-refs line 3: badPackedRefEntry: has no space after oid '\''$branch_1_oid'\'' but with '\''x'\''
+ error: packed-refs line 4: badRefName: has bad refname '\'' refs/heads/bad-branch'\''
+ error: packed-refs line 5: badRefName: has bad refname '\''refs/heads/branch.'\''
+ error: packed-refs line 7: badPackedRefEntry: '\''$short_oid'\'' has invalid peeled oid
+ error: packed-refs line 8: badRefName: has bad refname '\''refs/tags/annotated-tag-4.'\''
+ error: packed-refs line 9: badPackedRefEntry: has trailing garbage after peeled oid '\'' garbage'\''
+ EOF
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'packed-ref with sorted trait should be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
(
- cd worktree-2 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-1
- ) &&
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git tag -a annotated-tag-1 -m tag-1 &&
+ branch_1_oid=$(git rev-parse branch-1) &&
+ branch_2_oid=$(git rev-parse branch-2) &&
+ tag_1_oid=$(git rev-parse annotated-tag-1) &&
+ tag_1_peeled_oid=$(git rev-parse annotated-tag-1^{}) &&
+ refname1="refs/heads/main" &&
+ refname2="refs/heads/foo" &&
+ refname3="refs/tags/foo" &&
+
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled sorted
+ EOF
+ git refs verify 2>err &&
+ rm .git/packed-refs &&
+ test_must_be_empty err &&
- for bad_content in "$(git rev-parse HEAD)x" "xfsazqfxcadas" "Xfsazqfxcadas"
- do
- printf "%s" $bad_content >$worktree1_refdir_prefix/bad-branch-1 &&
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled sorted
+ $branch_2_oid $refname1
+ EOF
+ git refs verify 2>err &&
+ rm .git/packed-refs &&
+ test_must_be_empty err &&
+
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled sorted
+ $branch_2_oid $refname1
+ $branch_1_oid $refname2
+ $tag_1_oid $refname3
+ EOF
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
- error: worktrees/worktree-1/refs/worktree/bad-branch-1: badRefContent: $bad_content
+ error: packed-refs line 3: packedRefUnsorted: refname '\''$refname2'\'' is less than previous refname '\''$refname1'\''
EOF
- rm $worktree1_refdir_prefix/bad-branch-1 &&
- test_cmp expect err || return 1
- done &&
+ rm .git/packed-refs &&
+ test_cmp expect err &&
- for bad_content in "$(git rev-parse HEAD)x" "xfsazqfxcadas" "Xfsazqfxcadas"
- do
- printf "%s" $bad_content >$worktree2_refdir_prefix/bad-branch-2 &&
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled sorted
+ $tag_1_oid $refname3
+ ^$tag_1_peeled_oid
+ $branch_2_oid $refname2
+ EOF
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
- error: worktrees/worktree-2/refs/worktree/bad-branch-2: badRefContent: $bad_content
- EOF
- rm $worktree2_refdir_prefix/bad-branch-2 &&
- test_cmp expect err || return 1
- done &&
-
- printf "%s" "$(git rev-parse HEAD)" >$worktree1_refdir_prefix/branch-no-newline &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/branch-no-newline: refMissingNewline: misses LF at the end
- EOF
- rm $worktree1_refdir_prefix/branch-no-newline &&
- test_cmp expect err &&
-
- printf "%s garbage" "$(git rev-parse HEAD)" >$worktree1_refdir_prefix/branch-garbage &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/branch-garbage: trailingRefContent: has trailing garbage: '\'' garbage'\''
- EOF
- rm $worktree1_refdir_prefix/branch-garbage &&
- test_cmp expect err
+ error: packed-refs line 4: packedRefUnsorted: refname '\''$refname2'\'' is less than previous refname '\''$refname3'\''
+ EOF
+ rm .git/packed-refs &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'packed-ref without sorted trait should not be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git tag -a annotated-tag-1 -m tag-1 &&
+ branch_1_oid=$(git rev-parse branch-1) &&
+ branch_2_oid=$(git rev-parse branch-2) &&
+ tag_1_oid=$(git rev-parse annotated-tag-1) &&
+ tag_1_peeled_oid=$(git rev-parse annotated-tag-1^{}) &&
+ refname1="refs/heads/main" &&
+ refname2="refs/heads/foo" &&
+ refname3="refs/tags/foo" &&
+
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled
+ $branch_2_oid $refname1
+ $branch_1_oid $refname2
+ EOF
+ git refs verify 2>err &&
+ test_must_be_empty err
+ )
+'
+
+test_expect_success '--[no-]references option should apply to fsck' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ branch_dir_prefix=.git/refs/heads &&
+ (
+ cd repo &&
+ test_commit default &&
+ for trailing_content in " garbage" " more garbage"
+ do
+ printf "%s" "$(git rev-parse HEAD)$trailing_content" >$branch_dir_prefix/branch-garbage &&
+ git fsck 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\''$trailing_content'\''
+ EOF
+ rm $branch_dir_prefix/branch-garbage &&
+ test_cmp expect err || return 1
+ done &&
+
+ for trailing_content in " garbage" " more garbage"
+ do
+ printf "%s" "$(git rev-parse HEAD)$trailing_content" >$branch_dir_prefix/branch-garbage &&
+ git fsck --references 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\''$trailing_content'\''
+ EOF
+ rm $branch_dir_prefix/branch-garbage &&
+ test_cmp expect err || return 1
+ done &&
+
+ for trailing_content in " garbage" " more garbage"
+ do
+ printf "%s" "$(git rev-parse HEAD)$trailing_content" >$branch_dir_prefix/branch-garbage &&
+ git fsck --no-references 2>err &&
+ rm $branch_dir_prefix/branch-garbage &&
+ test_must_be_empty err || return 1
+ done
+ )
'
test_done
diff --git a/worktree.c b/worktree.c
index 6449b6798d..c34b9eb74e 100644
--- a/worktree.c
+++ b/worktree.c
@@ -199,6 +199,11 @@ struct worktree **get_worktrees(void)
return get_worktrees_internal(0);
}
+struct worktree **get_worktrees_without_reading_head(void)
+{
+ return get_worktrees_internal(1);
+}
+
char *get_worktree_git_dir(const struct worktree *wt)
{
if (!wt)
diff --git a/worktree.h b/worktree.h
index 16368588a0..e4bcccdc0a 100644
--- a/worktree.h
+++ b/worktree.h
@@ -31,6 +31,14 @@ struct worktree {
struct worktree **get_worktrees(void);
/*
+ * Like `get_worktrees`, but does not read HEAD. Skip reading HEAD allows to
+ * get the worktree without worrying about failures pertaining to parsing
+ * the HEAD ref. This is useful in contexts where it is assumed that the
+ * refdb may not be in a consistent state.
+ */
+struct worktree **get_worktrees_without_reading_head(void);
+
+/*
* Returns 1 if linked worktrees exist, 0 otherwise.
*/
int submodule_uses_worktrees(const char *path);