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
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));
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);
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);
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++) {
.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
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;
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);
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;
}
}
+ free_snapshot_refs(&snap);
return errors_found;
}