Skip to content

Commit 6edbb7b

Browse files
committed
Merge branch 'en/fsck-snapshot-ref-state'
"git fsck" used inconsistent set of refs to show a confused warning, which has been corrected. * en/fsck-snapshot-ref-state: fsck: snapshot default refs before object walk
2 parents b5c409c + f6b2625 commit 6edbb7b

File tree

1 file changed

+122
-40
lines changed

1 file changed

+122
-40
lines changed

builtin/fsck.c

Lines changed: 122 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static int show_progress = -1;
5151
static int show_dangling = 1;
5252
static int name_objects;
5353
static int check_references = 1;
54+
static timestamp_t now;
5455
#define ERROR_OBJECT 01
5556
#define ERROR_REACHABLE 02
5657
#define ERROR_PACK 04
@@ -510,6 +511,9 @@ static int fsck_handle_reflog_ent(const char *refname,
510511
timestamp_t timestamp, int tz UNUSED,
511512
const char *message UNUSED, void *cb_data UNUSED)
512513
{
514+
if (now && timestamp > now)
515+
return 0;
516+
513517
if (verbose)
514518
fprintf_ln(stderr, _("Checking reflog %s->%s"),
515519
oid_to_hex(ooid), oid_to_hex(noid));
@@ -531,8 +535,22 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
531535
return 0;
532536
}
533537

534-
static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
538+
struct ref_snapshot {
539+
char *refname;
540+
struct object_id oid;
541+
/* TODO: Maybe supplement with latest reflog entry info too? */
542+
};
543+
544+
struct snapshot {
545+
size_t nr;
546+
size_t alloc;
547+
struct ref_snapshot *ref;
548+
/* TODO: Consider also snapshotting the index of each worktree. */
549+
};
550+
551+
static int snapshot_ref(const struct reference *ref, void *cb_data)
535552
{
553+
struct snapshot *snap = cb_data;
536554
struct object *obj;
537555

538556
obj = parse_object(the_repository, ref->oid);
@@ -556,6 +574,20 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
556574
errors_found |= ERROR_REFS;
557575
}
558576
default_refs++;
577+
578+
ALLOC_GROW(snap->ref, snap->nr + 1, snap->alloc);
579+
snap->ref[snap->nr].refname = xstrdup(ref->name);
580+
oidcpy(&snap->ref[snap->nr].oid, ref->oid);
581+
snap->nr++;
582+
583+
return 0;
584+
}
585+
586+
static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
587+
{
588+
struct object *obj;
589+
590+
obj = parse_object(the_repository, ref->oid);
559591
obj->flags |= USED;
560592
fsck_put_object_name(&fsck_walk_options,
561593
ref->oid, "%s", ref->name);
@@ -568,14 +600,35 @@ static int fsck_head_link(const char *head_ref_name,
568600
const char **head_points_at,
569601
struct object_id *head_oid);
570602

571-
static void get_default_heads(void)
603+
static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
572604
{
573605
struct worktree **worktrees, **p;
574606
const char *head_points_at;
575607
struct object_id head_oid;
576608

609+
for (int i = 0; i < argc; i++) {
610+
const char *arg = argv[i];
611+
struct object_id oid;
612+
if (!repo_get_oid(the_repository, arg, &oid)) {
613+
struct reference ref = {
614+
.name = arg,
615+
.oid = &oid,
616+
};
617+
618+
snapshot_ref(&ref, snap);
619+
continue;
620+
}
621+
error(_("invalid parameter: expected sha1, got '%s'"), arg);
622+
errors_found |= ERROR_OBJECT;
623+
}
624+
625+
if (argc) {
626+
include_reflogs = 0;
627+
return;
628+
}
629+
577630
refs_for_each_rawref(get_main_ref_store(the_repository),
578-
fsck_handle_ref, NULL);
631+
snapshot_ref, snap);
579632

580633
worktrees = get_worktrees();
581634
for (p = worktrees; *p; p++) {
@@ -590,15 +643,52 @@ static void get_default_heads(void)
590643
.oid = &head_oid,
591644
};
592645

593-
fsck_handle_ref(&ref, NULL);
646+
snapshot_ref(&ref, snap);
594647
}
595648
strbuf_release(&refname);
596649

597-
if (include_reflogs)
650+
/*
651+
* TODO: Could use refs_for_each_reflog(...) to find
652+
* latest entry instead of using a global 'now' for that
653+
* purpose.
654+
*/
655+
}
656+
free_worktrees(worktrees);
657+
658+
/* Ignore reflogs newer than now */
659+
now = time(NULL);
660+
}
661+
662+
663+
static void free_snapshot_refs(struct snapshot *snap)
664+
{
665+
for (size_t i = 0; i < snap->nr; i++)
666+
free(snap->ref[i].refname);
667+
free(snap->ref);
668+
}
669+
670+
static void process_refs(struct snapshot *snap)
671+
{
672+
struct worktree **worktrees, **p;
673+
674+
for (size_t i = 0; i < snap->nr; i++) {
675+
struct reference ref = {
676+
.name = snap->ref[i].refname,
677+
.oid = &snap->ref[i].oid,
678+
};
679+
fsck_handle_ref(&ref, NULL);
680+
}
681+
682+
if (include_reflogs) {
683+
worktrees = get_worktrees();
684+
for (p = worktrees; *p; p++) {
685+
struct worktree *wt = *p;
686+
598687
refs_for_each_reflog(get_worktree_ref_store(wt),
599688
fsck_handle_reflog, wt);
689+
}
690+
free_worktrees(worktrees);
600691
}
601-
free_worktrees(worktrees);
602692

603693
/*
604694
* Not having any default heads isn't really fatal, but
@@ -963,8 +1053,12 @@ int cmd_fsck(int argc,
9631053
const char *prefix,
9641054
struct repository *repo UNUSED)
9651055
{
966-
int i;
9671056
struct odb_source *source;
1057+
struct snapshot snap = {
1058+
.nr = 0,
1059+
.alloc = 0,
1060+
.ref = NULL
1061+
};
9681062

9691063
/* fsck knows how to handle missing promisor objects */
9701064
fetch_if_missing = 0;
@@ -1000,6 +1094,17 @@ int cmd_fsck(int argc,
10001094
if (check_references)
10011095
fsck_refs(the_repository);
10021096

1097+
/*
1098+
* Take a snapshot of the refs before walking objects to avoid looking
1099+
* at a set of refs that may be changed by the user while we are walking
1100+
* objects. We can still walk over new objects that are added during the
1101+
* execution of fsck but won't miss any objects that were reachable.
1102+
*/
1103+
snapshot_refs(&snap, argc, argv);
1104+
1105+
/* Ensure we get a "fresh" view of the odb */
1106+
odb_reprepare(the_repository->objects);
1107+
10031108
if (connectivity_only) {
10041109
for_each_loose_object(the_repository->objects,
10051110
mark_loose_for_connectivity, NULL, 0);
@@ -1041,42 +1146,18 @@ int cmd_fsck(int argc,
10411146
errors_found |= ERROR_OBJECT;
10421147
}
10431148

1044-
for (i = 0; i < argc; i++) {
1045-
const char *arg = argv[i];
1046-
struct object_id oid;
1047-
if (!repo_get_oid(the_repository, arg, &oid)) {
1048-
struct object *obj = lookup_object(the_repository,
1049-
&oid);
1050-
1051-
if (!obj || !(obj->flags & HAS_OBJ)) {
1052-
if (is_promisor_object(the_repository, &oid))
1053-
continue;
1054-
error(_("%s: object missing"), oid_to_hex(&oid));
1055-
errors_found |= ERROR_OBJECT;
1056-
continue;
1057-
}
1058-
1059-
obj->flags |= USED;
1060-
fsck_put_object_name(&fsck_walk_options, &oid,
1061-
"%s", arg);
1062-
mark_object_reachable(obj);
1063-
continue;
1064-
}
1065-
error(_("invalid parameter: expected sha1, got '%s'"), arg);
1066-
errors_found |= ERROR_OBJECT;
1067-
}
1149+
/* Process the snapshotted refs and the reflogs. */
1150+
process_refs(&snap);
10681151

1069-
/*
1070-
* If we've not been given any explicit head information, do the
1071-
* default ones from .git/refs. We also consider the index file
1072-
* in this case (ie this implies --cache).
1073-
*/
1074-
if (!argc) {
1075-
get_default_heads();
1152+
/* If not given any explicit objects, process index files too. */
1153+
if (!argc)
10761154
keep_cache_objects = 1;
1077-
}
1078-
10791155
if (keep_cache_objects) {
1156+
/*
1157+
* TODO: Consider first walking these indexes in snapshot_refs,
1158+
* to snapshot where the index entries used to point, and then
1159+
* check those snapshotted locations here.
1160+
*/
10801161
struct worktree **worktrees, **p;
10811162

10821163
verify_index_checksum = 1;
@@ -1149,5 +1230,6 @@ int cmd_fsck(int argc,
11491230
}
11501231
}
11511232

1233+
free_snapshot_refs(&snap);
11521234
return errors_found;
11531235
}

0 commit comments

Comments
 (0)