Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 122 additions & 40 deletions builtin/fsck.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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));
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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++) {
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1148,5 +1229,6 @@ int cmd_fsck(int argc,
}
}

free_snapshot_refs(&snap);
return errors_found;
}
Loading