Volume XXII, number 280Wednesday, October 7, 2026Latest message 60 minutes ago

The Git List

News and archive of git@vger.kernel.org, since April 2005

v2, 12 partsfetch-pack: move fsck options into function scope

17 messages between Mar 23, 2026 and Mar 31, 2026, from Patrick Steinhardt, Junio C Hamano.

Plain Markdown or JSON for tools and agents. Diffs are folded; open one to read it.

Patrick SteinhardtMar 23, 2026, 15:02 UTC on lore

[PATCH v2 00/12] fsck: drop use of `the_repository`

Hi,

this patch series refactors "{builtin/,}fsck.c" to not depend on `the_repository` anymore. These refactorings are mostly done to prepare for upcoming changes where we'll make backend-specific fsck checks pluggable.

Changes in v2:
  - Propagate the repository via `struct fsck_options`.
  - Link to v1: https://lore.kernel.org/r/20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im
Thanks!
Patrick
---
Patrick Steinhardt (12):
      fetch-pack: move fsck options into function scope
      fsck: initialize fsck options via a function
      fsck: store repository in fsck options
      fsck: drop USE_THE_REPOSITORY
      builtin/fsck: fix trivial dependence on `the_repository`
      builtin/fsck: stop using `the_repository` when snapshotting refs
      builtin/fsck: stop using `the_repository` when checking refs
      builtin/fsck: stop using `the_repository` when checking reflogs
      builtin/fsck: stop using `the_repository` with loose objects
      builtin/fsck: stop using `the_repository` when checking packed objects
      builtin/fsck: stop using `the_repository` when marking objects
      builtin/fsck: stop using `the_repository` in error reporting
 builtin/fsck.c           | 273 ++++++++++++++++++++++++++---------------------
 builtin/index-pack.c     |   4 +-
 builtin/mktag.c          |   5 +-
 builtin/refs.c           |   6 +-
 builtin/unpack-objects.c |   6 +-
 fetch-pack.c             |   9 +-
 fsck.c                   |  76 ++++++++++---
 fsck.h                   |  42 +++-----
 object-file.c            |   3 +-
 pack-check.c             |   7 +-
 pack.h                   |   9 +-
 11 files changed, 259 insertions(+), 181 deletions(-)
Range-diff versus v1:
 1:  fd589e1813 <  -:  ---------- fsck: drop `the_repository` in `fsck_walk()`
 2:  6d0d46b70d <  -:  ---------- fsck: drop `the_repository` in `fsck_finish()`
 3:  79c3cd644b <  -:  ---------- fsck: refactor interface to parse fsck options
 4:  f5fd9d1489 <  -:  ---------- fsck: drop `the_repository` in `fsck_set_msg_types()`
 5:  58c145905e <  -:  ---------- fsck: stop relying on global state via `parse_oid_hex()`
 -:  ---------- >  1:  1b5958d8db fetch-pack: move fsck options into function scope
 -:  ---------- >  2:  19b502cd82 fsck: initialize fsck options via a function
 -:  ---------- >  3:  34b921be8b fsck: store repository in fsck options
 -:  ---------- >  4:  f8a74307d8 fsck: drop USE_THE_REPOSITORY
 6:  4d255cd44d !  5:  6e3f51757f builtin/fsck: fix trivial dependence on `the_repository`
    @@ Commit message
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## builtin/fsck.c ##
    -@@ builtin/fsck.c: static void mark_object_reachable(struct object *obj)
    - 	mark_object(obj, OBJ_ANY, NULL, NULL);
    - }
    - 
    --static int traverse_one_object(struct object *obj)
    -+static int traverse_one_object(struct repository *repo, struct object *obj)
    - {
    --	int result = fsck_walk(the_repository, obj, obj, &fsck_walk_options);
    -+	int result = fsck_walk(repo, obj, obj, &fsck_walk_options);
    - 
    - 	if (obj->type == OBJ_TREE) {
    - 		struct tree *tree = (struct tree *)obj;
     @@ builtin/fsck.c: static int traverse_one_object(struct object *obj)
      	return result;
      }
    @@ builtin/fsck.c: static int traverse_one_object(struct object *obj)
     +		progress = start_delayed_progress(repo,
      						  _("Checking connectivity"), 0);
      	while (pending.nr) {
    --		result |= traverse_one_object(object_array_pop(&pending));
    -+		result |= traverse_one_object(repo, object_array_pop(&pending));
    - 		display_progress(progress, ++nr);
    - 	}
    - 	stop_progress(&progress);
    + 		result |= traverse_one_object(object_array_pop(&pending));
     @@ builtin/fsck.c: static int mark_unreachable_referents(const struct object_id *oid,
      /*
       * Check a single reachable object
    @@ builtin/fsck.c: static void check_connectivity(void)
      	}
      }
      
    -@@ builtin/fsck.c: static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
    +@@ builtin/fsck.c: static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data)
      	return 0;
      }
      
    --static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
    -+static void snapshot_refs(struct repository *repo, struct snapshot *snap,
    -+			  int argc, const char **argv)
    +-static void fsck_source(struct odb_source *source)
    ++static void fsck_source(struct repository *repo, struct odb_source *source)
      {
    - 	struct refs_for_each_ref_options opts = {
    - 		.flags = REFS_FOR_EACH_INCLUDE_BROKEN,
    -@@ builtin/fsck.c: static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
    - 	for (int i = 0; i < argc; i++) {
    - 		const char *arg = argv[i];
    - 		struct object_id oid;
    --		if (!repo_get_oid(the_repository, arg, &oid)) {
    -+		if (!repo_get_oid(repo, arg, &oid)) {
    - 			struct reference ref = {
    - 				.name = arg,
    - 				.oid = &oid,
    -@@ builtin/fsck.c: static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
    - 		return;
    - 	}
    - 
    --	refs_for_each_ref_ext(get_main_ref_store(the_repository),
    -+	refs_for_each_ref_ext(get_main_ref_store(repo),
    - 			      snapshot_ref, snap, &opts);
    - 
    - 	worktrees = get_worktrees();
    -@@ builtin/fsck.c: static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
    - 
    - 		strbuf_worktree_ref(wt, &refname, "HEAD");
    - 
    --		head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
    -+		head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(repo),
    - 							 refname.buf, 0, &head_oid, NULL);
    - 
    - 		if (head_points_at && !is_null_oid(&head_oid)) {
    + 	struct progress *progress = NULL;
    + 	struct for_each_loose_cb cb_data = {
     @@ builtin/fsck.c: static void fsck_source(struct odb_source *source)
      		fprintf_ln(stderr, _("Checking object directory"));
      
      	if (show_progress)
     -		progress = start_progress(the_repository,
    -+		progress = start_progress(source->odb->repo,
    ++		progress = start_progress(repo,
      					  _("Checking object directories"), 256);
      
      	for_each_loose_file_in_source(source, fsck_loose,
    @@ builtin/fsck.c: static void fsck_source(struct odb_source *source)
      }
      
     -static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
    -+static int fsck_cache_tree(struct repository *repo, struct cache_tree *it,
    -+			   const char *index_path)
    ++static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const char *index_path)
      {
      	int i;
      	int err = 0;
    @@ builtin/fsck.c: static int check_pack_rev_indexes(struct repository *r, int show
      						  "Verifying reverse pack-indexes", pack_count);
      		pack_count = 0;
      	}
    -@@ builtin/fsck.c: static struct option fsck_opts[] = {
    - int cmd_fsck(int argc,
    - 	     const char **argv,
    - 	     const char *prefix,
    --	     struct repository *repo UNUSED)
    -+	     struct repository *repo)
    - {
    - 	struct odb_source *source;
    - 	struct snapshot snap = {
     @@ builtin/fsck.c: int cmd_fsck(int argc,
      	if (name_objects)
      		fsck_enable_object_names(&fsck_walk_options);
      
    --	fsck_options_parse_config(&fsck_obj_options, the_repository);
    +-	repo_config(the_repository, git_fsck_config, &fsck_obj_options);
     -	prepare_repo_settings(the_repository);
    -+	fsck_options_parse_config(&fsck_obj_options, repo);
    ++	repo_config(repo, git_fsck_config, &fsck_obj_options);
     +	prepare_repo_settings(repo);
      
      	if (check_references)
    @@ builtin/fsck.c: int cmd_fsck(int argc,
      	/*
      	 * Take a snapshot of the refs before walking objects to avoid looking
     @@ builtin/fsck.c: int cmd_fsck(int argc,
    - 	 * 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);
    -+	snapshot_refs(repo, &snap, argc, argv);
    + 	snapshot_refs(&snap, argc, argv);
      
      	/* Ensure we get a "fresh" view of the odb */
     -	odb_reprepare(the_repository->objects);
    @@ builtin/fsck.c: int cmd_fsck(int argc,
      	} else {
     -		odb_prepare_alternates(the_repository->objects);
     -		for (source = the_repository->objects->sources; source; source = source->next)
    +-			fsck_source(source);
     +		odb_prepare_alternates(repo->objects);
     +		for (source = repo->objects->sources; source; source = source->next)
    - 			fsck_source(source);
    ++			fsck_source(repo, source);
      
      		if (check_full) {
    + 			struct packed_git *p;
     @@ builtin/fsck.c: int cmd_fsck(int argc,
      			struct progress *progress = NULL;
      
    @@ builtin/fsck.c: int cmd_fsck(int argc,
      						p, fsck_obj_buffer,
      						progress, count))
      					errors_found |= ERROR_PACK;
    -@@ builtin/fsck.c: int cmd_fsck(int argc,
    - 			stop_progress(&progress);
    - 		}
    - 
    --		if (fsck_finish(the_repository, &fsck_obj_options))
    -+		if (fsck_finish(repo, &fsck_obj_options))
    - 			errors_found |= ERROR_OBJECT;
    - 	}
    - 
     @@ builtin/fsck.c: int cmd_fsck(int argc,
      		for (p = worktrees; *p; p++) {
      			struct worktree *wt = *p;
 7:  65f1c0078b !  6:  6c0507a4c8 builtin/fsck: stop using `the_repository` when snapshotting refs
    @@ builtin/fsck.c: struct snapshot {
      			/*
      			 * Increment default_refs anyway, because this is a
      			 * valid ref.
    -@@ builtin/fsck.c: static void snapshot_refs(struct repository *repo, struct snapshot *snap,
    +@@ builtin/fsck.c: static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
    + 	return 0;
    + }
    + 
    +-static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
    ++static void snapshot_refs(struct repository *repo,
    ++			  struct snapshot *snap, int argc, const char **argv)
    + {
      	struct refs_for_each_ref_options opts = {
      		.flags = REFS_FOR_EACH_INCLUDE_BROKEN,
      	};
    @@ builtin/fsck.c: static void snapshot_refs(struct repository *repo, struct snapsh
      	struct worktree **worktrees, **p;
      	const char *head_points_at;
      	struct object_id head_oid;
    -@@ builtin/fsck.c: static void snapshot_refs(struct repository *repo, struct snapshot *snap,
    +@@ builtin/fsck.c: static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
    + 	for (int i = 0; i < argc; i++) {
    + 		const char *arg = argv[i];
    + 		struct object_id oid;
    +-		if (!repo_get_oid(the_repository, arg, &oid)) {
    ++		if (!repo_get_oid(repo, arg, &oid)) {
    + 			struct reference ref = {
    + 				.name = arg,
      				.oid = &oid,
      			};
      
    @@ builtin/fsck.c: static void snapshot_refs(struct repository *repo, struct snapsh
      			continue;
      		}
      		error(_("invalid parameter: expected sha1, got '%s'"), arg);
    -@@ builtin/fsck.c: static void snapshot_refs(struct repository *repo, struct snapshot *snap,
    +@@ builtin/fsck.c: static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
    + 		return;
      	}
      
    - 	refs_for_each_ref_ext(get_main_ref_store(repo),
    +-	refs_for_each_ref_ext(get_main_ref_store(the_repository),
     -			      snapshot_ref, snap, &opts);
    ++	refs_for_each_ref_ext(get_main_ref_store(repo),
     +			      snapshot_ref, &data, &opts);
      
      	worktrees = get_worktrees();
      	for (p = worktrees; *p; p++) {
    -@@ builtin/fsck.c: static void snapshot_refs(struct repository *repo, struct snapshot *snap,
    +@@ builtin/fsck.c: static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
    + 
    + 		strbuf_worktree_ref(wt, &refname, "HEAD");
    + 
    +-		head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
    ++		head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(repo),
    + 							 refname.buf, 0, &head_oid, NULL);
    + 
    + 		if (head_points_at && !is_null_oid(&head_oid)) {
    +@@ builtin/fsck.c: static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
      				.oid = &head_oid,
      			};
      
    @@ builtin/fsck.c: static void snapshot_refs(struct repository *repo, struct snapsh
      		}
      		strbuf_release(&refname);
      
    +@@ builtin/fsck.c: int cmd_fsck(int argc,
    + 	 * 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);
    ++	snapshot_refs(repo, &snap, argc, argv);
    + 
    + 	/* Ensure we get a "fresh" view of the odb */
    + 	odb_reprepare(repo->objects);
 8:  42b9d4ad29 =  7:  844a192db2 builtin/fsck: stop using `the_repository` when checking refs
 9:  3c3af4cbcd =  8:  6557fc60ee builtin/fsck: stop using `the_repository` when checking reflogs
10:  387ba6f851 !  9:  31709b33c8 builtin/fsck: stop using `the_repository` with loose objects
    @@ Commit message
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## builtin/fsck.c ##
    -@@ builtin/fsck.c: static void check_connectivity(struct repository *repo)
    - 	}
    - }
    - 
    --static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
    -+static int fsck_obj(struct repository *repo,
    -+		    struct object *obj,
    -+		    void *buffer, unsigned long size)
    - {
    - 	int err;
    - 
    -@@ builtin/fsck.c: static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
    - 			   printable_type(&obj->oid, obj->type),
    - 			   describe_object(&obj->oid));
    - 
    --	if (fsck_walk(the_repository, obj, NULL, &fsck_obj_options))
    -+	if (fsck_walk(repo, obj, NULL, &fsck_obj_options))
    - 		objerror(obj, _("broken links"));
    --	err = fsck_object(the_repository, obj, buffer, size, &fsck_obj_options);
    -+	err = fsck_object(repo, obj, buffer, size, &fsck_obj_options);
    - 	if (err)
    - 		goto out;
    - 
    -@@ builtin/fsck.c: static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
    - 	}
    - 	obj->flags &= ~(REACHABLE | SEEN);
    - 	obj->flags |= HAS_OBJ;
    --	return fsck_obj(obj, buffer, size);
    -+	return fsck_obj(the_repository, obj, buffer, size);
    - }
    - 
    - static int default_refs;
     @@ builtin/fsck.c: static void process_refs(struct repository *repo, struct snapshot *snap)
      	}
      }
    @@ builtin/fsck.c: static int fsck_loose(const struct object_id *oid, const char *p
      				  contents, &eaten);
      
      	if (!obj) {
    -@@ builtin/fsck.c: static int fsck_loose(const struct object_id *oid, const char *path,
    - 
    - 	obj->flags &= ~(REACHABLE | SEEN);
    - 	obj->flags |= HAS_OBJ;
    --	if (fsck_obj(obj, contents, size))
    -+	if (fsck_obj(data->repo, obj, contents, size))
    - 		errors_found |= ERROR_OBJECT;
    - 
    - 	if (!eaten)
    -@@ builtin/fsck.c: static void fsck_source(struct odb_source *source)
    +@@ builtin/fsck.c: static void fsck_source(struct repository *repo, struct odb_source *source)
      {
      	struct progress *progress = NULL;
      	struct for_each_loose_cb cb_data = {
11:  031a245247 ! 10:  ecbd9abef9 builtin/fsck: stop using `the_repository` when checking packed objects
    @@ Commit message
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## builtin/fsck.c ##
    -@@ builtin/fsck.c: static int fsck_obj(struct repository *repo,
    +@@ builtin/fsck.c: static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
      }
      
      static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
    @@ builtin/fsck.c: static int fsck_obj(struct repository *repo,
      	if (!obj) {
      		errors_found |= ERROR_OBJECT;
      		return error(_("%s: object corrupt or missing"),
    -@@ builtin/fsck.c: static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
    - 	}
    - 	obj->flags &= ~(REACHABLE | SEEN);
    - 	obj->flags |= HAS_OBJ;
    --	return fsck_obj(the_repository, obj, buffer, size);
    -+	return fsck_obj(repo, obj, buffer, size);
    - }
    - 
    - static int default_refs;
     @@ builtin/fsck.c: int cmd_fsck(int argc,
      			repo_for_each_pack(repo, p) {
      				/* verify gives error messages itself */
12:  5d61f5a545 ! 11:  9e246797c5 builtin/fsck: stop using `the_repository` when marking objects
    @@ Commit message
     
      ## builtin/fsck.c ##
     @@ builtin/fsck.c: static int fsck_objects_error_func(struct fsck_options *o UNUSED,
    - 
      static struct object_array pending;
      
    -+struct mark_object_data {
    -+	struct repository *repo;
    -+	struct object *parent;
    -+};
    -+
      static int mark_object(struct object *obj, enum object_type type,
     -		       void *data, struct fsck_options *options UNUSED)
    -+		       void *cb_data, struct fsck_options *options UNUSED)
    ++		       void *data, struct fsck_options *options)
      {
    --	struct object *parent = data;
    -+	struct mark_object_data *data = cb_data;
    -+	struct object *parent = data->parent;
    + 	struct object *parent = data;
      
    - 	/*
    - 	 * The only case data is NULL or type is OBJ_ANY is when
     @@ builtin/fsck.c: static int mark_object(struct object *obj, enum object_type type,
      		return 0;
      	obj->flags |= REACHABLE;
      
     -	if (is_promisor_object(the_repository, &obj->oid))
    -+	if (is_promisor_object(data->repo, &obj->oid))
    ++	if (is_promisor_object(options->repo, &obj->oid))
      		/*
      		 * Further recursion does not need to be performed on this
      		 * object since it is a promisor object (so it does not need to
    @@ builtin/fsck.c: static int mark_object(struct object *obj, enum object_type type
      
      	if (!(obj->flags & HAS_OBJ)) {
     -		if (parent && !odb_has_object(the_repository->objects, &obj->oid,
    -+		if (parent && !odb_has_object(data->repo->objects, &obj->oid,
    ++		if (parent && !odb_has_object(options->repo->objects, &obj->oid,
      					      HAS_OBJECT_RECHECK_PACKED)) {
      			printf_ln(_("broken link from %7s %s\n"
      				    "              to %7s %s"),
     @@ builtin/fsck.c: static int mark_object(struct object *obj, enum object_type type,
    - 	return 0;
    - }
      
    --static void mark_object_reachable(struct object *obj)
    -+static void mark_object_reachable(struct repository *repo, struct object *obj)
    + static void mark_object_reachable(struct object *obj)
      {
     -	mark_object(obj, OBJ_ANY, NULL, NULL);
    -+	struct mark_object_data data = {
    -+		.repo = repo,
    -+	};
    -+	mark_object(obj, OBJ_ANY, &data, NULL);
    ++	mark_object(obj, OBJ_ANY, NULL, &fsck_walk_options);
      }
      
    - static int traverse_one_object(struct repository *repo, struct object *obj)
    - {
    --	int result = fsck_walk(repo, obj, obj, &fsck_walk_options);
    -+	struct mark_object_data data = {
    -+		.repo = repo,
    -+		.parent = obj,
    -+	};
    -+	int result = fsck_walk(repo, obj, &data, &fsck_walk_options);
    - 
    - 	if (obj->type == OBJ_TREE) {
    - 		struct tree *tree = (struct tree *)obj;
    + static int traverse_one_object(struct object *obj)
     @@ builtin/fsck.c: static int mark_used(struct object *obj, enum object_type type UNUSED,
      
      static int mark_unreachable_referents(const struct object_id *oid,
    @@ builtin/fsck.c: static int mark_used(struct object *obj, enum object_type type U
     +				      void *data)
      {
     +	struct repository *repo = data;
    - 	struct fsck_options options = FSCK_OPTIONS_DEFAULT;
    + 	struct fsck_options options;
     -	struct object *obj = lookup_object(the_repository, oid);
    -+	struct object *obj = lookup_object(repo, oid);
    ++	struct object *obj = lookup_object(data, oid);
      
      	if (!obj || !(obj->flags & HAS_OBJ))
      		return 0; /* not part of our original set */
    @@ builtin/fsck.c: static int mark_unreachable_referents(const struct object_id *oi
      			object_as_type(obj, type, 0);
      	}
      
    +-	fsck_options_init(&options, the_repository, FSCK_OPTIONS_DEFAULT);
    ++	fsck_options_init(&options, repo, FSCK_OPTIONS_DEFAULT);
      	options.walk = mark_used;
    --	fsck_walk(the_repository, obj, NULL, &options);
    -+	fsck_walk(repo, obj, NULL, &options);
    + 	fsck_walk(obj, NULL, &options);
      	if (obj->type == OBJ_TREE)
    - 		free_tree_buffer((struct tree *)obj);
    - 
     @@ builtin/fsck.c: static void check_connectivity(struct repository *repo)
      		 * traversal.
      		 */
    @@ builtin/fsck.c: static void check_connectivity(struct repository *repo)
      	}
      
      	/* Look up all the requirements, warn about missing objects.. */
    -@@ builtin/fsck.c: static void fsck_handle_reflog_oid(struct repository *repo,
    - 						     "%s@{%"PRItime"}",
    - 						     refname, timestamp);
    - 			obj->flags |= USED;
    --			mark_object_reachable(obj);
    -+			mark_object_reachable(repo, obj);
    - 		} else if (!is_promisor_object(repo, oid)) {
    - 			error(_("%s: invalid reflog entry %s"),
    - 			      refname, oid_to_hex(oid));
    -@@ builtin/fsck.c: static int fsck_handle_ref(const struct reference *ref, void *cb_data)
    - 	obj->flags |= USED;
    - 	fsck_put_object_name(&fsck_walk_options,
    - 			     ref->oid, "%s", ref->name);
    --	mark_object_reachable(obj);
    -+	mark_object_reachable(repo, obj);
    - 
    - 	return 0;
    - }
    -@@ builtin/fsck.c: static int fsck_cache_tree(struct repository *repo, struct cache_tree *it,
    - 		}
    - 		obj->flags |= USED;
    - 		fsck_put_object_name(&fsck_walk_options, &it->oid, ":");
    --		mark_object_reachable(obj);
    -+		mark_object_reachable(repo, obj);
    - 		if (obj->type != OBJ_TREE)
    - 			err |= objerror(obj, _("non-tree in cache-tree"));
    - 	}
    -@@ builtin/fsck.c: static int fsck_resolve_undo(struct index_state *istate,
    - 			obj->flags |= USED;
    - 			fsck_put_object_name(&fsck_walk_options, &ru->oid[i],
    - 					     ":(%d):%s", i, path);
    --			mark_object_reachable(obj);
    -+			mark_object_reachable(istate->repo, obj);
    - 		}
    - 	}
    - 	return 0;
    -@@ builtin/fsck.c: static void fsck_index(struct index_state *istate, const char *index_path,
    - 				     "%s:%s",
    - 				     is_current_worktree ? "" : index_path,
    - 				     istate->cache[i]->name);
    --		mark_object_reachable(obj);
    -+		mark_object_reachable(istate->repo, obj);
    - 	}
    - 	if (istate->cache_tree)
    - 		fsck_cache_tree(istate->repo, istate->cache_tree, index_path);
     @@ builtin/fsck.c: static void fsck_index(struct index_state *istate, const char *index_path,
      
      static int mark_object_for_connectivity(const struct object_id *oid,
13:  6842133df2 <  -:  ---------- fsck: provide repository in `struct fsck_report_object`
14:  06a88b16cd ! 12:  066e60a2ca builtin/fsck: stop using `the_repository` in error reporting
    @@ builtin/fsck.c: static const char *describe_object(const struct object_id *oid)
      
      	if (type == OBJ_NONE)
     -		type = odb_read_object_info(the_repository->objects,
    -+		type = odb_read_object_info(repo->objects,
    - 					    oid, NULL);
    +-					    oid, NULL);
    ++		type = odb_read_object_info(repo->objects, oid, NULL);
      
      	ret = type_name(type);
    + 	if (!ret)
     @@ builtin/fsck.c: static const char *printable_type(const struct object_id *oid,
      	return ret;
      }
    @@ builtin/fsck.c: static const char *printable_type(const struct object_id *oid,
      		   describe_object(&obj->oid), err);
      	return -1;
      }
    + 
    +-static int fsck_objects_error_func(struct fsck_options *o UNUSED,
    ++static int fsck_objects_error_func(struct fsck_options *o,
    + 				   void *fsck_report,
    + 				   enum fsck_msg_type msg_type,
    + 				   enum fsck_msg_id msg_id UNUSED,
     @@ builtin/fsck.c: static int fsck_objects_error_func(struct fsck_options *o UNUSED,
      	case FSCK_WARN:
      		/* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
      		fprintf_ln(stderr, _("warning in %s %s: %s"),
     -			   printable_type(oid, object_type),
    -+			   printable_type(report->repo, oid, object_type),
    ++			   printable_type(o->repo, oid, object_type),
      			   describe_object(oid), message);
      		return 0;
      	case FSCK_ERROR:
      		/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
      		fprintf_ln(stderr, _("error in %s %s: %s"),
     -			   printable_type(oid, object_type),
    -+			   printable_type(report->repo, oid, object_type),
    ++			   printable_type(o->repo, oid, object_type),
      			   describe_object(oid), message);
      		return 1;
      	default:
    @@ builtin/fsck.c: static int mark_object(struct object *obj, enum object_type type
      		/* ... these references to parent->fld are safe here */
      		printf_ln(_("broken link from %7s %s"),
     -			  printable_type(&parent->oid, parent->type),
    -+			  printable_type(data->repo, &parent->oid, parent->type),
    ++			  printable_type(options->repo, &parent->oid, parent->type),
      			  describe_object(&parent->oid));
      		printf_ln(_("broken link from %7s %s"),
      			  (type == OBJ_ANY ? _("unknown") : type_name(type)),
    @@ builtin/fsck.c: static int mark_object(struct object *obj, enum object_type type
      	if (type != OBJ_ANY && obj->type != type)
      		/* ... and the reference to parent is safe here */
     -		objerror(parent, _("wrong object type in link"));
    -+		objerror(data->repo, parent, _("wrong object type in link"));
    ++		objerror(options->repo, parent, _("wrong object type in link"));
      
      	if (obj->flags & REACHABLE)
      		return 0;
    @@ builtin/fsck.c: static int mark_object(struct object *obj, enum object_type type
      			printf_ln(_("broken link from %7s %s\n"
      				    "              to %7s %s"),
     -				  printable_type(&parent->oid, parent->type),
    -+				  printable_type(data->repo, &parent->oid, parent->type),
    ++				  printable_type(options->repo, &parent->oid, parent->type),
      				  describe_object(&parent->oid),
     -				  printable_type(&obj->oid, obj->type),
    -+				  printable_type(data->repo, &obj->oid, obj->type),
    ++				  printable_type(options->repo, &obj->oid, obj->type),
      				  describe_object(&obj->oid));
      			errors_found |= ERROR_REACHABLE;
      		}
    @@ builtin/fsck.c: static void check_unreachable_object(struct repository *repo, st
      				  describe_object(&obj->oid));
      		if (write_lost_and_found) {
      			char *filename = repo_git_path(repo, "lost-found/%s/%s",
    -@@ builtin/fsck.c: static int fsck_obj(struct repository *repo,
    +@@ builtin/fsck.c: static void check_connectivity(struct repository *repo)
    + 	}
    + }
    + 
    +-static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
    ++static int fsck_obj(struct repository *repo,
    ++		    struct object *obj, void *buffer, unsigned long size)
    + {
    + 	int err;
    + 
    +@@ builtin/fsck.c: static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
      
      	if (verbose)
      		fprintf_ln(stderr, _("Checking %s %s"),
    @@ builtin/fsck.c: static int fsck_obj(struct repository *repo,
     +			   printable_type(repo, &obj->oid, obj->type),
      			   describe_object(&obj->oid));
      
    - 	if (fsck_walk(repo, obj, NULL, &fsck_obj_options))
    + 	if (fsck_walk(obj, NULL, &fsck_obj_options))
     -		objerror(obj, _("broken links"));
     +		objerror(repo, obj, _("broken links"));
    - 	err = fsck_object(repo, obj, buffer, size, &fsck_obj_options);
    + 	err = fsck_object(obj, buffer, size, &fsck_obj_options);
      	if (err)
      		goto out;
    -@@ builtin/fsck.c: static int fsck_obj(struct repository *repo,
    +@@ builtin/fsck.c: static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
      
      		if (show_tags && tag->tagged) {
      			printf_ln(_("tagged %s %s (%s) in %s"),
    @@ builtin/fsck.c: static int fsck_obj(struct repository *repo,
      				  describe_object(&tag->tagged->oid),
      				  tag->tag,
      				  describe_object(&tag->object.oid));
    -@@ builtin/fsck.c: static int fsck_cache_tree(struct repository *repo, struct cache_tree *it,
    +@@ builtin/fsck.c: static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
    + 	}
    + 	obj->flags &= ~(REACHABLE | SEEN);
    + 	obj->flags |= HAS_OBJ;
    +-	return fsck_obj(obj, buffer, size);
    ++	return fsck_obj(repo, obj, buffer, size);
    + }
    + 
    + static int default_refs;
    +@@ builtin/fsck.c: static int fsck_loose(const struct object_id *oid, const char *path,
    + 
    + 	obj->flags &= ~(REACHABLE | SEEN);
    + 	obj->flags |= HAS_OBJ;
    +-	if (fsck_obj(obj, contents, size))
    ++	if (fsck_obj(data->repo, obj, contents, size))
    + 		errors_found |= ERROR_OBJECT;
    + 
    + 	if (!eaten)
    +@@ builtin/fsck.c: static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const
      		fsck_put_object_name(&fsck_walk_options, &it->oid, ":");
    - 		mark_object_reachable(repo, obj);
    + 		mark_object_reachable(obj);
      		if (obj->type != OBJ_TREE)
     -			err |= objerror(obj, _("non-tree in cache-tree"));
     +			err |= objerror(repo, obj, _("non-tree in cache-tree"));

--- base-commit: 7ff1e8dc1e1680510c96e69965b3fa81372c5037 change-id: 20260320-b4-pks-fsck-without-the-repository-4ddc4c8ed61e

Patrick SteinhardtMar 23, 2026, 15:02 UTC in reply to Patrick Steinhardt on lore

When fetching a packfile, we optionally verify received objects via the fsck subsystem. The options for those consistency checks are declared in global scope without a good reason, and they are never cleaned up. So in case the options are reused, they may accumulate more state over time.

Furthermore, in subsequent changes we'll introduce a repository pointer into the structure. Obviously though, we don't have a repository available at static time, except for `the_repository`, which we don't want to use here.

Refactor the code to move the options into the respective functions and properly manage their lifecycle.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 fetch-pack.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
Show changes to fetch-pack.c +4 −1
diff --git a/fetch-pack.c b/fetch-pack.c
index 6ecd468ef7..ec5abb92b5 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -51,7 +51,6 @@ static int server_supports_filtering;
 static int advertise_sid;
 static struct shallow_lock shallow_lock;
 static const char *alternate_shallow_file;
-static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
 static struct strbuf fsck_msg_types = STRBUF_INIT;
 static struct string_list uri_protocols = STRING_LIST_INIT_DUP;
 
@@ -1100,6 +1099,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 				 struct shallow_info *si,
 				 struct string_list *pack_lockfiles)
 {
+	struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
 	struct repository *r = the_repository;
 	struct ref *ref = copy_ref_list(orig_ref);
 	struct object_id oid;
@@ -1235,6 +1235,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 		die("fsck failed");
 
  all_done:
+	fsck_options_clear(&fsck_options);
 	if (negotiator)
 		negotiator->release(negotiator);
 	return ref;
@@ -1654,6 +1655,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 				    struct string_list *pack_lockfiles)
 {
 	struct repository *r = the_repository;
+	struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
 	struct ref *ref = copy_ref_list(orig_ref);
 	enum fetch_state state = FETCH_CHECK_LOCAL;
 	struct oidset common = OIDSET_INIT;
@@ -1882,6 +1884,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 	if (negotiator)
 		negotiator->release(negotiator);
 
+	fsck_options_clear(&fsck_options);
 	oidset_clear(&common);
 	return ref;
 }
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:02 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 02/12] fsck: initialize fsck options via a function

We initialize the `struct fsck_options` via a set of macros, often in global scope. In the next commit though we're about to introduce a new repository field to the options that must be initialized, and naturally we don't have a repo other than `the_repository` available in this scope.

Refactor the code to instead intrdouce a new `fsck_options_init()` function that initializes the options for us and move initialization into function scope.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fsck.c           | 10 +++++++---
 builtin/index-pack.c     |  4 +++-
 builtin/mktag.c          |  3 ++-
 builtin/refs.c           |  4 +++-
 builtin/unpack-objects.c |  4 +++-
 fetch-pack.c             |  8 ++++++--
 fsck.c                   | 45 +++++++++++++++++++++++++++++++++++++++++++++
 fsck.h                   | 38 ++++++++++----------------------------
 object-file.c            |  3 ++-
 9 files changed, 81 insertions(+), 38 deletions(-)
Show changes to 9 files +81 −38

builtin/fsck.c, builtin/index-pack.c, builtin/mktag.c, builtin/refs.c, builtin/unpack-objects.c, fetch-pack.c, fsck.c, fsck.h, object-file.c

diff --git a/builtin/fsck.c b/builtin/fsck.c
index 9bab32effe..59e3b0f7ac 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -42,8 +42,8 @@ static int check_full = 1;
 static int connectivity_only;
 static int check_strict;
 static int keep_cache_objects;
-static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;
-static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;
+static struct fsck_options fsck_walk_options;
+static struct fsck_options fsck_obj_options;
 static int errors_found;
 static int write_lost_and_found;
 static int verbose;
@@ -224,7 +224,7 @@ static int mark_unreachable_referents(const struct object_id *oid,
 				      struct object_info *oi UNUSED,
 				      void *data UNUSED)
 {
-	struct fsck_options options = FSCK_OPTIONS_DEFAULT;
+	struct fsck_options options;
 	struct object *obj = lookup_object(the_repository, oid);
 
 	if (!obj || !(obj->flags & HAS_OBJ))
@@ -243,6 +243,7 @@ static int mark_unreachable_referents(const struct object_id *oid,
 			object_as_type(obj, type, 0);
 	}
 
+	fsck_options_init(&options, FSCK_OPTIONS_DEFAULT);
 	options.walk = mark_used;
 	fsck_walk(obj, NULL, &options);
 	if (obj->type == OBJ_TREE)
@@ -1004,7 +1005,10 @@ int cmd_fsck(int argc,
 
 	argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
 
+	fsck_options_init(&fsck_walk_options, FSCK_OPTIONS_DEFAULT);
 	fsck_walk_options.walk = mark_object;
+
+	fsck_options_init(&fsck_obj_options, FSCK_OPTIONS_DEFAULT);
 	fsck_obj_options.walk = mark_used;
 	fsck_obj_options.error_func = fsck_objects_error_func;
 	if (check_strict)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index d1e47279a8..c8d28bcf8e 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -136,7 +136,7 @@ static int nr_threads;
 static int from_stdin;
 static int strict;
 static int do_fsck_object;
-static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
+static struct fsck_options fsck_options;
 static int verbose;
 static const char *progress_title;
 static int show_resolving_progress;
@@ -1908,6 +1908,8 @@ int cmd_index_pack(int argc,
 	show_usage_if_asked(argc, argv, index_pack_usage);
 
 	disable_replace_refs();
+
+	fsck_options_init(&fsck_options, FSCK_OPTIONS_MISSING_GITMODULES);
 	fsck_options.walk = mark_link;
 
 	reset_pack_idx_option(&opts);
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 7cf6e1230a..9f37f9dede 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -16,7 +16,7 @@ static char const * const builtin_mktag_usage[] = {
 };
 static int option_strict = 1;
 
-static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
+static struct fsck_options fsck_options;
 
 static int mktag_fsck_error_func(struct fsck_options *o UNUSED,
 				 void *fsck_report UNUSED,
@@ -94,6 +94,7 @@ int cmd_mktag(int argc,
 	if (strbuf_read(&buf, 0, 0) < 0)
 		die_errno(_("could not read from stdin"));
 
+	fsck_options_init(&fsck_options, FSCK_OPTIONS_STRICT);
 	fsck_options.error_func = mktag_fsck_error_func;
 	fsck_set_msg_type_from_ids(&fsck_options, FSCK_MSG_EXTRA_HEADER_ENTRY,
 				   FSCK_WARN);
diff --git a/builtin/refs.c b/builtin/refs.c
index 3064f888b2..1719ada549 100644
--- a/builtin/refs.c
+++ b/builtin/refs.c
@@ -80,7 +80,7 @@ static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,
 static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
 			   struct repository *repo UNUSED)
 {
-	struct fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT;
+	struct fsck_options fsck_refs_options;
 	struct worktree **worktrees;
 	const char * const verify_usage[] = {
 		REFS_VERIFY_USAGE,
@@ -93,6 +93,8 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
 	};
 	int ret = 0;
 
+	fsck_options_init(&fsck_refs_options, FSCK_OPTIONS_REFS);
+
 	argc = parse_options(argc, argv, prefix, options, verify_usage, 0);
 	if (argc)
 		usage(_("'git refs verify' takes no arguments"));
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 6fc64e9e4b..9e4bb9d25c 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -29,7 +29,7 @@ static unsigned int offset, len;
 static off_t consumed_bytes;
 static off_t max_input_size;
 static struct git_hash_ctx ctx;
-static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
+static struct fsck_options fsck_options;
 static struct progress *progress;
 
 /*
@@ -627,6 +627,8 @@ int cmd_unpack_objects(int argc,
 
 	show_usage_if_asked(argc, argv, unpack_usage);
 
+	fsck_options_init(&fsck_options, FSCK_OPTIONS_STRICT);
+
 	for (i = 1 ; i < argc; i++) {
 		const char *arg = argv[i];
 
diff --git a/fetch-pack.c b/fetch-pack.c
index ec5abb92b5..7339162368 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -1099,7 +1099,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 				 struct shallow_info *si,
 				 struct string_list *pack_lockfiles)
 {
-	struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
+	struct fsck_options fsck_options = { 0 };
 	struct repository *r = the_repository;
 	struct ref *ref = copy_ref_list(orig_ref);
 	struct object_id oid;
@@ -1228,6 +1228,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 		alternate_shallow_file = setup_temporary_shallow(si->shallow);
 	} else
 		alternate_shallow_file = NULL;
+
+	fsck_options_init(&fsck_options, FSCK_OPTIONS_MISSING_GITMODULES);
 	if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,
 		     &fsck_options.gitmodules_found))
 		die(_("git fetch-pack: fetch failed."));
@@ -1655,7 +1657,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 				    struct string_list *pack_lockfiles)
 {
 	struct repository *r = the_repository;
-	struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
+	struct fsck_options fsck_options;
 	struct ref *ref = copy_ref_list(orig_ref);
 	enum fetch_state state = FETCH_CHECK_LOCAL;
 	struct oidset common = OIDSET_INIT;
@@ -1673,6 +1675,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 	struct strvec index_pack_args = STRVEC_INIT;
 	const char *promisor_remote_config;
 
+	fsck_options_init(&fsck_options, FSCK_OPTIONS_MISSING_GITMODULES);
+
 	if (server_feature_v2("promisor-remote", &promisor_remote_config))
 		promisor_remote_reply(promisor_remote_config, NULL);
 
diff --git a/fsck.c b/fsck.c
index 0f02cf8f77..1ff8208502 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1380,6 +1380,51 @@ bool fsck_has_queued_checks(struct fsck_options *options)
 	       !oidset_equal(&options->gitattributes_found, &options->gitattributes_done);
 }
 
+void fsck_options_init(struct fsck_options *options,
+		       enum fsck_options_type type)
+{
+	static const struct fsck_options defaults[] = {
+		[FSCK_OPTIONS_DEFAULT] = {
+			.skip_oids = OIDSET_INIT,
+			.gitmodules_found = OIDSET_INIT,
+			.gitmodules_done = OIDSET_INIT,
+			.gitattributes_found = OIDSET_INIT,
+			.gitattributes_done = OIDSET_INIT,
+			.error_func = fsck_objects_error_function
+		},
+		[FSCK_OPTIONS_STRICT] = {
+			.strict = 1,
+			.gitmodules_found = OIDSET_INIT,
+			.gitmodules_done = OIDSET_INIT,
+			.gitattributes_found = OIDSET_INIT,
+			.gitattributes_done = OIDSET_INIT,
+			.error_func = fsck_objects_error_function,
+		},
+		[FSCK_OPTIONS_MISSING_GITMODULES] = {
+			.strict = 1,
+			.gitmodules_found = OIDSET_INIT,
+			.gitmodules_done = OIDSET_INIT,
+			.gitattributes_found = OIDSET_INIT,
+			.gitattributes_done = OIDSET_INIT,
+			.error_func = fsck_objects_error_cb_print_missing_gitmodules,
+		},
+		[FSCK_OPTIONS_REFS] = {
+			.error_func = fsck_refs_error_function,
+		},
+	};
+
+	switch (type) {
+	case FSCK_OPTIONS_DEFAULT:
+	case FSCK_OPTIONS_STRICT:
+	case FSCK_OPTIONS_MISSING_GITMODULES:
+	case FSCK_OPTIONS_REFS:
+		memcpy(options, &defaults[type], sizeof(*options));
+		break;
+	default:
+		BUG("unknown fsck options type %d", type);
+	}
+}
+
 void fsck_options_clear(struct fsck_options *options)
 {
 	free(options->msg_type);
diff --git a/fsck.h b/fsck.h
index 65ecbb7fe1..9c973b53b2 100644
--- a/fsck.h
+++ b/fsck.h
@@ -180,34 +180,6 @@ struct fsck_options {
 	kh_oid_map_t *object_names;
 };
 
-#define FSCK_OPTIONS_DEFAULT { \
-	.skip_oids = OIDSET_INIT, \
-	.gitmodules_found = OIDSET_INIT, \
-	.gitmodules_done = OIDSET_INIT, \
-	.gitattributes_found = OIDSET_INIT, \
-	.gitattributes_done = OIDSET_INIT, \
-	.error_func = fsck_objects_error_function \
-}
-#define FSCK_OPTIONS_STRICT { \
-	.strict = 1, \
-	.gitmodules_found = OIDSET_INIT, \
-	.gitmodules_done = OIDSET_INIT, \
-	.gitattributes_found = OIDSET_INIT, \
-	.gitattributes_done = OIDSET_INIT, \
-	.error_func = fsck_objects_error_function, \
-}
-#define FSCK_OPTIONS_MISSING_GITMODULES { \
-	.strict = 1, \
-	.gitmodules_found = OIDSET_INIT, \
-	.gitmodules_done = OIDSET_INIT, \
-	.gitattributes_found = OIDSET_INIT, \
-	.gitattributes_done = OIDSET_INIT, \
-	.error_func = fsck_objects_error_cb_print_missing_gitmodules, \
-}
-#define FSCK_REFS_OPTIONS_DEFAULT { \
-	.error_func = fsck_refs_error_function, \
-}
-
 /* descend in all linked child objects
  * the return value is:
  *    -1	error in processing the object
@@ -255,6 +227,16 @@ int fsck_finish(struct fsck_options *options);
  */
 bool fsck_has_queued_checks(struct fsck_options *options);
 
+enum fsck_options_type {
+	FSCK_OPTIONS_DEFAULT,
+	FSCK_OPTIONS_STRICT,
+	FSCK_OPTIONS_MISSING_GITMODULES,
+	FSCK_OPTIONS_REFS,
+};
+
+void fsck_options_init(struct fsck_options *options,
+		       enum fsck_options_type type);
+
 /*
  * Clear the fsck_options struct, freeing any allocated memory.
  */
diff --git a/object-file.c b/object-file.c
index c62e5496e0..186b2ff764 100644
--- a/object-file.c
+++ b/object-file.c
@@ -1279,8 +1279,9 @@ static int index_mem(struct index_state *istate,
 		}
 	}
 	if (flags & INDEX_FORMAT_CHECK) {
-		struct fsck_options opts = FSCK_OPTIONS_DEFAULT;
+		struct fsck_options opts;
 
+		fsck_options_init(&opts, FSCK_OPTIONS_DEFAULT);
 		opts.strict = 1;
 		opts.error_func = hash_format_check_report;
 		if (fsck_buffer(null_oid(istate->repo->hash_algo), type, buf, size, &opts))
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:02 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 03/12] fsck: store repository in fsck options

The fsck subsystem relies on `the_repository` quite a bit. While we could of course explicitly pass a repository down the callchain, we already have a `struct fsck_options` that we pass to almost all functions.

Extend the options to also store the repository to make it readily available.

Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fsck.c           | 8 ++++----
 builtin/index-pack.c     | 2 +-
 builtin/mktag.c          | 4 ++--
 builtin/refs.c           | 4 ++--
 builtin/unpack-objects.c | 4 ++--
 fetch-pack.c             | 4 ++--
 fsck.c                   | 3 +++
 fsck.h                   | 4 ++++
 object-file.c            | 2 +-
 9 files changed, 21 insertions(+), 14 deletions(-)
Show changes to 9 files +21 −14

builtin/fsck.c, builtin/index-pack.c, builtin/mktag.c, builtin/refs.c, builtin/unpack-objects.c, fetch-pack.c, fsck.c, fsck.h, object-file.c

diff --git a/builtin/fsck.c b/builtin/fsck.c
index 59e3b0f7ac..990d836918 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -243,7 +243,7 @@ static int mark_unreachable_referents(const struct object_id *oid,
 			object_as_type(obj, type, 0);
 	}
 
-	fsck_options_init(&options, FSCK_OPTIONS_DEFAULT);
+	fsck_options_init(&options, the_repository, FSCK_OPTIONS_DEFAULT);
 	options.walk = mark_used;
 	fsck_walk(obj, NULL, &options);
 	if (obj->type == OBJ_TREE)
@@ -987,7 +987,7 @@ static struct option fsck_opts[] = {
 int cmd_fsck(int argc,
 	     const char **argv,
 	     const char *prefix,
-	     struct repository *repo UNUSED)
+	     struct repository *repo)
 {
 	struct odb_source *source;
 	struct snapshot snap = {
@@ -1005,10 +1005,10 @@ int cmd_fsck(int argc,
 
 	argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
 
-	fsck_options_init(&fsck_walk_options, FSCK_OPTIONS_DEFAULT);
+	fsck_options_init(&fsck_walk_options, repo, FSCK_OPTIONS_DEFAULT);
 	fsck_walk_options.walk = mark_object;
 
-	fsck_options_init(&fsck_obj_options, FSCK_OPTIONS_DEFAULT);
+	fsck_options_init(&fsck_obj_options, repo, FSCK_OPTIONS_DEFAULT);
 	fsck_obj_options.walk = mark_used;
 	fsck_obj_options.error_func = fsck_objects_error_func;
 	if (check_strict)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index c8d28bcf8e..e4129bd605 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1909,7 +1909,7 @@ int cmd_index_pack(int argc,
 
 	disable_replace_refs();
 
-	fsck_options_init(&fsck_options, FSCK_OPTIONS_MISSING_GITMODULES);
+	fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
 	fsck_options.walk = mark_link;
 
 	reset_pack_idx_option(&opts);
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 9f37f9dede..f40264a878 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -75,7 +75,7 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
 int cmd_mktag(int argc,
 	      const char **argv,
 	      const char *prefix,
-	      struct repository *repo UNUSED)
+	      struct repository *repo)
 {
 	static struct option builtin_mktag_options[] = {
 		OPT_BOOL(0, "strict", &option_strict,
@@ -94,7 +94,7 @@ int cmd_mktag(int argc,
 	if (strbuf_read(&buf, 0, 0) < 0)
 		die_errno(_("could not read from stdin"));
 
-	fsck_options_init(&fsck_options, FSCK_OPTIONS_STRICT);
+	fsck_options_init(&fsck_options, repo, FSCK_OPTIONS_STRICT);
 	fsck_options.error_func = mktag_fsck_error_func;
 	fsck_set_msg_type_from_ids(&fsck_options, FSCK_MSG_EXTRA_HEADER_ENTRY,
 				   FSCK_WARN);
diff --git a/builtin/refs.c b/builtin/refs.c
index 1719ada549..e3125bc61b 100644
--- a/builtin/refs.c
+++ b/builtin/refs.c
@@ -78,7 +78,7 @@ static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,
 }
 
 static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
-			   struct repository *repo UNUSED)
+			   struct repository *repo)
 {
 	struct fsck_options fsck_refs_options;
 	struct worktree **worktrees;
@@ -93,7 +93,7 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
 	};
 	int ret = 0;
 
-	fsck_options_init(&fsck_refs_options, FSCK_OPTIONS_REFS);
+	fsck_options_init(&fsck_refs_options, repo, FSCK_OPTIONS_REFS);
 
 	argc = parse_options(argc, argv, prefix, options, verify_usage, 0);
 	if (argc)
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 9e4bb9d25c..d863912b24 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -613,7 +613,7 @@ static void unpack_all(void)
 int cmd_unpack_objects(int argc,
 		       const char **argv,
 		       const char *prefix UNUSED,
-		       struct repository *repo UNUSED)
+		       struct repository *repo)
 {
 	int i;
 	struct object_id oid;
@@ -627,7 +627,7 @@ int cmd_unpack_objects(int argc,
 
 	show_usage_if_asked(argc, argv, unpack_usage);
 
-	fsck_options_init(&fsck_options, FSCK_OPTIONS_STRICT);
+	fsck_options_init(&fsck_options, repo, FSCK_OPTIONS_STRICT);
 
 	for (i = 1 ; i < argc; i++) {
 		const char *arg = argv[i];
diff --git a/fetch-pack.c b/fetch-pack.c
index 7339162368..84a21c5107 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -1229,7 +1229,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 	} else
 		alternate_shallow_file = NULL;
 
-	fsck_options_init(&fsck_options, FSCK_OPTIONS_MISSING_GITMODULES);
+	fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
 	if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,
 		     &fsck_options.gitmodules_found))
 		die(_("git fetch-pack: fetch failed."));
@@ -1675,7 +1675,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 	struct strvec index_pack_args = STRVEC_INIT;
 	const char *promisor_remote_config;
 
-	fsck_options_init(&fsck_options, FSCK_OPTIONS_MISSING_GITMODULES);
+	fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
 
 	if (server_feature_v2("promisor-remote", &promisor_remote_config))
 		promisor_remote_reply(promisor_remote_config, NULL);
diff --git a/fsck.c b/fsck.c
index 1ff8208502..a05997703a 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1381,6 +1381,7 @@ bool fsck_has_queued_checks(struct fsck_options *options)
 }
 
 void fsck_options_init(struct fsck_options *options,
+		       struct repository *repo,
 		       enum fsck_options_type type)
 {
 	static const struct fsck_options defaults[] = {
@@ -1423,6 +1424,8 @@ void fsck_options_init(struct fsck_options *options,
 	default:
 		BUG("unknown fsck options type %d", type);
 	}
+
+	options->repo = repo;
 }
 
 void fsck_options_clear(struct fsck_options *options)
diff --git a/fsck.h b/fsck.h
index 9c973b53b2..e77935c8a9 100644
--- a/fsck.h
+++ b/fsck.h
@@ -166,7 +166,10 @@ struct fsck_ref_report {
 	const char *path;
 };
 
+struct repository;
+
 struct fsck_options {
+	struct repository *repo;
 	fsck_walk_func walk;
 	fsck_error error_func;
 	unsigned strict;
@@ -235,6 +238,7 @@ enum fsck_options_type {
 };
 
 void fsck_options_init(struct fsck_options *options,
+		       struct repository *repo,
 		       enum fsck_options_type type);
 
 /*
diff --git a/object-file.c b/object-file.c
index 186b2ff764..24ed5d5577 100644
--- a/object-file.c
+++ b/object-file.c
@@ -1281,7 +1281,7 @@ static int index_mem(struct index_state *istate,
 	if (flags & INDEX_FORMAT_CHECK) {
 		struct fsck_options opts;
 
-		fsck_options_init(&opts, FSCK_OPTIONS_DEFAULT);
+		fsck_options_init(&opts, the_repository, FSCK_OPTIONS_DEFAULT);
 		opts.strict = 1;
 		opts.error_func = hash_format_check_report;
 		if (fsck_buffer(null_oid(istate->repo->hash_algo), type, buf, size, &opts))
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:02 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 04/12] fsck: drop USE_THE_REPOSITORY

Stop using `the_repository` in "fsck.c" in favor of the repository that we've already got available via `struct fsck_options`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 fsck.c | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)
Show changes to fsck.c +13 −15
diff --git a/fsck.c b/fsck.c
index a05997703a..b72200c352 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1,5 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
-
 #include "git-compat-util.h"
 #include "date.h"
 #include "dir.h"
@@ -207,7 +205,7 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)
 			if (equal == len)
 				die("skiplist requires a path");
 			oidset_parse_file(&options->skip_oids, buf + equal + 1,
-					  the_repository->hash_algo);
+					  options->repo->hash_algo);
 			buf += len + 1;
 			continue;
 		}
@@ -360,7 +358,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
 	int res = 0;
 	const char *name;
 
-	if (repo_parse_tree(the_repository, tree))
+	if (repo_parse_tree(options->repo, tree))
 		return -1;
 
 	name = fsck_get_object_name(options, &tree->object.oid);
@@ -375,14 +373,14 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
 			continue;
 
 		if (S_ISDIR(entry.mode)) {
-			obj = (struct object *)lookup_tree(the_repository, &entry.oid);
+			obj = (struct object *)lookup_tree(options->repo, &entry.oid);
 			if (name && obj)
 				fsck_put_object_name(options, &entry.oid, "%s%s/",
 						     name, entry.path);
 			result = options->walk(obj, OBJ_TREE, data, options);
 		}
 		else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
-			obj = (struct object *)lookup_blob(the_repository, &entry.oid);
+			obj = (struct object *)lookup_blob(options->repo, &entry.oid);
 			if (name && obj)
 				fsck_put_object_name(options, &entry.oid, "%s%s",
 						     name, entry.path);
@@ -409,7 +407,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
 	int result;
 	const char *name;
 
-	if (repo_parse_commit(the_repository, commit))
+	if (repo_parse_commit(options->repo, commit))
 		return -1;
 
 	name = fsck_get_object_name(options, &commit->object.oid);
@@ -417,7 +415,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
 		fsck_put_object_name(options, get_commit_tree_oid(commit),
 				     "%s:", name);
 
-	result = options->walk((struct object *) repo_get_commit_tree(the_repository, commit),
+	result = options->walk((struct object *) repo_get_commit_tree(options->repo, commit),
 			       OBJ_TREE, data, options);
 	if (result < 0)
 		return result;
@@ -474,7 +472,7 @@ static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *optio
 {
 	const char *name = fsck_get_object_name(options, &tag->object.oid);
 
-	if (parse_tag(the_repository, tag))
+	if (parse_tag(options->repo, tag))
 		return -1;
 	if (name)
 		fsck_put_object_name(options, &tag->tagged->oid, "%s", name);
@@ -487,7 +485,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
 		return -1;
 
 	if (obj->type == OBJ_NONE)
-		parse_object(the_repository, &obj->oid);
+		parse_object(options->repo, &obj->oid);
 
 	switch (obj->type) {
 	case OBJ_BLOB:
@@ -970,14 +968,14 @@ static int fsck_commit(const struct object_id *oid,
 
 	if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer))
 		return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
-	if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
+	if (parse_oid_hex_algop(buffer, &tree_oid, &p, options->repo->hash_algo) || *p != '\n') {
 		err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
 		if (err)
 			return err;
 	}
 	buffer = p + 1;
 	while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) {
-		if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
+		if (parse_oid_hex_algop(buffer, &parent_oid, &p, options->repo->hash_algo) || *p != '\n') {
 			err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
 			if (err)
 				return err;
@@ -1044,7 +1042,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
 		ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
 		goto done;
 	}
-	if (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\n') {
+	if (parse_oid_hex_algop(buffer, tagged_oid, &p, options->repo->hash_algo) || *p != '\n') {
 		ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
 		if (ret)
 			goto done;
@@ -1336,9 +1334,9 @@ static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,
 		if (oidset_contains(blobs_done, oid))
 			continue;
 
-		buf = odb_read_object(the_repository->objects, oid, &type, &size);
+		buf = odb_read_object(options->repo->objects, oid, &type, &size);
 		if (!buf) {
-			if (is_promisor_object(the_repository, oid))
+			if (is_promisor_object(options->repo, oid))
 				continue;
 			ret |= report(options,
 				      oid, OBJ_BLOB, msg_missing,
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:02 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 05/12] builtin/fsck: fix trivial dependence on `the_repository`

We have a bunch of sites in "builtin/fsck.c" that depend on `the_repository` even though we already have a repository available, or in cases where we can trivially make it available.

Refactor such sites to use the context-provided repository instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fsck.c | 98 +++++++++++++++++++++++++++++-----------------------------
 1 file changed, 49 insertions(+), 49 deletions(-)
Show changes to builtin/fsck.c +49 −49
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 990d836918..59680e6daf 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -195,13 +195,13 @@ static int traverse_one_object(struct object *obj)
 	return result;
 }
 
-static int traverse_reachable(void)
+static int traverse_reachable(struct repository *repo)
 {
 	struct progress *progress = NULL;
 	unsigned int nr = 0;
 	int result = 0;
 	if (show_progress)
-		progress = start_delayed_progress(the_repository,
+		progress = start_delayed_progress(repo,
 						  _("Checking connectivity"), 0);
 	while (pending.nr) {
 		result |= traverse_one_object(object_array_pop(&pending));
@@ -255,7 +255,7 @@ static int mark_unreachable_referents(const struct object_id *oid,
 /*
  * Check a single reachable object
  */
-static void check_reachable_object(struct object *obj)
+static void check_reachable_object(struct repository *repo, struct object *obj)
 {
 	/*
 	 * We obviously want the object to be parsed,
@@ -263,9 +263,9 @@ static void check_reachable_object(struct object *obj)
 	 * do a full fsck
 	 */
 	if (!(obj->flags & HAS_OBJ)) {
-		if (is_promisor_object(the_repository, &obj->oid))
+		if (is_promisor_object(repo, &obj->oid))
 			return;
-		if (has_object_pack(the_repository, &obj->oid))
+		if (has_object_pack(repo, &obj->oid))
 			return; /* it is in pack - forget about it */
 		printf_ln(_("missing %s %s"),
 			  printable_type(&obj->oid, obj->type),
@@ -278,7 +278,7 @@ static void check_reachable_object(struct object *obj)
 /*
  * Check a single unreachable object
  */
-static void check_unreachable_object(struct object *obj)
+static void check_unreachable_object(struct repository *repo, struct object *obj)
 {
 	/*
 	 * Missing unreachable object? Ignore it. It's not like
@@ -318,19 +318,19 @@ static void check_unreachable_object(struct object *obj)
 				  printable_type(&obj->oid, obj->type),
 				  describe_object(&obj->oid));
 		if (write_lost_and_found) {
-			char *filename = repo_git_path(the_repository, "lost-found/%s/%s",
+			char *filename = repo_git_path(repo, "lost-found/%s/%s",
 				obj->type == OBJ_COMMIT ? "commit" : "other",
 				describe_object(&obj->oid));
 			FILE *f;
 
-			if (safe_create_leading_directories_const(the_repository, filename)) {
+			if (safe_create_leading_directories_const(repo, filename)) {
 				error(_("could not create lost-found"));
 				free(filename);
 				return;
 			}
 			f = xfopen(filename, "w");
 			if (obj->type == OBJ_BLOB) {
-				if (odb_stream_blob_to_fd(the_repository->objects, fileno(f),
+				if (odb_stream_blob_to_fd(repo->objects, fileno(f),
 							  &obj->oid, NULL, 1))
 					die_errno(_("could not write '%s'"), filename);
 			} else
@@ -350,23 +350,23 @@ static void check_unreachable_object(struct object *obj)
 	 */
 }
 
-static void check_object(struct object *obj)
+static void check_object(struct repository *repo, struct object *obj)
 {
 	if (verbose)
 		fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid));
 
 	if (obj->flags & REACHABLE)
-		check_reachable_object(obj);
+		check_reachable_object(repo, obj);
 	else
-		check_unreachable_object(obj);
+		check_unreachable_object(repo, obj);
 }
 
-static void check_connectivity(void)
+static void check_connectivity(struct repository *repo)
 {
 	int i, max;
 
 	/* Traverse the pending reachable objects */
-	traverse_reachable();
+	traverse_reachable(repo);
 
 	/*
 	 * With --connectivity-only, we won't have actually opened and marked
@@ -384,20 +384,20 @@ static void check_connectivity(void)
 		 * and ignore any that weren't present in our earlier
 		 * traversal.
 		 */
-		odb_for_each_object(the_repository->objects, NULL,
+		odb_for_each_object(repo->objects, NULL,
 				    mark_unreachable_referents, NULL, 0);
 	}
 
 	/* Look up all the requirements, warn about missing objects.. */
-	max = get_max_object_index(the_repository);
+	max = get_max_object_index(repo);
 	if (verbose)
 		fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max);
 
 	for (i = 0; i < max; i++) {
-		struct object *obj = get_indexed_object(the_repository, i);
+		struct object *obj = get_indexed_object(repo, i);
 
 		if (obj)
-			check_object(obj);
+			check_object(repo, obj);
 	}
 }
 
@@ -770,7 +770,7 @@ static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data)
 	return 0;
 }
 
-static void fsck_source(struct odb_source *source)
+static void fsck_source(struct repository *repo, struct odb_source *source)
 {
 	struct progress *progress = NULL;
 	struct for_each_loose_cb cb_data = {
@@ -781,7 +781,7 @@ static void fsck_source(struct odb_source *source)
 		fprintf_ln(stderr, _("Checking object directory"));
 
 	if (show_progress)
-		progress = start_progress(the_repository,
+		progress = start_progress(repo,
 					  _("Checking object directories"), 256);
 
 	for_each_loose_file_in_source(source, fsck_loose,
@@ -790,7 +790,7 @@ static void fsck_source(struct odb_source *source)
 	stop_progress(&progress);
 }
 
-static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
+static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const char *index_path)
 {
 	int i;
 	int err = 0;
@@ -799,7 +799,7 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
 		fprintf_ln(stderr, _("Checking cache tree of %s"), index_path);
 
 	if (0 <= it->entry_count) {
-		struct object *obj = parse_object(the_repository, &it->oid);
+		struct object *obj = parse_object(repo, &it->oid);
 		if (!obj) {
 			error(_("%s: invalid sha1 pointer in cache-tree of %s"),
 			      oid_to_hex(&it->oid), index_path);
@@ -813,7 +813,7 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
 			err |= objerror(obj, _("non-tree in cache-tree"));
 	}
 	for (i = 0; i < it->subtree_nr; i++)
-		err |= fsck_cache_tree(it->down[i]->cache_tree, index_path);
+		err |= fsck_cache_tree(repo, it->down[i]->cache_tree, index_path);
 	return err;
 }
 
@@ -839,7 +839,7 @@ static int fsck_resolve_undo(struct index_state *istate,
 			if (!ru->mode[i] || !S_ISREG(ru->mode[i]))
 				continue;
 
-			obj = parse_object(the_repository, &ru->oid[i]);
+			obj = parse_object(istate->repo, &ru->oid[i]);
 			if (!obj) {
 				error(_("%s: invalid sha1 pointer in resolve-undo of %s"),
 				      oid_to_hex(&ru->oid[i]),
@@ -871,7 +871,7 @@ static void fsck_index(struct index_state *istate, const char *index_path,
 		mode = istate->cache[i]->ce_mode;
 		if (S_ISGITLINK(mode))
 			continue;
-		blob = lookup_blob(the_repository,
+		blob = lookup_blob(istate->repo,
 				   &istate->cache[i]->oid);
 		if (!blob)
 			continue;
@@ -884,7 +884,7 @@ static void fsck_index(struct index_state *istate, const char *index_path,
 		mark_object_reachable(obj);
 	}
 	if (istate->cache_tree)
-		fsck_cache_tree(istate->cache_tree, index_path);
+		fsck_cache_tree(istate->repo, istate->cache_tree, index_path);
 	fsck_resolve_undo(istate, index_path);
 }
 
@@ -907,7 +907,7 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress)
 	if (show_progress) {
 		repo_for_each_pack(r, p)
 			pack_count++;
-		progress = start_delayed_progress(the_repository,
+		progress = start_delayed_progress(r,
 						  "Verifying reverse pack-indexes", pack_count);
 		pack_count = 0;
 	}
@@ -1027,11 +1027,11 @@ int cmd_fsck(int argc,
 	if (name_objects)
 		fsck_enable_object_names(&fsck_walk_options);
 
-	repo_config(the_repository, git_fsck_config, &fsck_obj_options);
-	prepare_repo_settings(the_repository);
+	repo_config(repo, git_fsck_config, &fsck_obj_options);
+	prepare_repo_settings(repo);
 
 	if (check_references)
-		fsck_refs(the_repository);
+		fsck_refs(repo);
 
 	/*
 	 * Take a snapshot of the refs before walking objects to avoid looking
@@ -1042,15 +1042,15 @@ int cmd_fsck(int argc,
 	snapshot_refs(&snap, argc, argv);
 
 	/* Ensure we get a "fresh" view of the odb */
-	odb_reprepare(the_repository->objects);
+	odb_reprepare(repo->objects);
 
 	if (connectivity_only) {
-		odb_for_each_object(the_repository->objects, NULL,
+		odb_for_each_object(repo->objects, NULL,
 				    mark_object_for_connectivity, NULL, 0);
 	} else {
-		odb_prepare_alternates(the_repository->objects);
-		for (source = the_repository->objects->sources; source; source = source->next)
-			fsck_source(source);
+		odb_prepare_alternates(repo->objects);
+		for (source = repo->objects->sources; source; source = source->next)
+			fsck_source(repo, source);
 
 		if (check_full) {
 			struct packed_git *p;
@@ -1058,19 +1058,19 @@ int cmd_fsck(int argc,
 			struct progress *progress = NULL;
 
 			if (show_progress) {
-				repo_for_each_pack(the_repository, p) {
+				repo_for_each_pack(repo, p) {
 					if (open_pack_index(p))
 						continue;
 					total += p->num_objects;
 				}
 
-				progress = start_progress(the_repository,
+				progress = start_progress(repo,
 							  _("Checking objects"), total);
 			}
 
-			repo_for_each_pack(the_repository, p) {
+			repo_for_each_pack(repo, p) {
 				/* verify gives error messages itself */
-				if (verify_pack(the_repository,
+				if (verify_pack(repo,
 						p, fsck_obj_buffer,
 						progress, count))
 					errors_found |= ERROR_PACK;
@@ -1104,7 +1104,7 @@ int cmd_fsck(int argc,
 		for (p = worktrees; *p; p++) {
 			struct worktree *wt = *p;
 			struct index_state istate =
-				INDEX_STATE_INIT(the_repository);
+				INDEX_STATE_INIT(repo);
 			char *path, *wt_gitdir;
 
 			/*
@@ -1125,17 +1125,17 @@ int cmd_fsck(int argc,
 		free_worktrees(worktrees);
 	}
 
-	errors_found |= check_pack_rev_indexes(the_repository, show_progress);
-	if (verify_bitmap_files(the_repository))
+	errors_found |= check_pack_rev_indexes(repo, show_progress);
+	if (verify_bitmap_files(repo))
 		errors_found |= ERROR_BITMAP;
 
-	check_connectivity();
+	check_connectivity(repo);
 
-	if (the_repository->settings.core_commit_graph) {
+	if (repo->settings.core_commit_graph) {
 		struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
 
-		odb_prepare_alternates(the_repository->objects);
-		for (source = the_repository->objects->sources; source; source = source->next) {
+		odb_prepare_alternates(repo->objects);
+		for (source = repo->objects->sources; source; source = source->next) {
 			child_process_init(&commit_graph_verify);
 			commit_graph_verify.git_cmd = 1;
 			strvec_pushl(&commit_graph_verify.args, "commit-graph",
@@ -1149,11 +1149,11 @@ int cmd_fsck(int argc,
 		}
 	}
 
-	if (the_repository->settings.core_multi_pack_index) {
+	if (repo->settings.core_multi_pack_index) {
 		struct child_process midx_verify = CHILD_PROCESS_INIT;
 
-		odb_prepare_alternates(the_repository->objects);
-		for (source = the_repository->objects->sources; source; source = source->next) {
+		odb_prepare_alternates(repo->objects);
+		for (source = repo->objects->sources; source; source = source->next) {
 			child_process_init(&midx_verify);
 			midx_verify.git_cmd = 1;
 			strvec_pushl(&midx_verify.args, "multi-pack-index",
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:02 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 06/12] builtin/fsck: stop using `the_repository` when snapshotting refs

We depedn on `the_repository` when snapshotting refs. Refactor this to use a context-provided repository instead that is injected via the `struct snapshot_ref_data`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fsck.c | 33 ++++++++++++++++++++++-----------
 1 file changed, 22 insertions(+), 11 deletions(-)
Show changes to builtin/fsck.c +22 −11
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 59680e6daf..edbff16add 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -533,14 +533,20 @@ struct snapshot {
 	/* TODO: Consider also snapshotting the index of each worktree. */
 };
 
+struct snapshot_ref_data {
+	struct repository *repo;
+	struct snapshot *snap;
+};
+
 static int snapshot_ref(const struct reference *ref, void *cb_data)
 {
-	struct snapshot *snap = cb_data;
+	struct snapshot_ref_data *data = cb_data;
+	struct snapshot *snap = data->snap;
 	struct object *obj;
 
-	obj = parse_object(the_repository, ref->oid);
+	obj = parse_object(data->repo, ref->oid);
 	if (!obj) {
-		if (is_promisor_object(the_repository, ref->oid)) {
+		if (is_promisor_object(data->repo, ref->oid)) {
 			/*
 			 * Increment default_refs anyway, because this is a
 			 * valid ref.
@@ -581,11 +587,16 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
 	return 0;
 }
 
-static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
+static void snapshot_refs(struct repository *repo,
+			  struct snapshot *snap, int argc, const char **argv)
 {
 	struct refs_for_each_ref_options opts = {
 		.flags = REFS_FOR_EACH_INCLUDE_BROKEN,
 	};
+	struct snapshot_ref_data data = {
+		.repo = repo,
+		.snap = snap,
+	};
 	struct worktree **worktrees, **p;
 	const char *head_points_at;
 	struct object_id head_oid;
@@ -593,13 +604,13 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 	for (int i = 0; i < argc; i++) {
 		const char *arg = argv[i];
 		struct object_id oid;
-		if (!repo_get_oid(the_repository, arg, &oid)) {
+		if (!repo_get_oid(repo, arg, &oid)) {
 			struct reference ref = {
 				.name = arg,
 				.oid = &oid,
 			};
 
-			snapshot_ref(&ref, snap);
+			snapshot_ref(&ref, &data);
 			continue;
 		}
 		error(_("invalid parameter: expected sha1, got '%s'"), arg);
@@ -611,8 +622,8 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 		return;
 	}
 
-	refs_for_each_ref_ext(get_main_ref_store(the_repository),
-			      snapshot_ref, snap, &opts);
+	refs_for_each_ref_ext(get_main_ref_store(repo),
+			      snapshot_ref, &data, &opts);
 
 	worktrees = get_worktrees();
 	for (p = worktrees; *p; p++) {
@@ -621,7 +632,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 
 		strbuf_worktree_ref(wt, &refname, "HEAD");
 
-		head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+		head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(repo),
 							 refname.buf, 0, &head_oid, NULL);
 
 		if (head_points_at && !is_null_oid(&head_oid)) {
@@ -630,7 +641,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 				.oid = &head_oid,
 			};
 
-			snapshot_ref(&ref, snap);
+			snapshot_ref(&ref, &data);
 		}
 		strbuf_release(&refname);
 
@@ -1039,7 +1050,7 @@ int cmd_fsck(int argc,
 	 * 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);
+	snapshot_refs(repo, &snap, argc, argv);
 
 	/* Ensure we get a "fresh" view of the odb */
 	odb_reprepare(repo->objects);
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:02 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 07/12] builtin/fsck: stop using `the_repository` when checking refs

We implicitly rely on `the_repository` when checking refs. Refactor this to instead inject the repository via the callback payload.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fsck.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
Show changes to builtin/fsck.c +6 −5
diff --git a/builtin/fsck.c b/builtin/fsck.c
index edbff16add..efc60862ae 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -574,11 +574,12 @@ static int snapshot_ref(const struct reference *ref, void *cb_data)
 	return 0;
 }
 
-static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
+static int fsck_handle_ref(const struct reference *ref, void *cb_data)
 {
+	struct repository *repo = cb_data;
 	struct object *obj;
 
-	obj = parse_object(the_repository, ref->oid);
+	obj = parse_object(repo, ref->oid);
 	obj->flags |= USED;
 	fsck_put_object_name(&fsck_walk_options,
 			     ref->oid, "%s", ref->name);
@@ -665,7 +666,7 @@ static void free_snapshot_refs(struct snapshot *snap)
 	free(snap->ref);
 }
 
-static void process_refs(struct snapshot *snap)
+static void process_refs(struct repository *repo, struct snapshot *snap)
 {
 	struct worktree **worktrees, **p;
 
@@ -674,7 +675,7 @@ static void process_refs(struct snapshot *snap)
 			.name = snap->ref[i].refname,
 			.oid = &snap->ref[i].oid,
 		};
-		fsck_handle_ref(&ref, NULL);
+		fsck_handle_ref(&ref, repo);
 	}
 
 	if (include_reflogs) {
@@ -1095,7 +1096,7 @@ int cmd_fsck(int argc,
 	}
 
 	/* Process the snapshotted refs and the reflogs. */
-	process_refs(&snap);
+	process_refs(repo, &snap);
 
 	/* If not given any explicit objects, process index files too. */
 	if (!argc)
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:02 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 08/12] builtin/fsck: stop using `the_repository` when checking reflogs

We implicitly rely on `the_repository` when checking reflogs. Refactor this to instead inject the repository via the callback payload.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fsck.c | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)
Show changes to builtin/fsck.c +14 −10
diff --git a/builtin/fsck.c b/builtin/fsck.c
index efc60862ae..be9dbba2da 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -468,13 +468,14 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
 
 static int default_refs;
 
-static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
-	timestamp_t timestamp)
+static void fsck_handle_reflog_oid(struct repository *repo,
+				   const char *refname, struct object_id *oid,
+				   timestamp_t timestamp)
 {
 	struct object *obj;
 
 	if (!is_null_oid(oid)) {
-		obj = lookup_object(the_repository, oid);
+		obj = lookup_object(repo, oid);
 		if (obj && (obj->flags & HAS_OBJ)) {
 			if (timestamp)
 				fsck_put_object_name(&fsck_walk_options, oid,
@@ -482,7 +483,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 						     refname, timestamp);
 			obj->flags |= USED;
 			mark_object_reachable(obj);
-		} else if (!is_promisor_object(the_repository, oid)) {
+		} else if (!is_promisor_object(repo, oid)) {
 			error(_("%s: invalid reflog entry %s"),
 			      refname, oid_to_hex(oid));
 			errors_found |= ERROR_REACHABLE;
@@ -494,8 +495,10 @@ static int fsck_handle_reflog_ent(const char *refname,
 				  struct object_id *ooid, struct object_id *noid,
 				  const char *email UNUSED,
 				  timestamp_t timestamp, int tz UNUSED,
-				  const char *message UNUSED, void *cb_data UNUSED)
+				  const char *message UNUSED, void *cb_data)
 {
+	struct repository *repo = cb_data;
+
 	if (now && timestamp > now)
 		return 0;
 
@@ -503,19 +506,20 @@ static int fsck_handle_reflog_ent(const char *refname,
 		fprintf_ln(stderr, _("Checking reflog %s->%s"),
 			   oid_to_hex(ooid), oid_to_hex(noid));
 
-	fsck_handle_reflog_oid(refname, ooid, 0);
-	fsck_handle_reflog_oid(refname, noid, timestamp);
+	fsck_handle_reflog_oid(repo, refname, ooid, 0);
+	fsck_handle_reflog_oid(repo, refname, noid, timestamp);
 	return 0;
 }
 
 static int fsck_handle_reflog(const char *logname, void *cb_data)
 {
 	struct strbuf refname = STRBUF_INIT;
+	struct worktree *wt = cb_data;
 
-	strbuf_worktree_ref(cb_data, &refname, logname);
-	refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+	strbuf_worktree_ref(wt, &refname, logname);
+	refs_for_each_reflog_ent(get_main_ref_store(wt->repo),
 				 refname.buf, fsck_handle_reflog_ent,
-				 NULL);
+				 wt->repo);
 	strbuf_release(&refname);
 	return 0;
 }
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:03 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 09/12] builtin/fsck: stop using `the_repository` with loose objects

We depend on `the_repository` when performing consistency checks for loose objects. Refactor this to use a context-provided repository instead that is injected via the `struct for_each_loose_cb`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fsck.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
Show changes to builtin/fsck.c +8 −6
diff --git a/builtin/fsck.c b/builtin/fsck.c
index be9dbba2da..e8bdec7cd0 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -711,27 +711,28 @@ static void process_refs(struct repository *repo, struct snapshot *snap)
 	}
 }
 
-struct for_each_loose_cb
-{
+struct for_each_loose_cb {
+	struct repository *repo;
 	struct progress *progress;
 };
 
 static int fsck_loose(const struct object_id *oid, const char *path,
-		      void *data UNUSED)
+		      void *cb_data)
 {
+	struct for_each_loose_cb *data = cb_data;
 	struct object *obj;
 	enum object_type type = OBJ_NONE;
 	unsigned long size;
 	void *contents = NULL;
 	int eaten;
 	struct object_info oi = OBJECT_INFO_INIT;
-	struct object_id real_oid = *null_oid(the_hash_algo);
+	struct object_id real_oid = *null_oid(data->repo->hash_algo);
 	int err = 0;
 
 	oi.sizep = &size;
 	oi.typep = &type;
 
-	if (read_loose_object(the_repository, path, oid, &real_oid, &contents, &oi) < 0) {
+	if (read_loose_object(data->repo, path, oid, &real_oid, &contents, &oi) < 0) {
 		if (contents && !oideq(&real_oid, oid))
 			err = error(_("%s: hash-path mismatch, found at: %s"),
 				    oid_to_hex(&real_oid), path);
@@ -748,7 +749,7 @@ static int fsck_loose(const struct object_id *oid, const char *path,
 	if (!contents && type != OBJ_BLOB)
 		BUG("read_loose_object streamed a non-blob");
 
-	obj = parse_object_buffer(the_repository, oid, type, size,
+	obj = parse_object_buffer(data->repo, oid, type, size,
 				  contents, &eaten);
 
 	if (!obj) {
@@ -790,6 +791,7 @@ static void fsck_source(struct repository *repo, struct odb_source *source)
 {
 	struct progress *progress = NULL;
 	struct for_each_loose_cb cb_data = {
+		.repo = source->odb->repo,
 		.progress = progress,
 	};
 
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:03 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 10/12] builtin/fsck: stop using `the_repository` when checking packed objects

We implicitly rely on `the_repository` when checking objects part of a packfile. These objects are iterated over via `verify_pack()`, which is provided by the packfile subsystem, and a callback function is then invoked for each of the objects in that specific pack.

Unfortunately, it is not possible to provide a payload to the callback function. Refactor `verify_pack()` to accept a payload that is passed through to the callback so that we can inject the repository and get rid of the use of `the_repository`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fsck.c | 11 ++++++-----
 pack-check.c   |  7 ++++---
 pack.h         |  9 +++++++--
 3 files changed, 17 insertions(+), 10 deletions(-)
Show changes to 3 files +17 −10

builtin/fsck.c, pack-check.c, pack.h

diff --git a/builtin/fsck.c b/builtin/fsck.c
index e8bdec7cd0..22ca1200a2 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -447,15 +447,16 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 }
 
 static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
-			   unsigned long size, void *buffer, int *eaten)
+			   unsigned long size, void *buffer, int *eaten, void *cb_data)
 {
+	struct repository *repo = cb_data;
+	struct object *obj;
+
 	/*
 	 * Note, buffer may be NULL if type is OBJ_BLOB. See
 	 * verify_packfile(), data_valid variable for details.
 	 */
-	struct object *obj;
-	obj = parse_object_buffer(the_repository, oid, type, size, buffer,
-				  eaten);
+	obj = parse_object_buffer(repo, oid, type, size, buffer, eaten);
 	if (!obj) {
 		errors_found |= ERROR_OBJECT;
 		return error(_("%s: object corrupt or missing"),
@@ -1089,7 +1090,7 @@ int cmd_fsck(int argc,
 			repo_for_each_pack(repo, p) {
 				/* verify gives error messages itself */
 				if (verify_pack(repo,
-						p, fsck_obj_buffer,
+						p, fsck_obj_buffer, repo,
 						progress, count))
 					errors_found |= ERROR_PACK;
 				count += p->num_objects;
diff --git a/pack-check.c b/pack-check.c
index 7378c80730..79992bb509 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -53,6 +53,7 @@ static int verify_packfile(struct repository *r,
 			   struct packed_git *p,
 			   struct pack_window **w_curs,
 			   verify_fn fn,
+			   void *fn_data,
 			   struct progress *progress, uint32_t base_count)
 
 {
@@ -161,7 +162,7 @@ static int verify_packfile(struct repository *r,
 				    oid_to_hex(&oid), p->pack_name);
 		else if (fn) {
 			int eaten = 0;
-			err |= fn(&oid, type, size, data, &eaten);
+			err |= fn(&oid, type, size, data, &eaten, fn_data);
 			if (eaten)
 				data = NULL;
 		}
@@ -192,7 +193,7 @@ int verify_pack_index(struct packed_git *p)
 	return err;
 }
 
-int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,
+int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, void *fn_data,
 		struct progress *progress, uint32_t base_count)
 {
 	int err = 0;
@@ -202,7 +203,7 @@ int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,
 	if (!p->index_data)
 		return -1;
 
-	err |= verify_packfile(r, p, &w_curs, fn, progress, base_count);
+	err |= verify_packfile(r, p, &w_curs, fn, fn_data, progress, base_count);
 	unuse_pack(&w_curs);
 
 	return err;
diff --git a/pack.h b/pack.h
index ec76472e49..1cde92082b 100644
--- a/pack.h
+++ b/pack.h
@@ -85,7 +85,11 @@ struct pack_idx_entry {
 
 struct progress;
 /* Note, the data argument could be NULL if object type is blob */
-typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*);
+typedef int (*verify_fn)(const struct object_id *oid,
+			 enum object_type type,
+			 unsigned long size,
+			 void *buffer, int *eaten,
+			 void *fn_data);
 
 const char *write_idx_file(struct repository *repo,
 			   const char *index_name,
@@ -95,7 +99,8 @@ const char *write_idx_file(struct repository *repo,
 			   const unsigned char *sha1);
 int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
 int verify_pack_index(struct packed_git *);
-int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);
+int verify_pack(struct repository *, struct packed_git *, verify_fn fn, void *fn_data,
+		struct progress *, uint32_t);
 off_t write_pack_header(struct hashfile *f, uint32_t);
 void fixup_pack_header_footer(const struct git_hash_algo *, int,
 			      unsigned char *, const char *, uint32_t,
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:03 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 11/12] builtin/fsck: stop using `the_repository` when marking objects

We implicitly rely on `the_repository` when marking objects for connectivity. Refactor this to instead inject the repository via the callback payload.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fsck.c | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)
Show changes to builtin/fsck.c +14 −12
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 22ca1200a2..e37f708480 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -124,7 +124,7 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED,
 static struct object_array pending;
 
 static int mark_object(struct object *obj, enum object_type type,
-		       void *data, struct fsck_options *options UNUSED)
+		       void *data, struct fsck_options *options)
 {
 	struct object *parent = data;
 
@@ -153,7 +153,7 @@ static int mark_object(struct object *obj, enum object_type type,
 		return 0;
 	obj->flags |= REACHABLE;
 
-	if (is_promisor_object(the_repository, &obj->oid))
+	if (is_promisor_object(options->repo, &obj->oid))
 		/*
 		 * Further recursion does not need to be performed on this
 		 * object since it is a promisor object (so it does not need to
@@ -162,7 +162,7 @@ static int mark_object(struct object *obj, enum object_type type,
 		return 0;
 
 	if (!(obj->flags & HAS_OBJ)) {
-		if (parent && !odb_has_object(the_repository->objects, &obj->oid,
+		if (parent && !odb_has_object(options->repo->objects, &obj->oid,
 					      HAS_OBJECT_RECHECK_PACKED)) {
 			printf_ln(_("broken link from %7s %s\n"
 				    "              to %7s %s"),
@@ -181,7 +181,7 @@ static int mark_object(struct object *obj, enum object_type type,
 
 static void mark_object_reachable(struct object *obj)
 {
-	mark_object(obj, OBJ_ANY, NULL, NULL);
+	mark_object(obj, OBJ_ANY, NULL, &fsck_walk_options);
 }
 
 static int traverse_one_object(struct object *obj)
@@ -222,10 +222,11 @@ static int mark_used(struct object *obj, enum object_type type UNUSED,
 
 static int mark_unreachable_referents(const struct object_id *oid,
 				      struct object_info *oi UNUSED,
-				      void *data UNUSED)
+				      void *data)
 {
+	struct repository *repo = data;
 	struct fsck_options options;
-	struct object *obj = lookup_object(the_repository, oid);
+	struct object *obj = lookup_object(data, oid);
 
 	if (!obj || !(obj->flags & HAS_OBJ))
 		return 0; /* not part of our original set */
@@ -237,13 +238,13 @@ static int mark_unreachable_referents(const struct object_id *oid,
 	 * (and we want to avoid parsing blobs).
 	 */
 	if (obj->type == OBJ_NONE) {
-		enum object_type type = odb_read_object_info(the_repository->objects,
+		enum object_type type = odb_read_object_info(repo->objects,
 							     &obj->oid, NULL);
 		if (type > 0)
 			object_as_type(obj, type, 0);
 	}
 
-	fsck_options_init(&options, the_repository, FSCK_OPTIONS_DEFAULT);
+	fsck_options_init(&options, repo, FSCK_OPTIONS_DEFAULT);
 	options.walk = mark_used;
 	fsck_walk(obj, NULL, &options);
 	if (obj->type == OBJ_TREE)
@@ -385,7 +386,7 @@ static void check_connectivity(struct repository *repo)
 		 * traversal.
 		 */
 		odb_for_each_object(repo->objects, NULL,
-				    mark_unreachable_referents, NULL, 0);
+				    mark_unreachable_referents, repo, 0);
 	}
 
 	/* Look up all the requirements, warn about missing objects.. */
@@ -909,9 +910,10 @@ static void fsck_index(struct index_state *istate, const char *index_path,
 
 static int mark_object_for_connectivity(const struct object_id *oid,
 					struct object_info *oi UNUSED,
-					void *cb_data UNUSED)
+					void *cb_data)
 {
-	struct object *obj = lookup_unknown_object(the_repository, oid);
+	struct repository *repo = cb_data;
+	struct object *obj = lookup_unknown_object(repo, oid);
 	obj->flags |= HAS_OBJ;
 	return 0;
 }
@@ -1065,7 +1067,7 @@ int cmd_fsck(int argc,
 
 	if (connectivity_only) {
 		odb_for_each_object(repo->objects, NULL,
-				    mark_object_for_connectivity, NULL, 0);
+				    mark_object_for_connectivity, repo, 0);
 	} else {
 		odb_prepare_alternates(repo->objects);
 		for (source = repo->objects->sources; source; source = source->next)
-- 
2.53.0.1118.gaef5881109.dirty
Patrick SteinhardtMar 23, 2026, 15:03 UTC in reply to Patrick Steinhardt on lore

[PATCH v2 12/12] builtin/fsck: stop using `the_repository` in error reporting

In the preceding commit we have introduced the repository into `struct fsck_object_report`. This allows us to drop remaining uses of the global `the_repository` variable.

Drop them and remove `USE_THE_REPOSITORY_VARIABLE`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fsck.c | 46 +++++++++++++++++++++++-----------------------
 1 file changed, 23 insertions(+), 23 deletions(-)
Show changes to builtin/fsck.c +23 −23
diff --git a/builtin/fsck.c b/builtin/fsck.c
index e37f708480..99696604b8 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
 #include "builtin.h"
 #include "gettext.h"
 #include "hex.h"
@@ -66,14 +65,14 @@ static const char *describe_object(const struct object_id *oid)
 	return fsck_describe_object(&fsck_walk_options, oid);
 }
 
-static const char *printable_type(const struct object_id *oid,
+static const char *printable_type(struct repository *repo,
+				  const struct object_id *oid,
 				  enum object_type type)
 {
 	const char *ret;
 
 	if (type == OBJ_NONE)
-		type = odb_read_object_info(the_repository->objects,
-					    oid, NULL);
+		type = odb_read_object_info(repo->objects, oid, NULL);
 
 	ret = type_name(type);
 	if (!ret)
@@ -82,17 +81,17 @@ static const char *printable_type(const struct object_id *oid,
 	return ret;
 }
 
-static int objerror(struct object *obj, const char *err)
+static int objerror(struct repository *repo, struct object *obj, const char *err)
 {
 	errors_found |= ERROR_OBJECT;
 	/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
 	fprintf_ln(stderr, _("error in %s %s: %s"),
-		   printable_type(&obj->oid, obj->type),
+		   printable_type(repo, &obj->oid, obj->type),
 		   describe_object(&obj->oid), err);
 	return -1;
 }
 
-static int fsck_objects_error_func(struct fsck_options *o UNUSED,
+static int fsck_objects_error_func(struct fsck_options *o,
 				   void *fsck_report,
 				   enum fsck_msg_type msg_type,
 				   enum fsck_msg_id msg_id UNUSED,
@@ -106,13 +105,13 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED,
 	case FSCK_WARN:
 		/* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
 		fprintf_ln(stderr, _("warning in %s %s: %s"),
-			   printable_type(oid, object_type),
+			   printable_type(o->repo, oid, object_type),
 			   describe_object(oid), message);
 		return 0;
 	case FSCK_ERROR:
 		/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
 		fprintf_ln(stderr, _("error in %s %s: %s"),
-			   printable_type(oid, object_type),
+			   printable_type(o->repo, oid, object_type),
 			   describe_object(oid), message);
 		return 1;
 	default:
@@ -136,7 +135,7 @@ static int mark_object(struct object *obj, enum object_type type,
 	if (!obj) {
 		/* ... these references to parent->fld are safe here */
 		printf_ln(_("broken link from %7s %s"),
-			  printable_type(&parent->oid, parent->type),
+			  printable_type(options->repo, &parent->oid, parent->type),
 			  describe_object(&parent->oid));
 		printf_ln(_("broken link from %7s %s"),
 			  (type == OBJ_ANY ? _("unknown") : type_name(type)),
@@ -147,7 +146,7 @@ static int mark_object(struct object *obj, enum object_type type,
 
 	if (type != OBJ_ANY && obj->type != type)
 		/* ... and the reference to parent is safe here */
-		objerror(parent, _("wrong object type in link"));
+		objerror(options->repo, parent, _("wrong object type in link"));
 
 	if (obj->flags & REACHABLE)
 		return 0;
@@ -166,9 +165,9 @@ static int mark_object(struct object *obj, enum object_type type,
 					      HAS_OBJECT_RECHECK_PACKED)) {
 			printf_ln(_("broken link from %7s %s\n"
 				    "              to %7s %s"),
-				  printable_type(&parent->oid, parent->type),
+				  printable_type(options->repo, &parent->oid, parent->type),
 				  describe_object(&parent->oid),
-				  printable_type(&obj->oid, obj->type),
+				  printable_type(options->repo, &obj->oid, obj->type),
 				  describe_object(&obj->oid));
 			errors_found |= ERROR_REACHABLE;
 		}
@@ -269,7 +268,7 @@ static void check_reachable_object(struct repository *repo, struct object *obj)
 		if (has_object_pack(repo, &obj->oid))
 			return; /* it is in pack - forget about it */
 		printf_ln(_("missing %s %s"),
-			  printable_type(&obj->oid, obj->type),
+			  printable_type(repo, &obj->oid, obj->type),
 			  describe_object(&obj->oid));
 		errors_found |= ERROR_REACHABLE;
 		return;
@@ -296,7 +295,7 @@ static void check_unreachable_object(struct repository *repo, struct object *obj
 	 */
 	if (show_unreachable) {
 		printf_ln(_("unreachable %s %s"),
-			  printable_type(&obj->oid, obj->type),
+			  printable_type(repo, &obj->oid, obj->type),
 			  describe_object(&obj->oid));
 		return;
 	}
@@ -316,7 +315,7 @@ static void check_unreachable_object(struct repository *repo, struct object *obj
 	if (!(obj->flags & USED)) {
 		if (show_dangling)
 			printf_ln(_("dangling %s %s"),
-				  printable_type(&obj->oid, obj->type),
+				  printable_type(repo, &obj->oid, obj->type),
 				  describe_object(&obj->oid));
 		if (write_lost_and_found) {
 			char *filename = repo_git_path(repo, "lost-found/%s/%s",
@@ -402,7 +401,8 @@ static void check_connectivity(struct repository *repo)
 	}
 }
 
-static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
+static int fsck_obj(struct repository *repo,
+		    struct object *obj, void *buffer, unsigned long size)
 {
 	int err;
 
@@ -412,11 +412,11 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 
 	if (verbose)
 		fprintf_ln(stderr, _("Checking %s %s"),
-			   printable_type(&obj->oid, obj->type),
+			   printable_type(repo, &obj->oid, obj->type),
 			   describe_object(&obj->oid));
 
 	if (fsck_walk(obj, NULL, &fsck_obj_options))
-		objerror(obj, _("broken links"));
+		objerror(repo, obj, _("broken links"));
 	err = fsck_object(obj, buffer, size, &fsck_obj_options);
 	if (err)
 		goto out;
@@ -434,7 +434,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 
 		if (show_tags && tag->tagged) {
 			printf_ln(_("tagged %s %s (%s) in %s"),
-				  printable_type(&tag->tagged->oid, tag->tagged->type),
+				  printable_type(repo, &tag->tagged->oid, tag->tagged->type),
 				  describe_object(&tag->tagged->oid),
 				  tag->tag,
 				  describe_object(&tag->object.oid));
@@ -465,7 +465,7 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
 	}
 	obj->flags &= ~(REACHABLE | SEEN);
 	obj->flags |= HAS_OBJ;
-	return fsck_obj(obj, buffer, size);
+	return fsck_obj(repo, obj, buffer, size);
 }
 
 static int default_refs;
@@ -765,7 +765,7 @@ static int fsck_loose(const struct object_id *oid, const char *path,
 
 	obj->flags &= ~(REACHABLE | SEEN);
 	obj->flags |= HAS_OBJ;
-	if (fsck_obj(obj, contents, size))
+	if (fsck_obj(data->repo, obj, contents, size))
 		errors_found |= ERROR_OBJECT;
 
 	if (!eaten)
@@ -830,7 +830,7 @@ static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const
 		fsck_put_object_name(&fsck_walk_options, &it->oid, ":");
 		mark_object_reachable(obj);
 		if (obj->type != OBJ_TREE)
-			err |= objerror(obj, _("non-tree in cache-tree"));
+			err |= objerror(repo, obj, _("non-tree in cache-tree"));
 	}
 	for (i = 0; i < it->subtree_nr; i++)
 		err |= fsck_cache_tree(repo, it->down[i]->cache_tree, index_path);
-- 
2.53.0.1118.gaef5881109.dirty
Junio C HamanoMar 23, 2026, 15:43 UTC in reply to Patrick Steinhardt on lore

Re: [PATCH v2 01/12] fetch-pack: move fsck options into function scope

Patrick Steinhardt <ps@pks.im> writes:
> When fetching a packfile, we optionally verify received objects via the
> fsck subsystem. The options for those consistency checks are declared in
> global scope without a good reason, and they are never cleaned up. So in
> case the options are reused, they may accumulate more state over time.
Great realization.

So we have two code paths (the original and v2), both of which used to use the one in the global scope without cleaning up, but now we have a way to proerly clean them up. Very nice.

Show 57 quoted lines
> Furthermore, in subsequent changes we'll introduce a repository pointer
> into the structure. Obviously though, we don't have a repository
> available at static time, except for `the_repository`, which we don't
> want to use here.
>
> Refactor the code to move the options into the respective functions and
> properly manage their lifecycle.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  fetch-pack.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/fetch-pack.c b/fetch-pack.c
> index 6ecd468ef7..ec5abb92b5 100644
> --- a/fetch-pack.c
> +++ b/fetch-pack.c
> @@ -51,7 +51,6 @@ static int server_supports_filtering;
>  static int advertise_sid;
>  static struct shallow_lock shallow_lock;
>  static const char *alternate_shallow_file;
> -static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
>  static struct strbuf fsck_msg_types = STRBUF_INIT;
>  static struct string_list uri_protocols = STRING_LIST_INIT_DUP;
>  
> @@ -1100,6 +1099,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
>  				 struct shallow_info *si,
>  				 struct string_list *pack_lockfiles)
>  {
> +	struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
>  	struct repository *r = the_repository;
>  	struct ref *ref = copy_ref_list(orig_ref);
>  	struct object_id oid;
> @@ -1235,6 +1235,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
>  		die("fsck failed");
>  
>   all_done:
> +	fsck_options_clear(&fsck_options);
>  	if (negotiator)
>  		negotiator->release(negotiator);
>  	return ref;
> @@ -1654,6 +1655,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
>  				    struct string_list *pack_lockfiles)
>  {
>  	struct repository *r = the_repository;
> +	struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
>  	struct ref *ref = copy_ref_list(orig_ref);
>  	enum fetch_state state = FETCH_CHECK_LOCAL;
>  	struct oidset common = OIDSET_INIT;
> @@ -1882,6 +1884,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
>  	if (negotiator)
>  		negotiator->release(negotiator);
>  
> +	fsck_options_clear(&fsck_options);
>  	oidset_clear(&common);
>  	return ref;
>  }
Junio C HamanoMar 23, 2026, 15:48 UTC in reply to Patrick Steinhardt on lore

Re: [PATCH v2 02/12] fsck: initialize fsck options via a function

Patrick Steinhardt <ps@pks.im> writes:
Show 44 quoted lines
> +void fsck_options_init(struct fsck_options *options,
> +		       enum fsck_options_type type)
> +{
> +	static const struct fsck_options defaults[] = {
> +		[FSCK_OPTIONS_DEFAULT] = {
> +			.skip_oids = OIDSET_INIT,
> +			.gitmodules_found = OIDSET_INIT,
> +			.gitmodules_done = OIDSET_INIT,
> +			.gitattributes_found = OIDSET_INIT,
> +			.gitattributes_done = OIDSET_INIT,
> +			.error_func = fsck_objects_error_function
> +		},
> +		[FSCK_OPTIONS_STRICT] = {
> +			.strict = 1,
> +			.gitmodules_found = OIDSET_INIT,
> +			.gitmodules_done = OIDSET_INIT,
> +			.gitattributes_found = OIDSET_INIT,
> +			.gitattributes_done = OIDSET_INIT,
> +			.error_func = fsck_objects_error_function,
> +		},
> +		[FSCK_OPTIONS_MISSING_GITMODULES] = {
> +			.strict = 1,
> +			.gitmodules_found = OIDSET_INIT,
> +			.gitmodules_done = OIDSET_INIT,
> +			.gitattributes_found = OIDSET_INIT,
> +			.gitattributes_done = OIDSET_INIT,
> +			.error_func = fsck_objects_error_cb_print_missing_gitmodules,
> +		},
> +		[FSCK_OPTIONS_REFS] = {
> +			.error_func = fsck_refs_error_function,
> +		},
> +	};
> +
> +	switch (type) {
> +	case FSCK_OPTIONS_DEFAULT:
> +	case FSCK_OPTIONS_STRICT:
> +	case FSCK_OPTIONS_MISSING_GITMODULES:
> +	case FSCK_OPTIONS_REFS:
> +		memcpy(options, &defaults[type], sizeof(*options));
> +		break;
> +	default:
> +		BUG("unknown fsck options type %d", type);
> +	}
> +}

Wow, nice reorganization that migrates the earlier definitions from the header file.

By reusing these FSCK_OPTIONS_* names as an enum elements, we will let the compiler catch if any other branch adds new uses of these names with their original meaning, which is a nice touch, too.

Show 74 quoted lines
>  void fsck_options_clear(struct fsck_options *options)
>  {
>  	free(options->msg_type);
> diff --git a/fsck.h b/fsck.h
> index 65ecbb7fe1..9c973b53b2 100644
> --- a/fsck.h
> +++ b/fsck.h
> @@ -180,34 +180,6 @@ struct fsck_options {
>  	kh_oid_map_t *object_names;
>  };
>  
> -#define FSCK_OPTIONS_DEFAULT { \
> -	.skip_oids = OIDSET_INIT, \
> -	.gitmodules_found = OIDSET_INIT, \
> -	.gitmodules_done = OIDSET_INIT, \
> -	.gitattributes_found = OIDSET_INIT, \
> -	.gitattributes_done = OIDSET_INIT, \
> -	.error_func = fsck_objects_error_function \
> -}
> -#define FSCK_OPTIONS_STRICT { \
> -	.strict = 1, \
> -	.gitmodules_found = OIDSET_INIT, \
> -	.gitmodules_done = OIDSET_INIT, \
> -	.gitattributes_found = OIDSET_INIT, \
> -	.gitattributes_done = OIDSET_INIT, \
> -	.error_func = fsck_objects_error_function, \
> -}
> -#define FSCK_OPTIONS_MISSING_GITMODULES { \
> -	.strict = 1, \
> -	.gitmodules_found = OIDSET_INIT, \
> -	.gitmodules_done = OIDSET_INIT, \
> -	.gitattributes_found = OIDSET_INIT, \
> -	.gitattributes_done = OIDSET_INIT, \
> -	.error_func = fsck_objects_error_cb_print_missing_gitmodules, \
> -}
> -#define FSCK_REFS_OPTIONS_DEFAULT { \
> -	.error_func = fsck_refs_error_function, \
> -}
> -
>  /* descend in all linked child objects
>   * the return value is:
>   *    -1	error in processing the object
> @@ -255,6 +227,16 @@ int fsck_finish(struct fsck_options *options);
>   */
>  bool fsck_has_queued_checks(struct fsck_options *options);
>  
> +enum fsck_options_type {
> +	FSCK_OPTIONS_DEFAULT,
> +	FSCK_OPTIONS_STRICT,
> +	FSCK_OPTIONS_MISSING_GITMODULES,
> +	FSCK_OPTIONS_REFS,
> +};
> +
> +void fsck_options_init(struct fsck_options *options,
> +		       enum fsck_options_type type);
> +
>  /*
>   * Clear the fsck_options struct, freeing any allocated memory.
>   */
> diff --git a/object-file.c b/object-file.c
> index c62e5496e0..186b2ff764 100644
> --- a/object-file.c
> +++ b/object-file.c
> @@ -1279,8 +1279,9 @@ static int index_mem(struct index_state *istate,
>  		}
>  	}
>  	if (flags & INDEX_FORMAT_CHECK) {
> -		struct fsck_options opts = FSCK_OPTIONS_DEFAULT;
> +		struct fsck_options opts;
>  
> +		fsck_options_init(&opts, FSCK_OPTIONS_DEFAULT);
>  		opts.strict = 1;
>  		opts.error_func = hash_format_check_report;
>  		if (fsck_buffer(null_oid(istate->repo->hash_algo), type, buf, size, &opts))
Junio C HamanoMar 31, 2026, 22:05 UTC in reply to Patrick Steinhardt on lore

Re: [PATCH v2 00/12] fsck: drop use of `the_repository`

Patrick Steinhardt <ps@pks.im> writes:
Show 8 quoted lines
> this patch series refactors "{builtin/,}fsck.c" to not depend on
> `the_repository` anymore. These refactorings are mostly done to prepare
> for upcoming changes where we'll make backend-specific fsck checks
> pluggable.
>
> Changes in v2:
>   - Propagate the repository via `struct fsck_options`.
>   - Link to v1: https://lore.kernel.org/r/20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im
Looking good.  Shall we mark the topic for 'next' now?
Thanks.
Patrick SteinhardtMar 31, 2026, 22:50 UTC in reply to Junio C Hamano on lore

Re: [PATCH v2 00/12] fsck: drop use of `the_repository`

On Tue, Mar 31, 2026 at 03:05:04PM -0700, Junio C Hamano wrote:
Show 12 quoted lines
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > this patch series refactors "{builtin/,}fsck.c" to not depend on
> > `the_repository` anymore. These refactorings are mostly done to prepare
> > for upcoming changes where we'll make backend-specific fsck checks
> > pluggable.
> >
> > Changes in v2:
> >   - Propagate the repository via `struct fsck_options`.
> >   - Link to v1: https://lore.kernel.org/r/20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im
> 
> Looking good.  Shall we mark the topic for 'next' now?
No more changes are planned on my side, so I'm fine with that. Thanks!
Patrick

Back to recent threads

[PATCH v2 01/12] fetch-pack: move fsck options into function scope | The Git List