Skip to content

Commit 1cc8811

Browse files
shejialuogitster
authored andcommitted
packed-backend: check whether the "packed-refs" is sorted
We will always try to sort the "packed-refs" increasingly by comparing the refname. So, we should add checks to verify whether the "packed-refs" is sorted. It may seem that we could add a new "struct strbuf refname" into the "struct fsck_packed_ref_entry" and during the parsing process, we could store the refname into the entry and then we could compare later. However, this is not a good design due to the following reasons: 1. Because we need to store the state across the whole checking lifetime, we would consume a lot of memory if there are many entries in the "packed-refs" file. 2. The most important is that we cannot reuse the existing compare functions which cause repetition. So, instead of storing the "struct strbuf", let's use the existing structure "struct snaphost_record". And thus we could use the existing function "cmp_packed_ref_records". However, this function need an extra parameter for "struct snaphost". Extract the common part into a new function "cmp_packed_ref_records" to reuse this function to compare. Then, create a new function "packed_fsck_ref_sorted" to use the new fsck message "packedRefUnsorted(ERROR)" to report to the user. Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: shejialuo <shejialuo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 8e182c5 commit 1cc8811

File tree

4 files changed

+111
-11
lines changed

4 files changed

+111
-11
lines changed

Documentation/fsck-msgids.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@
190190
`packedRefMissingHeader`::
191191
(INFO) The "packed-refs" file does not contain the header.
192192

193+
`packedRefUnsorted`::
194+
(ERROR) The "packed-refs" file is not sorted.
195+
193196
`refMissingNewline`::
194197
(INFO) A loose ref that does not end with newline(LF). As
195198
valid implementations of Git never created such a loose ref

fsck.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ enum fsck_msg_type {
5656
FUNC(MISSING_TYPE_ENTRY, ERROR) \
5757
FUNC(MULTIPLE_AUTHORS, ERROR) \
5858
FUNC(PACKED_REF_ENTRY_NOT_TERMINATED, ERROR) \
59+
FUNC(PACKED_REF_UNSORTED, ERROR) \
5960
FUNC(TREE_NOT_SORTED, ERROR) \
6061
FUNC(UNKNOWN_TYPE, ERROR) \
6162
FUNC(ZERO_PADDED_DATE, ERROR) \

refs/packed-backend.c

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -301,14 +301,8 @@ struct snapshot_record {
301301
size_t len;
302302
};
303303

304-
static int cmp_packed_ref_records(const void *v1, const void *v2,
305-
void *cb_data)
304+
static int cmp_packed_refname(const char *r1, const char *r2)
306305
{
307-
const struct snapshot *snapshot = cb_data;
308-
const struct snapshot_record *e1 = v1, *e2 = v2;
309-
const char *r1 = e1->start + snapshot_hexsz(snapshot) + 1;
310-
const char *r2 = e2->start + snapshot_hexsz(snapshot) + 1;
311-
312306
while (1) {
313307
if (*r1 == '\n')
314308
return *r2 == '\n' ? 0 : -1;
@@ -323,6 +317,17 @@ static int cmp_packed_ref_records(const void *v1, const void *v2,
323317
}
324318
}
325319

320+
static int cmp_packed_ref_records(const void *v1, const void *v2,
321+
void *cb_data)
322+
{
323+
const struct snapshot *snapshot = cb_data;
324+
const struct snapshot_record *e1 = v1, *e2 = v2;
325+
const char *r1 = e1->start + snapshot_hexsz(snapshot) + 1;
326+
const char *r2 = e2->start + snapshot_hexsz(snapshot) + 1;
327+
328+
return cmp_packed_refname(r1, r2);
329+
}
330+
326331
/*
327332
* Compare a snapshot record at `rec` to the specified NUL-terminated
328333
* refname.
@@ -1776,13 +1781,17 @@ struct fsck_packed_ref_entry {
17761781
int has_peeled;
17771782
struct object_id oid;
17781783
struct object_id peeled;
1784+
1785+
struct snapshot_record record;
17791786
};
17801787

1781-
static struct fsck_packed_ref_entry *create_fsck_packed_ref_entry(int line_number)
1788+
static struct fsck_packed_ref_entry *create_fsck_packed_ref_entry(int line_number,
1789+
const char *start)
17821790
{
17831791
struct fsck_packed_ref_entry *entry = xcalloc(1, sizeof(*entry));
17841792
entry->line_number = line_number;
17851793
entry->has_peeled = 0;
1794+
entry->record.start = start;
17861795
return entry;
17871796
}
17881797

@@ -1981,6 +1990,50 @@ static int packed_fsck_ref_oid(struct fsck_options *o, struct ref_store *ref_sto
19811990
return ret;
19821991
}
19831992

1993+
static int packed_fsck_ref_sorted(struct fsck_options *o,
1994+
struct ref_store *ref_store,
1995+
struct fsck_packed_ref_entry **entries,
1996+
int nr)
1997+
{
1998+
size_t hexsz = ref_store->repo->hash_algo->hexsz;
1999+
struct strbuf packed_entry = STRBUF_INIT;
2000+
struct fsck_ref_report report = { 0 };
2001+
struct strbuf refname1 = STRBUF_INIT;
2002+
struct strbuf refname2 = STRBUF_INIT;
2003+
int ret = 0;
2004+
2005+
for (int i = 1; i < nr; i++) {
2006+
const char *r1 = entries[i - 1]->record.start + hexsz + 1;
2007+
const char *r2 = entries[i]->record.start + hexsz + 1;
2008+
2009+
if (cmp_packed_refname(r1, r2) >= 0) {
2010+
const char *err_fmt =
2011+
"refname '%s' is not less than next refname '%s'";
2012+
const char *eol;
2013+
eol = memchr(entries[i - 1]->record.start, '\n',
2014+
entries[i - 1]->record.len);
2015+
strbuf_add(&refname1, r1, eol - r1);
2016+
eol = memchr(entries[i]->record.start, '\n',
2017+
entries[i]->record.len);
2018+
strbuf_add(&refname2, r2, eol - r2);
2019+
2020+
strbuf_addf(&packed_entry, "packed-refs line %d",
2021+
entries[i - 1]->line_number);
2022+
report.path = packed_entry.buf;
2023+
ret = fsck_report_ref(o, &report,
2024+
FSCK_MSG_PACKED_REF_UNSORTED,
2025+
err_fmt, refname1.buf, refname2.buf);
2026+
goto cleanup;
2027+
}
2028+
}
2029+
2030+
cleanup:
2031+
strbuf_release(&packed_entry);
2032+
strbuf_release(&refname1);
2033+
strbuf_release(&refname2);
2034+
return ret;
2035+
}
2036+
19842037
static int packed_fsck_ref_content(struct fsck_options *o,
19852038
struct ref_store *ref_store,
19862039
const char *start, const char *eof)
@@ -2010,7 +2063,7 @@ static int packed_fsck_ref_content(struct fsck_options *o,
20102063
ALLOC_ARRAY(entries, entry_alloc);
20112064
while (start < eof) {
20122065
struct fsck_packed_ref_entry *entry
2013-
= create_fsck_packed_ref_entry(line_number);
2066+
= create_fsck_packed_ref_entry(line_number, start);
20142067
ALLOC_GROW(entries, entry_nr + 1, entry_alloc);
20152068
entries[entry_nr++] = entry;
20162069

@@ -2026,16 +2079,19 @@ static int packed_fsck_ref_content(struct fsck_options *o,
20262079
start = eol + 1;
20272080
line_number++;
20282081
}
2082+
entry->record.len = start - entry->record.start;
20292083
}
20302084

20312085
/*
20322086
* If there is anything wrong during the parsing of the "packed-refs"
20332087
* file, we should not check the object of the refs.
20342088
*/
2035-
if (ret)
2089+
if (ret) {
20362090
o->safe_object_check = 0;
2037-
else
2091+
} else {
20382092
ret |= packed_fsck_ref_oid(o, ref_store, entries, entry_nr);
2093+
ret |= packed_fsck_ref_sorted(o, ref_store, entries, entry_nr);
2094+
}
20392095

20402096
free_fsck_packed_ref_entries(entries, entry_nr);
20412097
return ret;

t/t0602-reffiles-fsck.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,4 +765,44 @@ test_expect_success 'packed-refs objects should be checked' '
765765
done
766766
'
767767

768+
test_expect_success 'packed-ref sorted should be checked' '
769+
test_when_finished "rm -rf repo" &&
770+
git init repo &&
771+
cd repo &&
772+
test_commit default &&
773+
git branch branch-1 &&
774+
git branch branch-2 &&
775+
git tag -a annotated-tag-1 -m tag-1 &&
776+
777+
branch_1_oid=$(git rev-parse branch-1) &&
778+
branch_2_oid=$(git rev-parse branch-2) &&
779+
tag_1_oid=$(git rev-parse annotated-tag-1) &&
780+
tag_1_peeled_oid=$(git rev-parse annotated-tag-1^{}) &&
781+
782+
refname1="refs/heads/main" &&
783+
refname2="refs/heads/foo" &&
784+
refname3="refs/tags/foo" &&
785+
786+
printf "# pack-refs with: peeled fully-peeled sorted \n" >.git/packed-refs &&
787+
printf "%s %s\n" "$branch_2_oid" "$refname1" >>.git/packed-refs &&
788+
printf "%s %s\n" "$branch_1_oid" "$refname2" >>.git/packed-refs &&
789+
test_must_fail git refs verify 2>err &&
790+
cat >expect <<-EOF &&
791+
error: packed-refs line 2: packedRefUnsorted: refname '\''$refname1'\'' is not less than next refname '\''$refname2'\''
792+
EOF
793+
rm .git/packed-refs &&
794+
test_cmp expect err &&
795+
796+
printf "# pack-refs with: peeled fully-peeled sorted \n" >.git/packed-refs &&
797+
printf "%s %s\n" "$tag_1_oid" "$refname3" >>.git/packed-refs &&
798+
printf "^%s\n" "$tag_1_peeled_oid" >>.git/packed-refs &&
799+
printf "%s %s\n" "$branch_2_oid" "$refname2" >>.git/packed-refs &&
800+
test_must_fail git refs verify 2>err &&
801+
cat >expect <<-EOF &&
802+
error: packed-refs line 2: packedRefUnsorted: refname '\''$refname3'\'' is not less than next refname '\''$refname2'\''
803+
EOF
804+
rm .git/packed-refs &&
805+
test_cmp expect err
806+
'
807+
768808
test_done

0 commit comments

Comments
 (0)