diff --git a/builtin/fsck.c b/builtin/fsck.c index c489582faa6650..eec4626bfac97b 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -51,6 +51,7 @@ static int show_progress = -1; static int show_dangling = 1; static int name_objects; static int check_references = 1; +static timestamp_t now; #define ERROR_OBJECT 01 #define ERROR_REACHABLE 02 #define ERROR_PACK 04 @@ -509,6 +510,9 @@ static int fsck_handle_reflog_ent(const char *refname, timestamp_t timestamp, int tz UNUSED, const char *message UNUSED, void *cb_data UNUSED) { + if (now && timestamp > now) + return 0; + if (verbose) fprintf_ln(stderr, _("Checking reflog %s->%s"), oid_to_hex(ooid), oid_to_hex(noid)); @@ -530,8 +534,22 @@ static int fsck_handle_reflog(const char *logname, void *cb_data) return 0; } -static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED) +struct ref_snapshot { + char *refname; + struct object_id oid; + /* TODO: Maybe supplement with latest reflog entry info too? */ +}; + +struct snapshot { + size_t nr; + size_t alloc; + struct ref_snapshot *ref; + /* TODO: Consider also snapshotting the index of each worktree. */ +}; + +static int snapshot_ref(const struct reference *ref, void *cb_data) { + struct snapshot *snap = cb_data; struct object *obj; obj = parse_object(the_repository, ref->oid); @@ -555,6 +573,20 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED) errors_found |= ERROR_REFS; } default_refs++; + + ALLOC_GROW(snap->ref, snap->nr + 1, snap->alloc); + snap->ref[snap->nr].refname = xstrdup(ref->name); + oidcpy(&snap->ref[snap->nr].oid, ref->oid); + snap->nr++; + + return 0; +} + +static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED) +{ + struct object *obj; + + obj = parse_object(the_repository, ref->oid); obj->flags |= USED; fsck_put_object_name(&fsck_walk_options, ref->oid, "%s", ref->name); @@ -567,14 +599,35 @@ static int fsck_head_link(const char *head_ref_name, const char **head_points_at, struct object_id *head_oid); -static void get_default_heads(void) +static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) { struct worktree **worktrees, **p; const char *head_points_at; struct object_id head_oid; + for (int i = 0; i < argc; i++) { + const char *arg = argv[i]; + struct object_id oid; + if (!repo_get_oid(the_repository, arg, &oid)) { + struct reference ref = { + .name = arg, + .oid = &oid, + }; + + snapshot_ref(&ref, snap); + continue; + } + error(_("invalid parameter: expected sha1, got '%s'"), arg); + errors_found |= ERROR_OBJECT; + } + + if (argc) { + include_reflogs = 0; + return; + } + refs_for_each_rawref(get_main_ref_store(the_repository), - fsck_handle_ref, NULL); + snapshot_ref, snap); worktrees = get_worktrees(); for (p = worktrees; *p; p++) { @@ -589,15 +642,52 @@ static void get_default_heads(void) .oid = &head_oid, }; - fsck_handle_ref(&ref, NULL); + snapshot_ref(&ref, snap); } strbuf_release(&refname); - if (include_reflogs) + /* + * TODO: Could use refs_for_each_reflog(...) to find + * latest entry instead of using a global 'now' for that + * purpose. + */ + } + free_worktrees(worktrees); + + /* Ignore reflogs newer than now */ + now = time(NULL); +} + + +static void free_snapshot_refs(struct snapshot *snap) +{ + for (size_t i = 0; i < snap->nr; i++) + free(snap->ref[i].refname); + free(snap->ref); +} + +static void process_refs(struct snapshot *snap) +{ + struct worktree **worktrees, **p; + + for (size_t i = 0; i < snap->nr; i++) { + struct reference ref = { + .name = snap->ref[i].refname, + .oid = &snap->ref[i].oid, + }; + fsck_handle_ref(&ref, NULL); + } + + if (include_reflogs) { + worktrees = get_worktrees(); + for (p = worktrees; *p; p++) { + struct worktree *wt = *p; + refs_for_each_reflog(get_worktree_ref_store(wt), fsck_handle_reflog, wt); + } + free_worktrees(worktrees); } - free_worktrees(worktrees); /* * Not having any default heads isn't really fatal, but @@ -962,8 +1052,12 @@ int cmd_fsck(int argc, const char *prefix, struct repository *repo UNUSED) { - int i; struct odb_source *source; + struct snapshot snap = { + .nr = 0, + .alloc = 0, + .ref = NULL + }; /* fsck knows how to handle missing promisor objects */ fetch_if_missing = 0; @@ -999,6 +1093,17 @@ int cmd_fsck(int argc, if (check_references) fsck_refs(the_repository); + /* + * Take a snapshot of the refs before walking objects to avoid looking + * at a set of refs that may be changed by the user while we are walking + * objects. We can still walk over new objects that are added during the + * execution of fsck but won't miss any objects that were reachable. + */ + snapshot_refs(&snap, argc, argv); + + /* Ensure we get a "fresh" view of the odb */ + odb_reprepare(the_repository->objects); + if (connectivity_only) { for_each_loose_object(the_repository->objects, mark_loose_for_connectivity, NULL, 0); @@ -1040,42 +1145,18 @@ int cmd_fsck(int argc, errors_found |= ERROR_OBJECT; } - for (i = 0; i < argc; i++) { - const char *arg = argv[i]; - struct object_id oid; - if (!repo_get_oid(the_repository, arg, &oid)) { - struct object *obj = lookup_object(the_repository, - &oid); - - if (!obj || !(obj->flags & HAS_OBJ)) { - if (is_promisor_object(the_repository, &oid)) - continue; - error(_("%s: object missing"), oid_to_hex(&oid)); - errors_found |= ERROR_OBJECT; - continue; - } - - obj->flags |= USED; - fsck_put_object_name(&fsck_walk_options, &oid, - "%s", arg); - mark_object_reachable(obj); - continue; - } - error(_("invalid parameter: expected sha1, got '%s'"), arg); - errors_found |= ERROR_OBJECT; - } + /* Process the snapshotted refs and the reflogs. */ + process_refs(&snap); - /* - * If we've not been given any explicit head information, do the - * default ones from .git/refs. We also consider the index file - * in this case (ie this implies --cache). - */ - if (!argc) { - get_default_heads(); + /* If not given any explicit objects, process index files too. */ + if (!argc) keep_cache_objects = 1; - } - if (keep_cache_objects) { + /* + * TODO: Consider first walking these indexes in snapshot_refs, + * to snapshot where the index entries used to point, and then + * check those snapshotted locations here. + */ struct worktree **worktrees, **p; verify_index_checksum = 1; @@ -1148,5 +1229,6 @@ int cmd_fsck(int argc, } } + free_snapshot_refs(&snap); return errors_found; }