{"thread":{"id":"65317","subject":"[PATCH 00/14] fsck: drop use of `the_repository`","startedAt":"2026-03-20T11:47:44Z","lastAt":"2026-03-23T12:22:45Z","messageCount":18,"participants":["Patrick Steinhardt","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":14},"messages":[{"id":"539513","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","threadId":"65317","inReplyTo":null,"subject":"[PATCH 00/14] fsck: drop use of `the_repository`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:05Z","receivedAt":"2026-03-20T11:47:44Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"Hi,\n\nthis patch series refactors \"{builtin/,}fsck.c\" to not depend on\n`the_repository` anymore. These refactorings are mostly done to prepare\nfor upcoming changes where we'll make backend-specific fsck checks\npluggable.\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (14):\n      fsck: drop `the_repository` in `fsck_walk()`\n      fsck: drop `the_repository` in `fsck_finish()`\n      fsck: refactor interface to parse fsck options\n      fsck: drop `the_repository` in `fsck_set_msg_types()`\n      fsck: stop relying on global state via `parse_oid_hex()`\n      builtin/fsck: fix trivial dependence on `the_repository`\n      builtin/fsck: stop using `the_repository` when snapshotting refs\n      builtin/fsck: stop using `the_repository` when checking refs\n      builtin/fsck: stop using `the_repository` when checking reflogs\n      builtin/fsck: stop using `the_repository` with loose objects\n      builtin/fsck: stop using `the_repository` when checking packed objects\n      builtin/fsck: stop using `the_repository` when marking objects\n      fsck: provide repository in `struct fsck_report_object`\n      builtin/fsck: stop using `the_repository` in error reporting\n\n builtin/fsck.c           | 299 ++++++++++++++++-------------\n builtin/index-pack.c     |  15 +-\n builtin/mktag.c          |   7 +-\n builtin/refs.c           |   2 +-\n builtin/unpack-objects.c |   9 +-\n fetch-pack.c             |   4 +-\n fsck.c                   | 485 ++++++++++++++++++++++++++++-------------------\n fsck.h                   |  41 ++--\n object-file.c            |   5 +-\n pack-check.c             |   7 +-\n pack.h                   |   9 +-\n 11 files changed, 520 insertions(+), 363 deletions(-)\n\n\n---\nbase-commit: 7ff1e8dc1e1680510c96e69965b3fa81372c5037\nchange-id: 20260320-b4-pks-fsck-without-the-repository-4ddc4c8ed61e\n\n"},{"id":"539514","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-1-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 01/14] fsck: drop `the_repository` in `fsck_walk()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:06Z","receivedAt":"2026-03-20T11:47:46Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"The function `fsck_walk()` and its object type specific functions\n`fsck_walk_tree()` et al implicitly rely on `the_repository`. Remove\nthis dependency by injecting the repository as a parameter instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c           |  6 +++---\n builtin/index-pack.c     |  2 +-\n builtin/unpack-objects.c |  2 +-\n fsck.c                   | 40 ++++++++++++++++++++++++++--------------\n fsck.h                   | 15 ++++++++++++---\n 5 files changed, 43 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 9bab32effe..15477767c7 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -186,7 +186,7 @@ static void mark_object_reachable(struct object *obj)\n \n static int traverse_one_object(struct object *obj)\n {\n-\tint result = fsck_walk(obj, obj, &fsck_walk_options);\n+\tint result = fsck_walk(the_repository, obj, obj, &fsck_walk_options);\n \n \tif (obj->type == OBJ_TREE) {\n \t\tstruct tree *tree = (struct tree *)obj;\n@@ -244,7 +244,7 @@ static int mark_unreachable_referents(const struct object_id *oid,\n \t}\n \n \toptions.walk = mark_used;\n-\tfsck_walk(obj, NULL, &options);\n+\tfsck_walk(the_repository, obj, NULL, &options);\n \tif (obj->type == OBJ_TREE)\n \t\tfree_tree_buffer((struct tree *)obj);\n \n@@ -413,7 +413,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)\n \t\t\t   printable_type(&obj->oid, obj->type),\n \t\t\t   describe_object(&obj->oid));\n \n-\tif (fsck_walk(obj, NULL, &fsck_obj_options))\n+\tif (fsck_walk(the_repository, obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, _(\"broken links\"));\n \terr = fsck_object(obj, buffer, size, &fsck_obj_options);\n \tif (err)\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex d1e47279a8..a3d37d34cc 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -954,7 +954,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\tif (do_fsck_object &&\n \t\t\t    fsck_object(obj, buf, size, &fsck_options))\n \t\t\t\tdie(_(\"fsck error in packed object\"));\n-\t\t\tif (strict && fsck_walk(obj, NULL, &fsck_options))\n+\t\t\tif (strict && fsck_walk(the_repository, obj, NULL, &fsck_options))\n \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), oid_to_hex(&obj->oid));\n \t\t\tif (record_outgoing_links)\n \t\t\t\tdo_record_outgoing_links(obj);\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 6fc64e9e4b..52b62ff6d4 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -244,7 +244,7 @@ static int check_object(struct object *obj, enum object_type type,\n \tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))\n \t\tdie(\"fsck error in packed object\");\n \tfsck_options.walk = check_object;\n-\tif (fsck_walk(obj, NULL, &fsck_options))\n+\tif (fsck_walk(the_repository, obj, NULL, &fsck_options))\n \t\tdie(\"Error on reachable objects of %s\", oid_to_hex(&obj->oid));\n \twrite_cached_object(obj, obj_buf);\n \treturn 0;\ndiff --git a/fsck.c b/fsck.c\nindex 0f02cf8f77..c6b6f533be 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -353,14 +353,17 @@ const char *fsck_describe_object(struct fsck_options *options,\n \treturn buf->buf;\n }\n \n-static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n+static int fsck_walk_tree(struct repository *repo,\n+\t\t\t  struct tree *tree,\n+\t\t\t  void *data,\n+\t\t\t  struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n \tstruct name_entry entry;\n \tint res = 0;\n \tconst char *name;\n \n-\tif (repo_parse_tree(the_repository, tree))\n+\tif (repo_parse_tree(repo, tree))\n \t\treturn -1;\n \n \tname = fsck_get_object_name(options, &tree->object.oid);\n@@ -375,14 +378,14 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op\n \t\t\tcontinue;\n \n \t\tif (S_ISDIR(entry.mode)) {\n-\t\t\tobj = (struct object *)lookup_tree(the_repository, &entry.oid);\n+\t\t\tobj = (struct object *)lookup_tree(repo, &entry.oid);\n \t\t\tif (name && obj)\n \t\t\t\tfsck_put_object_name(options, &entry.oid, \"%s%s/\",\n \t\t\t\t\t\t     name, entry.path);\n \t\t\tresult = options->walk(obj, OBJ_TREE, data, options);\n \t\t}\n \t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {\n-\t\t\tobj = (struct object *)lookup_blob(the_repository, &entry.oid);\n+\t\t\tobj = (struct object *)lookup_blob(repo, &entry.oid);\n \t\t\tif (name && obj)\n \t\t\t\tfsck_put_object_name(options, &entry.oid, \"%s%s\",\n \t\t\t\t\t\t     name, entry.path);\n@@ -401,7 +404,10 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op\n \treturn res;\n }\n \n-static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)\n+static int fsck_walk_commit(struct repository *repo,\n+\t\t\t    struct commit *commit,\n+\t\t\t    void *data,\n+\t\t\t    struct fsck_options *options)\n {\n \tint counter = 0, generation = 0, name_prefix_len = 0;\n \tstruct commit_list *parents;\n@@ -409,7 +415,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio\n \tint result;\n \tconst char *name;\n \n-\tif (repo_parse_commit(the_repository, commit))\n+\tif (repo_parse_commit(repo, commit))\n \t\treturn -1;\n \n \tname = fsck_get_object_name(options, &commit->object.oid);\n@@ -417,7 +423,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio\n \t\tfsck_put_object_name(options, get_commit_tree_oid(commit),\n \t\t\t\t     \"%s:\", name);\n \n-\tresult = options->walk((struct object *) repo_get_commit_tree(the_repository, commit),\n+\tresult = options->walk((struct object *) repo_get_commit_tree(repo, commit),\n \t\t\t       OBJ_TREE, data, options);\n \tif (result < 0)\n \t\treturn result;\n@@ -470,34 +476,40 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio\n \treturn res;\n }\n \n-static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)\n+static int fsck_walk_tag(struct repository *repo,\n+\t\t\t struct tag *tag,\n+\t\t\t void *data,\n+\t\t\t struct fsck_options *options)\n {\n \tconst char *name = fsck_get_object_name(options, &tag->object.oid);\n \n-\tif (parse_tag(the_repository, tag))\n+\tif (parse_tag(repo, tag))\n \t\treturn -1;\n \tif (name)\n \t\tfsck_put_object_name(options, &tag->tagged->oid, \"%s\", name);\n \treturn options->walk(tag->tagged, OBJ_ANY, data, options);\n }\n \n-int fsck_walk(struct object *obj, void *data, struct fsck_options *options)\n+int fsck_walk(struct repository *repo,\n+\t      struct object *obj,\n+\t      void *data,\n+\t      struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n \n \tif (obj->type == OBJ_NONE)\n-\t\tparse_object(the_repository, &obj->oid);\n+\t\tparse_object(repo, &obj->oid);\n \n \tswitch (obj->type) {\n \tcase OBJ_BLOB:\n \t\treturn 0;\n \tcase OBJ_TREE:\n-\t\treturn fsck_walk_tree((struct tree *)obj, data, options);\n+\t\treturn fsck_walk_tree(repo, (struct tree *)obj, data, options);\n \tcase OBJ_COMMIT:\n-\t\treturn fsck_walk_commit((struct commit *)obj, data, options);\n+\t\treturn fsck_walk_commit(repo, (struct commit *)obj, data, options);\n \tcase OBJ_TAG:\n-\t\treturn fsck_walk_tag((struct tag *)obj, data, options);\n+\t\treturn fsck_walk_tag(repo, (struct tag *)obj, data, options);\n \tdefault:\n \t\terror(\"Unknown object type for %s\",\n \t\t      fsck_describe_object(options, &obj->oid));\ndiff --git a/fsck.h b/fsck.h\nindex 65ecbb7fe1..4bd54865fe 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -208,14 +208,23 @@ struct fsck_options {\n \t.error_func = fsck_refs_error_function, \\\n }\n \n-/* descend in all linked child objects\n- * the return value is:\n+/*\n+ * Perform consistency checks for the given object and all of its decendents.\n+ *\n+ * If set, the `walk` callback function in the options structure will be called\n+ * for every commit. The data parameter will be passed as callback data.\n+ *\n+ * Returns:\n+ *\n  *    -1\terror in processing the object\n  *    <0\treturn value of the callback, which lead to an abort\n  *    >0\treturn value of the first signaled error >0 (in the case of no other errors)\n  *    0\t\teverything OK\n  */\n-int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n+int fsck_walk(struct repository *repo,\n+\t      struct object *obj,\n+\t      void *data,\n+\t      struct fsck_options *options);\n \n /*\n  * Blob objects my pass a NULL data pointer, which indicates they are too large\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539515","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-2-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 02/14] fsck: drop `the_repository` in `fsck_finish()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:07Z","receivedAt":"2026-03-20T11:47:49Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"The function `fsck_finish()` and  its transitively called function\n`fsck_blobs()` implicitly rely on `the_repository`. Remove this\ndependency by injecting the repository as a parameter instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c           |  2 +-\n builtin/index-pack.c     |  2 +-\n builtin/unpack-objects.c |  2 +-\n fetch-pack.c             |  4 ++--\n fsck.c                   | 13 +++++++------\n fsck.h                   |  2 +-\n object-file.c            |  2 +-\n 7 files changed, 14 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 15477767c7..8f994010da 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -1075,7 +1075,7 @@ int cmd_fsck(int argc,\n \t\t\tstop_progress(&progress);\n \t\t}\n \n-\t\tif (fsck_finish(&fsck_obj_options))\n+\t\tif (fsck_finish(the_repository, &fsck_obj_options))\n \t\t\terrors_found |= ERROR_OBJECT;\n \t}\n \ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex a3d37d34cc..3d1f7dbd5f 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -2123,7 +2123,7 @@ int cmd_index_pack(int argc,\n \t\t\tdie(_(\"cannot perform queued object checks outside \"\n \t\t\t      \"of a repository\"));\n \n-\t\tif (fsck_finish(&fsck_options))\n+\t\tif (fsck_finish(the_repository, &fsck_options))\n \t\t\tdie(_(\"fsck error in pack objects\"));\n \t}\n \ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 52b62ff6d4..9596b3ea4f 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -676,7 +676,7 @@ int cmd_unpack_objects(int argc,\n \tgit_hash_final_oid(&oid, &tmp_ctx);\n \tif (strict) {\n \t\twrite_rest();\n-\t\tif (fsck_finish(&fsck_options))\n+\t\tif (fsck_finish(the_repository, &fsck_options))\n \t\t\tdie(_(\"fsck error in pack objects\"));\n \t}\n \tif (!hasheq(fill(the_hash_algo->rawsz), oid.hash,\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 6ecd468ef7..6fd534633d 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -1231,7 +1231,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \tif (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n \t\t     &fsck_options.gitmodules_found))\n \t\tdie(_(\"git fetch-pack: fetch failed.\"));\n-\tif (fsck_finish(&fsck_options))\n+\tif (fsck_finish(the_repository, &fsck_options))\n \t\tdie(\"fsck failed\");\n \n  all_done:\n@@ -1876,7 +1876,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,\n \tstring_list_clear(&packfile_uris, 0);\n \tstrvec_clear(&index_pack_args);\n \n-\tif (fsck_finish(&fsck_options))\n+\tif (fsck_finish(the_repository, &fsck_options))\n \t\tdie(\"fsck failed\");\n \n \tif (negotiator)\ndiff --git a/fsck.c b/fsck.c\nindex c6b6f533be..1f7d568acf 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -1331,7 +1331,8 @@ int fsck_refs_error_function(struct fsck_options *options UNUSED,\n \treturn ret;\n }\n \n-static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,\n+static int fsck_blobs(struct repository *repo,\n+\t\t      struct oidset *blobs_found, struct oidset *blobs_done,\n \t\t      enum fsck_msg_id msg_missing, enum fsck_msg_id msg_type,\n \t\t      struct fsck_options *options, const char *blob_type)\n {\n@@ -1348,9 +1349,9 @@ static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,\n \t\tif (oidset_contains(blobs_done, oid))\n \t\t\tcontinue;\n \n-\t\tbuf = odb_read_object(the_repository->objects, oid, &type, &size);\n+\t\tbuf = odb_read_object(repo->objects, oid, &type, &size);\n \t\tif (!buf) {\n-\t\t\tif (is_promisor_object(the_repository, oid))\n+\t\t\tif (is_promisor_object(repo, oid))\n \t\t\t\tcontinue;\n \t\t\tret |= report(options,\n \t\t\t\t      oid, OBJ_BLOB, msg_missing,\n@@ -1372,14 +1373,14 @@ static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,\n \treturn ret;\n }\n \n-int fsck_finish(struct fsck_options *options)\n+int fsck_finish(struct repository *repo, struct fsck_options *options)\n {\n \tint ret = 0;\n \n-\tret |= fsck_blobs(&options->gitmodules_found, &options->gitmodules_done,\n+\tret |= fsck_blobs(repo, &options->gitmodules_found, &options->gitmodules_done,\n \t\t\t  FSCK_MSG_GITMODULES_MISSING, FSCK_MSG_GITMODULES_BLOB,\n \t\t\t  options, \".gitmodules\");\n-\tret |= fsck_blobs(&options->gitattributes_found, &options->gitattributes_done,\n+\tret |= fsck_blobs(repo, &options->gitattributes_found, &options->gitattributes_done,\n \t\t\t  FSCK_MSG_GITATTRIBUTES_MISSING, FSCK_MSG_GITATTRIBUTES_BLOB,\n \t\t\t  options, \".gitattributes\");\n \ndiff --git a/fsck.h b/fsck.h\nindex 4bd54865fe..bfe9b3ffc9 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -255,7 +255,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n  * after completing all fsck_object() calls in order to resolve any remaining\n  * checks.\n  */\n-int fsck_finish(struct fsck_options *options);\n+int fsck_finish(struct repository *repo, struct fsck_options *options);\n \n /*\n  * Check whether there are any checks that have been queued up and that still\ndiff --git a/object-file.c b/object-file.c\nindex c62e5496e0..ecbcc501dd 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -1285,7 +1285,7 @@ static int index_mem(struct index_state *istate,\n \t\topts.error_func = hash_format_check_report;\n \t\tif (fsck_buffer(null_oid(istate->repo->hash_algo), type, buf, size, &opts))\n \t\t\tdie(_(\"refusing to create malformed object\"));\n-\t\tfsck_finish(&opts);\n+\t\tfsck_finish(the_repository, &opts);\n \t}\n \n \tif (write_object)\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539516","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-3-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 03/14] fsck: refactor interface to parse fsck options","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:08Z","receivedAt":"2026-03-20T11:47:51Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"The function `git_fsck_config()` is supposed to be used as a callback\nfunction for `repo_config()`. It expects the caller to provide a\nrepository as callback payload, which is easy to miss. Furthermore, in\nthe next commit we're about to refactor the function to not rely on\nglobal state anymore, which will require callers to pass a different\npayload.\n\nPrepare for this change by refactoring the interface to be fully\nself-contained. Like this, callers know exactly which parameters are\nexpected.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c  |  2 +-\n builtin/mktag.c |  3 ++-\n builtin/refs.c  |  2 +-\n fsck.c          | 11 +++++++++--\n fsck.h          |  9 ++++-----\n 5 files changed, 17 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 8f994010da..fd689a1e14 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -1023,7 +1023,7 @@ int cmd_fsck(int argc,\n \tif (name_objects)\n \t\tfsck_enable_object_names(&fsck_walk_options);\n \n-\trepo_config(the_repository, git_fsck_config, &fsck_obj_options);\n+\tfsck_options_parse_config(&fsck_obj_options, the_repository);\n \tprepare_repo_settings(the_repository);\n \n \tif (check_references)\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 7cf6e1230a..2cf843c195 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -98,7 +98,8 @@ int cmd_mktag(int argc,\n \tfsck_set_msg_type_from_ids(&fsck_options, FSCK_MSG_EXTRA_HEADER_ENTRY,\n \t\t\t\t   FSCK_WARN);\n \t/* config might set fsck.extraHeaderEntry=* again */\n-\trepo_config(the_repository, git_fsck_config, &fsck_options);\n+\tfsck_options_parse_config(&fsck_options, the_repository);\n+\n \tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n \t\t\t\t&tagged_oid, &tagged_type))\n \t\tdie(_(\"tag on stdin did not pass our strict fsck check\"));\ndiff --git a/builtin/refs.c b/builtin/refs.c\nindex 3064f888b2..c0a3b475e9 100644\n--- a/builtin/refs.c\n+++ b/builtin/refs.c\n@@ -97,7 +97,7 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,\n \tif (argc)\n \t\tusage(_(\"'git refs verify' takes no arguments\"));\n \n-\trepo_config(the_repository, git_fsck_config, &fsck_refs_options);\n+\tfsck_options_parse_config(&fsck_refs_options, the_repository);\n \tprepare_repo_settings(the_repository);\n \n \tworktrees = get_worktrees_without_reading_head();\ndiff --git a/fsck.c b/fsck.c\nindex 1f7d568acf..a7ebab9581 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -1404,8 +1404,9 @@ void fsck_options_clear(struct fsck_options *options)\n \tkh_clear_oid_map(options->object_names);\n }\n \n-int git_fsck_config(const char *var, const char *value,\n-\t\t    const struct config_context *ctx, void *cb)\n+static int fsck_options_parse_config_key(const char *var, const char *value,\n+\t\t\t\t\t const struct config_context *ctx,\n+\t\t\t\t\t void *cb)\n {\n \tstruct fsck_options *options = cb;\n \tconst char *msg_id;\n@@ -1435,6 +1436,12 @@ int git_fsck_config(const char *var, const char *value,\n \treturn git_default_config(var, value, ctx, cb);\n }\n \n+void fsck_options_parse_config(struct fsck_options *options,\n+\t\t\t       struct repository *repo)\n+{\n+\trepo_config(repo, fsck_options_parse_config_key, options);\n+}\n+\n /*\n  * Custom error callbacks that are used in more than one place.\n  */\ndiff --git a/fsck.h b/fsck.h\nindex bfe9b3ffc9..7d0d876103 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -304,12 +304,11 @@ void fsck_put_object_name(struct fsck_options *options,\n const char *fsck_describe_object(struct fsck_options *options,\n \t\t\t\t const struct object_id *oid);\n \n-struct key_value_info;\n /*\n- * repo_config() callback for use by fsck-y tools that want to support\n- * fsck.<msg> fsck.skipList etc.\n+ * Parse fsck options from the gitconfig. This covers settings like for example\n+ * fsck.<msg> fsck.skipList.\n  */\n-int git_fsck_config(const char *var, const char *value,\n-\t\t    const struct config_context *ctx, void *cb);\n+void fsck_options_parse_config(struct fsck_options *options,\n+\t\t\t       struct repository *repo);\n \n #endif\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539517","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-4-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 04/14] fsck: drop `the_repository` in `fsck_set_msg_types()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:09Z","receivedAt":"2026-03-20T11:47:54Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"We implicitly rely on `the_repository` in `fsck_set_msg_types()` because\nof our reliance on `the_hash_algo`. Fix this dependency on global state\nby requiring the callers to provide the hash function.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/index-pack.c     |  6 ++++--\n builtin/unpack-objects.c |  3 ++-\n fsck.c                   | 27 +++++++++++++++++++--------\n fsck.h                   |  3 ++-\n 4 files changed, 27 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 3d1f7dbd5f..c4117e5970 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1932,13 +1932,15 @@ int cmd_index_pack(int argc,\n \t\t\t} else if (skip_to_optional_arg(arg, \"--strict\", &arg)) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n-\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg,\n+\t\t\t\t\t\t   the_hash_algo);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\n \t\t\t} else if (skip_to_optional_arg(arg, \"--fsck-objects\", &arg)) {\n \t\t\t\tdo_fsck_object = 1;\n-\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg,\n+\t\t\t\t\t\t   the_hash_algo);\n \t\t\t} else if (!strcmp(arg, \"--verify\")) {\n \t\t\t\tverify = 1;\n \t\t\t} else if (!strcmp(arg, \"--verify-stat\")) {\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 9596b3ea4f..3e748a55d9 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -649,7 +649,8 @@ int cmd_unpack_objects(int argc,\n \t\t\t}\n \t\t\tif (skip_prefix(arg, \"--strict=\", &arg)) {\n \t\t\t\tstrict = 1;\n-\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg,\n+\t\t\t\t\t\t   the_hash_algo);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (skip_prefix(arg, \"--pack_header=\", &arg)) {\ndiff --git a/fsck.c b/fsck.c\nindex a7ebab9581..efd987dd2b 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -182,7 +182,8 @@ void fsck_set_msg_type(struct fsck_options *options,\n \tfree(to_free);\n }\n \n-void fsck_set_msg_types(struct fsck_options *options, const char *values)\n+void fsck_set_msg_types(struct fsck_options *options, const char *values,\n+\t\t\tconst struct git_hash_algo *algo)\n {\n \tchar *buf = xstrdup(values), *to_free = buf;\n \tint done = 0;\n@@ -207,7 +208,7 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)\n \t\t\tif (equal == len)\n \t\t\t\tdie(\"skiplist requires a path\");\n \t\t\toidset_parse_file(&options->skip_oids, buf + equal + 1,\n-\t\t\t\t\t  the_repository->hash_algo);\n+\t\t\t\t\t  algo);\n \t\t\tbuf += len + 1;\n \t\t\tcontinue;\n \t\t}\n@@ -1404,11 +1405,16 @@ void fsck_options_clear(struct fsck_options *options)\n \tkh_clear_oid_map(options->object_names);\n }\n \n+struct fsck_options_parse_config_key_data {\n+\tstruct repository *repo;\n+\tstruct fsck_options *options;\n+};\n+\n static int fsck_options_parse_config_key(const char *var, const char *value,\n \t\t\t\t\t const struct config_context *ctx,\n-\t\t\t\t\t void *cb)\n+\t\t\t\t\t void *cb_data)\n {\n-\tstruct fsck_options *options = cb;\n+\tstruct fsck_options_parse_config_key_data *data = cb_data;\n \tconst char *msg_id;\n \n \tif (strcmp(var, \"fsck.skiplist\") == 0) {\n@@ -1420,7 +1426,8 @@ static int fsck_options_parse_config_key(const char *var, const char *value,\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n \t\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n \t\t\tfree(path);\n-\t\t\tfsck_set_msg_types(options, sb.buf);\n+\t\t\tfsck_set_msg_types(data->options, sb.buf,\n+\t\t\t\t\t   data->repo->hash_algo);\n \t\t\tstrbuf_release(&sb);\n \t\t}\n \t\treturn 0;\n@@ -1429,17 +1436,21 @@ static int fsck_options_parse_config_key(const char *var, const char *value,\n \tif (skip_prefix(var, \"fsck.\", &msg_id)) {\n \t\tif (!value)\n \t\t\treturn config_error_nonbool(var);\n-\t\tfsck_set_msg_type(options, msg_id, value);\n+\t\tfsck_set_msg_type(data->options, msg_id, value);\n \t\treturn 0;\n \t}\n \n-\treturn git_default_config(var, value, ctx, cb);\n+\treturn git_default_config(var, value, ctx, cb_data);\n }\n \n void fsck_options_parse_config(struct fsck_options *options,\n \t\t\t       struct repository *repo)\n {\n-\trepo_config(repo, fsck_options_parse_config_key, options);\n+\tstruct fsck_options_parse_config_key_data data = {\n+\t\t.repo = repo,\n+\t\t.options = options,\n+\t};\n+\trepo_config(repo, fsck_options_parse_config_key, &data);\n }\n \n /*\ndiff --git a/fsck.h b/fsck.h\nindex 7d0d876103..3ca1683f74 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -118,7 +118,8 @@ void fsck_set_msg_type_from_ids(struct fsck_options *options,\n \t\t\t\tenum fsck_msg_type msg_type);\n void fsck_set_msg_type(struct fsck_options *options,\n \t\t       const char *msg_id, const char *msg_type);\n-void fsck_set_msg_types(struct fsck_options *options, const char *values);\n+void fsck_set_msg_types(struct fsck_options *options, const char *values,\n+\t\t\tconst struct git_hash_algo *algo);\n int is_valid_msg_type(const char *msg_id, const char *msg_type);\n \n /*\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539518","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-5-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 05/14] fsck: stop relying on global state via `parse_oid_hex()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:10Z","receivedAt":"2026-03-20T11:47:58Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"The function `parse_oid_hex()` implicitly relies on global state, as it\nuses the hash algorithm of `the_repository` to parse the input string.\nRefactor \"fsck.c\" to use `parse_oid_hex_algop()` instead and make the\nrepository available via parameters as required.\n\nThis was the last dependency on `the_repository` in \"fsck.c\", so we can\nnow drop `USE_THE_REPOSITORY_VARIABLE`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c           |  2 +-\n builtin/index-pack.c     |  5 +++--\n builtin/mktag.c          |  4 ++--\n builtin/unpack-objects.c |  2 +-\n fsck.c                   | 33 ++++++++++++++++++---------------\n fsck.h                   | 11 +++++++----\n object-file.c            |  3 ++-\n 7 files changed, 34 insertions(+), 26 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex fd689a1e14..ff9951c10c 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -415,7 +415,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)\n \n \tif (fsck_walk(the_repository, obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, _(\"broken links\"));\n-\terr = fsck_object(obj, buffer, size, &fsck_obj_options);\n+\terr = fsck_object(the_repository, obj, buffer, size, &fsck_obj_options);\n \tif (err)\n \t\tgoto out;\n \ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex c4117e5970..031e4697d9 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -933,7 +933,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\telse\n \t\t\t\tdie(_(\"invalid blob object %s\"), oid_to_hex(oid));\n \t\t\tif (do_fsck_object &&\n-\t\t\t    fsck_object(&blob->object, (void *)data, size, &fsck_options))\n+\t\t\t    fsck_object(the_repository, &blob->object, (void *)data,\n+\t\t\t\t\tsize, &fsck_options))\n \t\t\t\tdie(_(\"fsck error in packed object\"));\n \t\t} else {\n \t\t\tstruct object *obj;\n@@ -952,7 +953,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\tif (!obj)\n \t\t\t\tdie(_(\"invalid %s\"), type_name(type));\n \t\t\tif (do_fsck_object &&\n-\t\t\t    fsck_object(obj, buf, size, &fsck_options))\n+\t\t\t    fsck_object(the_repository, obj, buf, size, &fsck_options))\n \t\t\t\tdie(_(\"fsck error in packed object\"));\n \t\t\tif (strict && fsck_walk(the_repository, obj, NULL, &fsck_options))\n \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), oid_to_hex(&obj->oid));\ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 2cf843c195..1925f04321 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -100,8 +100,8 @@ int cmd_mktag(int argc,\n \t/* config might set fsck.extraHeaderEntry=* again */\n \tfsck_options_parse_config(&fsck_options, the_repository);\n \n-\tif (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,\n-\t\t\t\t&tagged_oid, &tagged_type))\n+\tif (fsck_tag_standalone(the_repository, NULL, buf.buf, buf.len,\n+\t\t\t\t&fsck_options, &tagged_oid, &tagged_type))\n \t\tdie(_(\"tag on stdin did not pass our strict fsck check\"));\n \n \tif (verify_object_in_tag(&tagged_oid, &tagged_type) < 0)\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 3e748a55d9..dadc2ba65f 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -241,7 +241,7 @@ static int check_object(struct object *obj, enum object_type type,\n \tobj_buf = lookup_object_buffer(obj);\n \tif (!obj_buf)\n \t\tdie(\"Whoops! Cannot find object '%s'\", oid_to_hex(&obj->oid));\n-\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))\n+\tif (fsck_object(the_repository, obj, obj_buf->buffer, obj_buf->size, &fsck_options))\n \t\tdie(\"fsck error in packed object\");\n \tfsck_options.walk = check_object;\n \tif (fsck_walk(the_repository, obj, NULL, &fsck_options))\ndiff --git a/fsck.c b/fsck.c\nindex efd987dd2b..edf7a0618f 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -1,5 +1,3 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n-\n #include \"git-compat-util.h\"\n #include \"date.h\"\n #include \"dir.h\"\n@@ -962,7 +960,8 @@ static int fsck_ident(const char **ident, const char *ident_end,\n \treturn 0;\n }\n \n-static int fsck_commit(const struct object_id *oid,\n+static int fsck_commit(struct repository *repo,\n+\t\t       const struct object_id *oid,\n \t\t       const char *buffer, unsigned long size,\n \t\t       struct fsck_options *options)\n {\n@@ -983,14 +982,14 @@ static int fsck_commit(const struct object_id *oid,\n \n \tif (buffer >= buffer_end || !skip_prefix(buffer, \"tree \", &buffer))\n \t\treturn report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n-\tif (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\\n') {\n+\tif (parse_oid_hex_algop(buffer, &tree_oid, &p, repo->hash_algo) || *p != '\\n') {\n \t\terr = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n \t\tif (err)\n \t\t\treturn err;\n \t}\n \tbuffer = p + 1;\n \twhile (buffer < buffer_end && skip_prefix(buffer, \"parent \", &buffer)) {\n-\t\tif (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\\n') {\n+\t\tif (parse_oid_hex_algop(buffer, &parent_oid, &p, repo->hash_algo) || *p != '\\n') {\n \t\t\terr = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n \t\t\tif (err)\n \t\t\t\treturn err;\n@@ -1024,16 +1023,18 @@ static int fsck_commit(const struct object_id *oid,\n \treturn 0;\n }\n \n-static int fsck_tag(const struct object_id *oid, const char *buffer,\n+static int fsck_tag(struct repository *repo,\n+\t\t    const struct object_id *oid, const char *buffer,\n \t\t    unsigned long size, struct fsck_options *options)\n {\n \tstruct object_id tagged_oid;\n \tint tagged_type;\n-\treturn fsck_tag_standalone(oid, buffer, size, options, &tagged_oid,\n-\t\t\t\t   &tagged_type);\n+\treturn fsck_tag_standalone(repo, oid, buffer, size, options,\n+\t\t\t\t   &tagged_oid, &tagged_type);\n }\n \n-int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n+int fsck_tag_standalone(struct repository *repo,\n+\t\t\tconst struct object_id *oid, const char *buffer,\n \t\t\tunsigned long size, struct fsck_options *options,\n \t\t\tstruct object_id *tagged_oid,\n \t\t\tint *tagged_type)\n@@ -1057,7 +1058,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n-\tif (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\\n') {\n+\tif (parse_oid_hex_algop(buffer, tagged_oid, &p, repo->hash_algo) || *p != '\\n') {\n \t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tif (ret)\n \t\t\tgoto done;\n@@ -1266,16 +1267,18 @@ static int fsck_blob(const struct object_id *oid, const char *buf,\n \treturn ret;\n }\n \n-int fsck_object(struct object *obj, void *data, unsigned long size,\n+int fsck_object(struct repository *repo,\n+\t\tstruct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn report(options, NULL, OBJ_NONE, FSCK_MSG_BAD_OBJECT_SHA1, \"no valid object to fsck\");\n \n-\treturn fsck_buffer(&obj->oid, obj->type, data, size, options);\n+\treturn fsck_buffer(repo, &obj->oid, obj->type, data, size, options);\n }\n \n-int fsck_buffer(const struct object_id *oid, enum object_type type,\n+int fsck_buffer(struct repository *repo,\n+\t\tconst struct object_id *oid, enum object_type type,\n \t\tconst void *data, unsigned long size,\n \t\tstruct fsck_options *options)\n {\n@@ -1284,9 +1287,9 @@ int fsck_buffer(const struct object_id *oid, enum object_type type,\n \tif (type == OBJ_TREE)\n \t\treturn fsck_tree(oid, data, size, options);\n \tif (type == OBJ_COMMIT)\n-\t\treturn fsck_commit(oid, data, size, options);\n+\t\treturn fsck_commit(repo, oid, data, size, options);\n \tif (type == OBJ_TAG)\n-\t\treturn fsck_tag(oid, data, size, options);\n+\t\treturn fsck_tag(repo, oid, data, size, options);\n \n \treturn report(options, oid, type,\n \t\t      FSCK_MSG_UNKNOWN_TYPE,\ndiff --git a/fsck.h b/fsck.h\nindex 3ca1683f74..62b7c3c93a 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -231,14 +231,16 @@ int fsck_walk(struct repository *repo,\n  * Blob objects my pass a NULL data pointer, which indicates they are too large\n  * to fit in memory. All other types must pass a real buffer.\n  */\n-int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tstruct fsck_options *options);\n+int fsck_object(struct repository *repo,\n+\t\tstruct object *obj, void *data, unsigned long size,\n+\t\tstruct fsck_options *options);\n \n /*\n  * Same as fsck_object(), but for when the caller doesn't have an object\n  * struct.\n  */\n-int fsck_buffer(const struct object_id *oid, enum object_type,\n+int fsck_buffer(struct repository *repo,\n+\t\tconst struct object_id *oid, enum object_type,\n \t\tconst void *data, unsigned long size,\n \t\tstruct fsck_options *options);\n \n@@ -246,7 +248,8 @@ int fsck_buffer(const struct object_id *oid, enum object_type,\n  * fsck a tag, and pass info about it back to the caller. This is\n  * exposed fsck_object() internals for git-mktag(1).\n  */\n-int fsck_tag_standalone(const struct object_id *oid, const char *buffer,\n+int fsck_tag_standalone(struct repository *repo,\n+\t\t\tconst struct object_id *oid, const char *buffer,\n \t\t\tunsigned long size, struct fsck_options *options,\n \t\t\tstruct object_id *tagged_oid,\n \t\t\tint *tag_type);\ndiff --git a/object-file.c b/object-file.c\nindex ecbcc501dd..f58acaa939 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -1283,7 +1283,8 @@ static int index_mem(struct index_state *istate,\n \n \t\topts.strict = 1;\n \t\topts.error_func = hash_format_check_report;\n-\t\tif (fsck_buffer(null_oid(istate->repo->hash_algo), type, buf, size, &opts))\n+\t\tif (fsck_buffer(the_repository, null_oid(istate->repo->hash_algo),\n+\t\t\t\ttype, buf, size, &opts))\n \t\t\tdie(_(\"refusing to create malformed object\"));\n \t\tfsck_finish(the_repository, &opts);\n \t}\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539519","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-6-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 06/14] builtin/fsck: fix trivial dependence on `the_repository`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:11Z","receivedAt":"2026-03-20T11:48:00Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"We have a bunch of sites in \"builtin/fsck.c\" that depend on\n`the_repository` even though we already have a repository available, or\nin cases where we can trivially make it available.\n\nRefactor such sites to use the context-provided repository instead.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c | 116 +++++++++++++++++++++++++++++----------------------------\n 1 file changed, 59 insertions(+), 57 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex ff9951c10c..8a52ad02ae 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -184,9 +184,9 @@ static void mark_object_reachable(struct object *obj)\n \tmark_object(obj, OBJ_ANY, NULL, NULL);\n }\n \n-static int traverse_one_object(struct object *obj)\n+static int traverse_one_object(struct repository *repo, struct object *obj)\n {\n-\tint result = fsck_walk(the_repository, obj, obj, &fsck_walk_options);\n+\tint result = fsck_walk(repo, obj, obj, &fsck_walk_options);\n \n \tif (obj->type == OBJ_TREE) {\n \t\tstruct tree *tree = (struct tree *)obj;\n@@ -195,16 +195,16 @@ static int traverse_one_object(struct object *obj)\n \treturn result;\n }\n \n-static int traverse_reachable(void)\n+static int traverse_reachable(struct repository *repo)\n {\n \tstruct progress *progress = NULL;\n \tunsigned int nr = 0;\n \tint result = 0;\n \tif (show_progress)\n-\t\tprogress = start_delayed_progress(the_repository,\n+\t\tprogress = start_delayed_progress(repo,\n \t\t\t\t\t\t  _(\"Checking connectivity\"), 0);\n \twhile (pending.nr) {\n-\t\tresult |= traverse_one_object(object_array_pop(&pending));\n+\t\tresult |= traverse_one_object(repo, object_array_pop(&pending));\n \t\tdisplay_progress(progress, ++nr);\n \t}\n \tstop_progress(&progress);\n@@ -254,7 +254,7 @@ static int mark_unreachable_referents(const struct object_id *oid,\n /*\n  * Check a single reachable object\n  */\n-static void check_reachable_object(struct object *obj)\n+static void check_reachable_object(struct repository *repo, struct object *obj)\n {\n \t/*\n \t * We obviously want the object to be parsed,\n@@ -262,9 +262,9 @@ static void check_reachable_object(struct object *obj)\n \t * do a full fsck\n \t */\n \tif (!(obj->flags & HAS_OBJ)) {\n-\t\tif (is_promisor_object(the_repository, &obj->oid))\n+\t\tif (is_promisor_object(repo, &obj->oid))\n \t\t\treturn;\n-\t\tif (has_object_pack(the_repository, &obj->oid))\n+\t\tif (has_object_pack(repo, &obj->oid))\n \t\t\treturn; /* it is in pack - forget about it */\n \t\tprintf_ln(_(\"missing %s %s\"),\n \t\t\t  printable_type(&obj->oid, obj->type),\n@@ -277,7 +277,7 @@ static void check_reachable_object(struct object *obj)\n /*\n  * Check a single unreachable object\n  */\n-static void check_unreachable_object(struct object *obj)\n+static void check_unreachable_object(struct repository *repo, struct object *obj)\n {\n \t/*\n \t * Missing unreachable object? Ignore it. It's not like\n@@ -317,19 +317,19 @@ static void check_unreachable_object(struct object *obj)\n \t\t\t\t  printable_type(&obj->oid, obj->type),\n \t\t\t\t  describe_object(&obj->oid));\n \t\tif (write_lost_and_found) {\n-\t\t\tchar *filename = repo_git_path(the_repository, \"lost-found/%s/%s\",\n+\t\t\tchar *filename = repo_git_path(repo, \"lost-found/%s/%s\",\n \t\t\t\tobj->type == OBJ_COMMIT ? \"commit\" : \"other\",\n \t\t\t\tdescribe_object(&obj->oid));\n \t\t\tFILE *f;\n \n-\t\t\tif (safe_create_leading_directories_const(the_repository, filename)) {\n+\t\t\tif (safe_create_leading_directories_const(repo, filename)) {\n \t\t\t\terror(_(\"could not create lost-found\"));\n \t\t\t\tfree(filename);\n \t\t\t\treturn;\n \t\t\t}\n \t\t\tf = xfopen(filename, \"w\");\n \t\t\tif (obj->type == OBJ_BLOB) {\n-\t\t\t\tif (odb_stream_blob_to_fd(the_repository->objects, fileno(f),\n+\t\t\t\tif (odb_stream_blob_to_fd(repo->objects, fileno(f),\n \t\t\t\t\t\t\t  &obj->oid, NULL, 1))\n \t\t\t\t\tdie_errno(_(\"could not write '%s'\"), filename);\n \t\t\t} else\n@@ -349,23 +349,23 @@ static void check_unreachable_object(struct object *obj)\n \t */\n }\n \n-static void check_object(struct object *obj)\n+static void check_object(struct repository *repo, struct object *obj)\n {\n \tif (verbose)\n \t\tfprintf_ln(stderr, _(\"Checking %s\"), describe_object(&obj->oid));\n \n \tif (obj->flags & REACHABLE)\n-\t\tcheck_reachable_object(obj);\n+\t\tcheck_reachable_object(repo, obj);\n \telse\n-\t\tcheck_unreachable_object(obj);\n+\t\tcheck_unreachable_object(repo, obj);\n }\n \n-static void check_connectivity(void)\n+static void check_connectivity(struct repository *repo)\n {\n \tint i, max;\n \n \t/* Traverse the pending reachable objects */\n-\ttraverse_reachable();\n+\ttraverse_reachable(repo);\n \n \t/*\n \t * With --connectivity-only, we won't have actually opened and marked\n@@ -383,20 +383,20 @@ static void check_connectivity(void)\n \t\t * and ignore any that weren't present in our earlier\n \t\t * traversal.\n \t\t */\n-\t\todb_for_each_object(the_repository->objects, NULL,\n+\t\todb_for_each_object(repo->objects, NULL,\n \t\t\t\t    mark_unreachable_referents, NULL, 0);\n \t}\n \n \t/* Look up all the requirements, warn about missing objects.. */\n-\tmax = get_max_object_index(the_repository);\n+\tmax = get_max_object_index(repo);\n \tif (verbose)\n \t\tfprintf_ln(stderr, _(\"Checking connectivity (%d objects)\"), max);\n \n \tfor (i = 0; i < max; i++) {\n-\t\tstruct object *obj = get_indexed_object(the_repository, i);\n+\t\tstruct object *obj = get_indexed_object(repo, i);\n \n \t\tif (obj)\n-\t\t\tcheck_object(obj);\n+\t\t\tcheck_object(repo, obj);\n \t}\n }\n \n@@ -580,7 +580,8 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)\n \treturn 0;\n }\n \n-static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)\n+static void snapshot_refs(struct repository *repo, struct snapshot *snap,\n+\t\t\t  int argc, const char **argv)\n {\n \tstruct refs_for_each_ref_options opts = {\n \t\t.flags = REFS_FOR_EACH_INCLUDE_BROKEN,\n@@ -592,7 +593,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)\n \tfor (int i = 0; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n \t\tstruct object_id oid;\n-\t\tif (!repo_get_oid(the_repository, arg, &oid)) {\n+\t\tif (!repo_get_oid(repo, arg, &oid)) {\n \t\t\tstruct reference ref = {\n \t\t\t\t.name = arg,\n \t\t\t\t.oid = &oid,\n@@ -610,7 +611,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)\n \t\treturn;\n \t}\n \n-\trefs_for_each_ref_ext(get_main_ref_store(the_repository),\n+\trefs_for_each_ref_ext(get_main_ref_store(repo),\n \t\t\t      snapshot_ref, snap, &opts);\n \n \tworktrees = get_worktrees();\n@@ -620,7 +621,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)\n \n \t\tstrbuf_worktree_ref(wt, &refname, \"HEAD\");\n \n-\t\thead_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),\n+\t\thead_points_at = refs_resolve_ref_unsafe(get_main_ref_store(repo),\n \t\t\t\t\t\t\t refname.buf, 0, &head_oid, NULL);\n \n \t\tif (head_points_at && !is_null_oid(&head_oid)) {\n@@ -780,7 +781,7 @@ static void fsck_source(struct odb_source *source)\n \t\tfprintf_ln(stderr, _(\"Checking object directory\"));\n \n \tif (show_progress)\n-\t\tprogress = start_progress(the_repository,\n+\t\tprogress = start_progress(source->odb->repo,\n \t\t\t\t\t  _(\"Checking object directories\"), 256);\n \n \tfor_each_loose_file_in_source(source, fsck_loose,\n@@ -789,7 +790,8 @@ static void fsck_source(struct odb_source *source)\n \tstop_progress(&progress);\n }\n \n-static int fsck_cache_tree(struct cache_tree *it, const char *index_path)\n+static int fsck_cache_tree(struct repository *repo, struct cache_tree *it,\n+\t\t\t   const char *index_path)\n {\n \tint i;\n \tint err = 0;\n@@ -798,7 +800,7 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path)\n \t\tfprintf_ln(stderr, _(\"Checking cache tree of %s\"), index_path);\n \n \tif (0 <= it->entry_count) {\n-\t\tstruct object *obj = parse_object(the_repository, &it->oid);\n+\t\tstruct object *obj = parse_object(repo, &it->oid);\n \t\tif (!obj) {\n \t\t\terror(_(\"%s: invalid sha1 pointer in cache-tree of %s\"),\n \t\t\t      oid_to_hex(&it->oid), index_path);\n@@ -812,7 +814,7 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path)\n \t\t\terr |= objerror(obj, _(\"non-tree in cache-tree\"));\n \t}\n \tfor (i = 0; i < it->subtree_nr; i++)\n-\t\terr |= fsck_cache_tree(it->down[i]->cache_tree, index_path);\n+\t\terr |= fsck_cache_tree(repo, it->down[i]->cache_tree, index_path);\n \treturn err;\n }\n \n@@ -838,7 +840,7 @@ static int fsck_resolve_undo(struct index_state *istate,\n \t\t\tif (!ru->mode[i] || !S_ISREG(ru->mode[i]))\n \t\t\t\tcontinue;\n \n-\t\t\tobj = parse_object(the_repository, &ru->oid[i]);\n+\t\t\tobj = parse_object(istate->repo, &ru->oid[i]);\n \t\t\tif (!obj) {\n \t\t\t\terror(_(\"%s: invalid sha1 pointer in resolve-undo of %s\"),\n \t\t\t\t      oid_to_hex(&ru->oid[i]),\n@@ -870,7 +872,7 @@ static void fsck_index(struct index_state *istate, const char *index_path,\n \t\tmode = istate->cache[i]->ce_mode;\n \t\tif (S_ISGITLINK(mode))\n \t\t\tcontinue;\n-\t\tblob = lookup_blob(the_repository,\n+\t\tblob = lookup_blob(istate->repo,\n \t\t\t\t   &istate->cache[i]->oid);\n \t\tif (!blob)\n \t\t\tcontinue;\n@@ -883,7 +885,7 @@ static void fsck_index(struct index_state *istate, const char *index_path,\n \t\tmark_object_reachable(obj);\n \t}\n \tif (istate->cache_tree)\n-\t\tfsck_cache_tree(istate->cache_tree, index_path);\n+\t\tfsck_cache_tree(istate->repo, istate->cache_tree, index_path);\n \tfsck_resolve_undo(istate, index_path);\n }\n \n@@ -906,7 +908,7 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress)\n \tif (show_progress) {\n \t\trepo_for_each_pack(r, p)\n \t\t\tpack_count++;\n-\t\tprogress = start_delayed_progress(the_repository,\n+\t\tprogress = start_delayed_progress(r,\n \t\t\t\t\t\t  \"Verifying reverse pack-indexes\", pack_count);\n \t\tpack_count = 0;\n \t}\n@@ -986,7 +988,7 @@ static struct option fsck_opts[] = {\n int cmd_fsck(int argc,\n \t     const char **argv,\n \t     const char *prefix,\n-\t     struct repository *repo UNUSED)\n+\t     struct repository *repo)\n {\n \tstruct odb_source *source;\n \tstruct snapshot snap = {\n@@ -1023,11 +1025,11 @@ int cmd_fsck(int argc,\n \tif (name_objects)\n \t\tfsck_enable_object_names(&fsck_walk_options);\n \n-\tfsck_options_parse_config(&fsck_obj_options, the_repository);\n-\tprepare_repo_settings(the_repository);\n+\tfsck_options_parse_config(&fsck_obj_options, repo);\n+\tprepare_repo_settings(repo);\n \n \tif (check_references)\n-\t\tfsck_refs(the_repository);\n+\t\tfsck_refs(repo);\n \n \t/*\n \t * Take a snapshot of the refs before walking objects to avoid looking\n@@ -1035,17 +1037,17 @@ int cmd_fsck(int argc,\n \t * objects. We can still walk over new objects that are added during the\n \t * execution of fsck but won't miss any objects that were reachable.\n \t */\n-\tsnapshot_refs(&snap, argc, argv);\n+\tsnapshot_refs(repo, &snap, argc, argv);\n \n \t/* Ensure we get a \"fresh\" view of the odb */\n-\todb_reprepare(the_repository->objects);\n+\todb_reprepare(repo->objects);\n \n \tif (connectivity_only) {\n-\t\todb_for_each_object(the_repository->objects, NULL,\n+\t\todb_for_each_object(repo->objects, NULL,\n \t\t\t\t    mark_object_for_connectivity, NULL, 0);\n \t} else {\n-\t\todb_prepare_alternates(the_repository->objects);\n-\t\tfor (source = the_repository->objects->sources; source; source = source->next)\n+\t\todb_prepare_alternates(repo->objects);\n+\t\tfor (source = repo->objects->sources; source; source = source->next)\n \t\t\tfsck_source(source);\n \n \t\tif (check_full) {\n@@ -1054,19 +1056,19 @@ int cmd_fsck(int argc,\n \t\t\tstruct progress *progress = NULL;\n \n \t\t\tif (show_progress) {\n-\t\t\t\trepo_for_each_pack(the_repository, p) {\n+\t\t\t\trepo_for_each_pack(repo, p) {\n \t\t\t\t\tif (open_pack_index(p))\n \t\t\t\t\t\tcontinue;\n \t\t\t\t\ttotal += p->num_objects;\n \t\t\t\t}\n \n-\t\t\t\tprogress = start_progress(the_repository,\n+\t\t\t\tprogress = start_progress(repo,\n \t\t\t\t\t\t\t  _(\"Checking objects\"), total);\n \t\t\t}\n \n-\t\t\trepo_for_each_pack(the_repository, p) {\n+\t\t\trepo_for_each_pack(repo, p) {\n \t\t\t\t/* verify gives error messages itself */\n-\t\t\t\tif (verify_pack(the_repository,\n+\t\t\t\tif (verify_pack(repo,\n \t\t\t\t\t\tp, fsck_obj_buffer,\n \t\t\t\t\t\tprogress, count))\n \t\t\t\t\terrors_found |= ERROR_PACK;\n@@ -1075,7 +1077,7 @@ int cmd_fsck(int argc,\n \t\t\tstop_progress(&progress);\n \t\t}\n \n-\t\tif (fsck_finish(the_repository, &fsck_obj_options))\n+\t\tif (fsck_finish(repo, &fsck_obj_options))\n \t\t\terrors_found |= ERROR_OBJECT;\n \t}\n \n@@ -1100,7 +1102,7 @@ int cmd_fsck(int argc,\n \t\tfor (p = worktrees; *p; p++) {\n \t\t\tstruct worktree *wt = *p;\n \t\t\tstruct index_state istate =\n-\t\t\t\tINDEX_STATE_INIT(the_repository);\n+\t\t\t\tINDEX_STATE_INIT(repo);\n \t\t\tchar *path, *wt_gitdir;\n \n \t\t\t/*\n@@ -1121,17 +1123,17 @@ int cmd_fsck(int argc,\n \t\tfree_worktrees(worktrees);\n \t}\n \n-\terrors_found |= check_pack_rev_indexes(the_repository, show_progress);\n-\tif (verify_bitmap_files(the_repository))\n+\terrors_found |= check_pack_rev_indexes(repo, show_progress);\n+\tif (verify_bitmap_files(repo))\n \t\terrors_found |= ERROR_BITMAP;\n \n-\tcheck_connectivity();\n+\tcheck_connectivity(repo);\n \n-\tif (the_repository->settings.core_commit_graph) {\n+\tif (repo->settings.core_commit_graph) {\n \t\tstruct child_process commit_graph_verify = CHILD_PROCESS_INIT;\n \n-\t\todb_prepare_alternates(the_repository->objects);\n-\t\tfor (source = the_repository->objects->sources; source; source = source->next) {\n+\t\todb_prepare_alternates(repo->objects);\n+\t\tfor (source = repo->objects->sources; source; source = source->next) {\n \t\t\tchild_process_init(&commit_graph_verify);\n \t\t\tcommit_graph_verify.git_cmd = 1;\n \t\t\tstrvec_pushl(&commit_graph_verify.args, \"commit-graph\",\n@@ -1145,11 +1147,11 @@ int cmd_fsck(int argc,\n \t\t}\n \t}\n \n-\tif (the_repository->settings.core_multi_pack_index) {\n+\tif (repo->settings.core_multi_pack_index) {\n \t\tstruct child_process midx_verify = CHILD_PROCESS_INIT;\n \n-\t\todb_prepare_alternates(the_repository->objects);\n-\t\tfor (source = the_repository->objects->sources; source; source = source->next) {\n+\t\todb_prepare_alternates(repo->objects);\n+\t\tfor (source = repo->objects->sources; source; source = source->next) {\n \t\t\tchild_process_init(&midx_verify);\n \t\t\tmidx_verify.git_cmd = 1;\n \t\t\tstrvec_pushl(&midx_verify.args, \"multi-pack-index\",\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539520","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-7-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 07/14] builtin/fsck: stop using `the_repository` when snapshotting refs","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:12Z","receivedAt":"2026-03-20T11:48:03Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"We depedn on `the_repository` when snapshotting refs. Refactor this to\nuse a context-provided repository instead that is injected via the\n`struct snapshot_ref_data`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c | 22 ++++++++++++++++------\n 1 file changed, 16 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 8a52ad02ae..00476bb921 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -532,14 +532,20 @@ struct snapshot {\n \t/* TODO: Consider also snapshotting the index of each worktree. */\n };\n \n+struct snapshot_ref_data {\n+\tstruct repository *repo;\n+\tstruct snapshot *snap;\n+};\n+\n static int snapshot_ref(const struct reference *ref, void *cb_data)\n {\n-\tstruct snapshot *snap = cb_data;\n+\tstruct snapshot_ref_data *data = cb_data;\n+\tstruct snapshot *snap = data->snap;\n \tstruct object *obj;\n \n-\tobj = parse_object(the_repository, ref->oid);\n+\tobj = parse_object(data->repo, ref->oid);\n \tif (!obj) {\n-\t\tif (is_promisor_object(the_repository, ref->oid)) {\n+\t\tif (is_promisor_object(data->repo, ref->oid)) {\n \t\t\t/*\n \t\t\t * Increment default_refs anyway, because this is a\n \t\t\t * valid ref.\n@@ -586,6 +592,10 @@ static void snapshot_refs(struct repository *repo, struct snapshot *snap,\n \tstruct refs_for_each_ref_options opts = {\n \t\t.flags = REFS_FOR_EACH_INCLUDE_BROKEN,\n \t};\n+\tstruct snapshot_ref_data data = {\n+\t\t.repo = repo,\n+\t\t.snap = snap,\n+\t};\n \tstruct worktree **worktrees, **p;\n \tconst char *head_points_at;\n \tstruct object_id head_oid;\n@@ -599,7 +609,7 @@ static void snapshot_refs(struct repository *repo, struct snapshot *snap,\n \t\t\t\t.oid = &oid,\n \t\t\t};\n \n-\t\t\tsnapshot_ref(&ref, snap);\n+\t\t\tsnapshot_ref(&ref, &data);\n \t\t\tcontinue;\n \t\t}\n \t\terror(_(\"invalid parameter: expected sha1, got '%s'\"), arg);\n@@ -612,7 +622,7 @@ static void snapshot_refs(struct repository *repo, struct snapshot *snap,\n \t}\n \n \trefs_for_each_ref_ext(get_main_ref_store(repo),\n-\t\t\t      snapshot_ref, snap, &opts);\n+\t\t\t      snapshot_ref, &data, &opts);\n \n \tworktrees = get_worktrees();\n \tfor (p = worktrees; *p; p++) {\n@@ -630,7 +640,7 @@ static void snapshot_refs(struct repository *repo, struct snapshot *snap,\n \t\t\t\t.oid = &head_oid,\n \t\t\t};\n \n-\t\t\tsnapshot_ref(&ref, snap);\n+\t\t\tsnapshot_ref(&ref, &data);\n \t\t}\n \t\tstrbuf_release(&refname);\n \n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539521","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-8-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 08/14] builtin/fsck: stop using `the_repository` when checking refs","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:13Z","receivedAt":"2026-03-20T11:48:06Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"We implicitly rely on `the_repository` when checking refs. Refactor this\nto instead inject the repository via the callback payload.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c | 11 ++++++-----\n 1 file changed, 6 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 00476bb921..63cd58e58b 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -573,11 +573,12 @@ static int snapshot_ref(const struct reference *ref, void *cb_data)\n \treturn 0;\n }\n \n-static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)\n+static int fsck_handle_ref(const struct reference *ref, void *cb_data)\n {\n+\tstruct repository *repo = cb_data;\n \tstruct object *obj;\n \n-\tobj = parse_object(the_repository, ref->oid);\n+\tobj = parse_object(repo, ref->oid);\n \tobj->flags |= USED;\n \tfsck_put_object_name(&fsck_walk_options,\n \t\t\t     ref->oid, \"%s\", ref->name);\n@@ -664,7 +665,7 @@ static void free_snapshot_refs(struct snapshot *snap)\n \tfree(snap->ref);\n }\n \n-static void process_refs(struct snapshot *snap)\n+static void process_refs(struct repository *repo, struct snapshot *snap)\n {\n \tstruct worktree **worktrees, **p;\n \n@@ -673,7 +674,7 @@ static void process_refs(struct snapshot *snap)\n \t\t\t.name = snap->ref[i].refname,\n \t\t\t.oid = &snap->ref[i].oid,\n \t\t};\n-\t\tfsck_handle_ref(&ref, NULL);\n+\t\tfsck_handle_ref(&ref, repo);\n \t}\n \n \tif (include_reflogs) {\n@@ -1092,7 +1093,7 @@ int cmd_fsck(int argc,\n \t}\n \n \t/* Process the snapshotted refs and the reflogs. */\n-\tprocess_refs(&snap);\n+\tprocess_refs(repo, &snap);\n \n \t/* If not given any explicit objects, process index files too. */\n \tif (!argc)\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539522","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-9-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 09/14] builtin/fsck: stop using `the_repository` when checking reflogs","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:14Z","receivedAt":"2026-03-20T11:48:09Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"We implicitly rely on `the_repository` when checking reflogs. Refactor\nthis to instead inject the repository via the callback payload.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c | 24 ++++++++++++++----------\n 1 file changed, 14 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 63cd58e58b..ea441b072e 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -467,13 +467,14 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,\n \n static int default_refs;\n \n-static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,\n-\ttimestamp_t timestamp)\n+static void fsck_handle_reflog_oid(struct repository *repo,\n+\t\t\t\t   const char *refname, struct object_id *oid,\n+\t\t\t\t   timestamp_t timestamp)\n {\n \tstruct object *obj;\n \n \tif (!is_null_oid(oid)) {\n-\t\tobj = lookup_object(the_repository, oid);\n+\t\tobj = lookup_object(repo, oid);\n \t\tif (obj && (obj->flags & HAS_OBJ)) {\n \t\t\tif (timestamp)\n \t\t\t\tfsck_put_object_name(&fsck_walk_options, oid,\n@@ -481,7 +482,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,\n \t\t\t\t\t\t     refname, timestamp);\n \t\t\tobj->flags |= USED;\n \t\t\tmark_object_reachable(obj);\n-\t\t} else if (!is_promisor_object(the_repository, oid)) {\n+\t\t} else if (!is_promisor_object(repo, oid)) {\n \t\t\terror(_(\"%s: invalid reflog entry %s\"),\n \t\t\t      refname, oid_to_hex(oid));\n \t\t\terrors_found |= ERROR_REACHABLE;\n@@ -493,8 +494,10 @@ static int fsck_handle_reflog_ent(const char *refname,\n \t\t\t\t  struct object_id *ooid, struct object_id *noid,\n \t\t\t\t  const char *email UNUSED,\n \t\t\t\t  timestamp_t timestamp, int tz UNUSED,\n-\t\t\t\t  const char *message UNUSED, void *cb_data UNUSED)\n+\t\t\t\t  const char *message UNUSED, void *cb_data)\n {\n+\tstruct repository *repo = cb_data;\n+\n \tif (now && timestamp > now)\n \t\treturn 0;\n \n@@ -502,19 +505,20 @@ static int fsck_handle_reflog_ent(const char *refname,\n \t\tfprintf_ln(stderr, _(\"Checking reflog %s->%s\"),\n \t\t\t   oid_to_hex(ooid), oid_to_hex(noid));\n \n-\tfsck_handle_reflog_oid(refname, ooid, 0);\n-\tfsck_handle_reflog_oid(refname, noid, timestamp);\n+\tfsck_handle_reflog_oid(repo, refname, ooid, 0);\n+\tfsck_handle_reflog_oid(repo, refname, noid, timestamp);\n \treturn 0;\n }\n \n static int fsck_handle_reflog(const char *logname, void *cb_data)\n {\n \tstruct strbuf refname = STRBUF_INIT;\n+\tstruct worktree *wt = cb_data;\n \n-\tstrbuf_worktree_ref(cb_data, &refname, logname);\n-\trefs_for_each_reflog_ent(get_main_ref_store(the_repository),\n+\tstrbuf_worktree_ref(wt, &refname, logname);\n+\trefs_for_each_reflog_ent(get_main_ref_store(wt->repo),\n \t\t\t\t refname.buf, fsck_handle_reflog_ent,\n-\t\t\t\t NULL);\n+\t\t\t\t wt->repo);\n \tstrbuf_release(&refname);\n \treturn 0;\n }\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539523","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-10-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 10/14] builtin/fsck: stop using `the_repository` with loose objects","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:15Z","receivedAt":"2026-03-20T11:48:11Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"We depend on `the_repository` when performing consistency checks for\nloose objects. Refactor this to use a context-provided repository\ninstead that is injected via the `struct for_each_loose_cb`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c | 26 +++++++++++++++-----------\n 1 file changed, 15 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex ea441b072e..83299c583e 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -400,7 +400,9 @@ static void check_connectivity(struct repository *repo)\n \t}\n }\n \n-static int fsck_obj(struct object *obj, void *buffer, unsigned long size)\n+static int fsck_obj(struct repository *repo,\n+\t\t    struct object *obj,\n+\t\t    void *buffer, unsigned long size)\n {\n \tint err;\n \n@@ -413,9 +415,9 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)\n \t\t\t   printable_type(&obj->oid, obj->type),\n \t\t\t   describe_object(&obj->oid));\n \n-\tif (fsck_walk(the_repository, obj, NULL, &fsck_obj_options))\n+\tif (fsck_walk(repo, obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, _(\"broken links\"));\n-\terr = fsck_object(the_repository, obj, buffer, size, &fsck_obj_options);\n+\terr = fsck_object(repo, obj, buffer, size, &fsck_obj_options);\n \tif (err)\n \t\tgoto out;\n \n@@ -462,7 +464,7 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,\n \t}\n \tobj->flags &= ~(REACHABLE | SEEN);\n \tobj->flags |= HAS_OBJ;\n-\treturn fsck_obj(obj, buffer, size);\n+\treturn fsck_obj(the_repository, obj, buffer, size);\n }\n \n static int default_refs;\n@@ -710,27 +712,28 @@ static void process_refs(struct repository *repo, struct snapshot *snap)\n \t}\n }\n \n-struct for_each_loose_cb\n-{\n+struct for_each_loose_cb {\n+\tstruct repository *repo;\n \tstruct progress *progress;\n };\n \n static int fsck_loose(const struct object_id *oid, const char *path,\n-\t\t      void *data UNUSED)\n+\t\t      void *cb_data)\n {\n+\tstruct for_each_loose_cb *data = cb_data;\n \tstruct object *obj;\n \tenum object_type type = OBJ_NONE;\n \tunsigned long size;\n \tvoid *contents = NULL;\n \tint eaten;\n \tstruct object_info oi = OBJECT_INFO_INIT;\n-\tstruct object_id real_oid = *null_oid(the_hash_algo);\n+\tstruct object_id real_oid = *null_oid(data->repo->hash_algo);\n \tint err = 0;\n \n \toi.sizep = &size;\n \toi.typep = &type;\n \n-\tif (read_loose_object(the_repository, path, oid, &real_oid, &contents, &oi) < 0) {\n+\tif (read_loose_object(data->repo, path, oid, &real_oid, &contents, &oi) < 0) {\n \t\tif (contents && !oideq(&real_oid, oid))\n \t\t\terr = error(_(\"%s: hash-path mismatch, found at: %s\"),\n \t\t\t\t    oid_to_hex(&real_oid), path);\n@@ -747,7 +750,7 @@ static int fsck_loose(const struct object_id *oid, const char *path,\n \tif (!contents && type != OBJ_BLOB)\n \t\tBUG(\"read_loose_object streamed a non-blob\");\n \n-\tobj = parse_object_buffer(the_repository, oid, type, size,\n+\tobj = parse_object_buffer(data->repo, oid, type, size,\n \t\t\t\t  contents, &eaten);\n \n \tif (!obj) {\n@@ -761,7 +764,7 @@ static int fsck_loose(const struct object_id *oid, const char *path,\n \n \tobj->flags &= ~(REACHABLE | SEEN);\n \tobj->flags |= HAS_OBJ;\n-\tif (fsck_obj(obj, contents, size))\n+\tif (fsck_obj(data->repo, obj, contents, size))\n \t\terrors_found |= ERROR_OBJECT;\n \n \tif (!eaten)\n@@ -789,6 +792,7 @@ static void fsck_source(struct odb_source *source)\n {\n \tstruct progress *progress = NULL;\n \tstruct for_each_loose_cb cb_data = {\n+\t\t.repo = source->odb->repo,\n \t\t.progress = progress,\n \t};\n \n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539524","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-11-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 11/14] builtin/fsck: stop using `the_repository` when checking packed objects","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:16Z","receivedAt":"2026-03-20T11:48:15Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"We implicitly rely on `the_repository` when checking objects part of a\npackfile. These objects are iterated over via `verify_pack()`, which is\nprovided by the packfile subsystem, and a callback function is then\ninvoked for each of the objects in that specific pack.\n\nUnfortunately, it is not possible to provide a payload to the callback\nfunction. Refactor `verify_pack()` to accept a payload that is passed\nthrough to the callback so that we can inject the repository and get rid\nof the use of `the_repository`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c | 13 +++++++------\n pack-check.c   |  7 ++++---\n pack.h         |  9 +++++++--\n 3 files changed, 18 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 83299c583e..874af76e05 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -448,15 +448,16 @@ static int fsck_obj(struct repository *repo,\n }\n \n static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,\n-\t\t\t   unsigned long size, void *buffer, int *eaten)\n+\t\t\t   unsigned long size, void *buffer, int *eaten, void *cb_data)\n {\n+\tstruct repository *repo = cb_data;\n+\tstruct object *obj;\n+\n \t/*\n \t * Note, buffer may be NULL if type is OBJ_BLOB. See\n \t * verify_packfile(), data_valid variable for details.\n \t */\n-\tstruct object *obj;\n-\tobj = parse_object_buffer(the_repository, oid, type, size, buffer,\n-\t\t\t\t  eaten);\n+\tobj = parse_object_buffer(repo, oid, type, size, buffer, eaten);\n \tif (!obj) {\n \t\terrors_found |= ERROR_OBJECT;\n \t\treturn error(_(\"%s: object corrupt or missing\"),\n@@ -464,7 +465,7 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,\n \t}\n \tobj->flags &= ~(REACHABLE | SEEN);\n \tobj->flags |= HAS_OBJ;\n-\treturn fsck_obj(the_repository, obj, buffer, size);\n+\treturn fsck_obj(repo, obj, buffer, size);\n }\n \n static int default_refs;\n@@ -1088,7 +1089,7 @@ int cmd_fsck(int argc,\n \t\t\trepo_for_each_pack(repo, p) {\n \t\t\t\t/* verify gives error messages itself */\n \t\t\t\tif (verify_pack(repo,\n-\t\t\t\t\t\tp, fsck_obj_buffer,\n+\t\t\t\t\t\tp, fsck_obj_buffer, repo,\n \t\t\t\t\t\tprogress, count))\n \t\t\t\t\terrors_found |= ERROR_PACK;\n \t\t\t\tcount += p->num_objects;\ndiff --git a/pack-check.c b/pack-check.c\nindex 7378c80730..79992bb509 100644\n--- a/pack-check.c\n+++ b/pack-check.c\n@@ -53,6 +53,7 @@ static int verify_packfile(struct repository *r,\n \t\t\t   struct packed_git *p,\n \t\t\t   struct pack_window **w_curs,\n \t\t\t   verify_fn fn,\n+\t\t\t   void *fn_data,\n \t\t\t   struct progress *progress, uint32_t base_count)\n \n {\n@@ -161,7 +162,7 @@ static int verify_packfile(struct repository *r,\n \t\t\t\t    oid_to_hex(&oid), p->pack_name);\n \t\telse if (fn) {\n \t\t\tint eaten = 0;\n-\t\t\terr |= fn(&oid, type, size, data, &eaten);\n+\t\t\terr |= fn(&oid, type, size, data, &eaten, fn_data);\n \t\t\tif (eaten)\n \t\t\t\tdata = NULL;\n \t\t}\n@@ -192,7 +193,7 @@ int verify_pack_index(struct packed_git *p)\n \treturn err;\n }\n \n-int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,\n+int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, void *fn_data,\n \t\tstruct progress *progress, uint32_t base_count)\n {\n \tint err = 0;\n@@ -202,7 +203,7 @@ int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,\n \tif (!p->index_data)\n \t\treturn -1;\n \n-\terr |= verify_packfile(r, p, &w_curs, fn, progress, base_count);\n+\terr |= verify_packfile(r, p, &w_curs, fn, fn_data, progress, base_count);\n \tunuse_pack(&w_curs);\n \n \treturn err;\ndiff --git a/pack.h b/pack.h\nindex ec76472e49..1cde92082b 100644\n--- a/pack.h\n+++ b/pack.h\n@@ -85,7 +85,11 @@ struct pack_idx_entry {\n \n struct progress;\n /* Note, the data argument could be NULL if object type is blob */\n-typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*);\n+typedef int (*verify_fn)(const struct object_id *oid,\n+\t\t\t enum object_type type,\n+\t\t\t unsigned long size,\n+\t\t\t void *buffer, int *eaten,\n+\t\t\t void *fn_data);\n \n const char *write_idx_file(struct repository *repo,\n \t\t\t   const char *index_name,\n@@ -95,7 +99,8 @@ const char *write_idx_file(struct repository *repo,\n \t\t\t   const unsigned char *sha1);\n int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);\n int verify_pack_index(struct packed_git *);\n-int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);\n+int verify_pack(struct repository *, struct packed_git *, verify_fn fn, void *fn_data,\n+\t\tstruct progress *, uint32_t);\n off_t write_pack_header(struct hashfile *f, uint32_t);\n void fixup_pack_header_footer(const struct git_hash_algo *, int,\n \t\t\t      unsigned char *, const char *, uint32_t,\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539525","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-12-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 12/14] builtin/fsck: stop using `the_repository` when marking objects","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:17Z","receivedAt":"2026-03-20T11:48:18Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"We implicitly rely on `the_repository` when marking objects for\nconnectivity. Refactor this to instead inject the repository via the\ncallback payload.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c | 55 +++++++++++++++++++++++++++++++++++--------------------\n 1 file changed, 35 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 874af76e05..b15708d155 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -123,10 +123,16 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED,\n \n static struct object_array pending;\n \n+struct mark_object_data {\n+\tstruct repository *repo;\n+\tstruct object *parent;\n+};\n+\n static int mark_object(struct object *obj, enum object_type type,\n-\t\t       void *data, struct fsck_options *options UNUSED)\n+\t\t       void *cb_data, struct fsck_options *options UNUSED)\n {\n-\tstruct object *parent = data;\n+\tstruct mark_object_data *data = cb_data;\n+\tstruct object *parent = data->parent;\n \n \t/*\n \t * The only case data is NULL or type is OBJ_ANY is when\n@@ -153,7 +159,7 @@ static int mark_object(struct object *obj, enum object_type type,\n \t\treturn 0;\n \tobj->flags |= REACHABLE;\n \n-\tif (is_promisor_object(the_repository, &obj->oid))\n+\tif (is_promisor_object(data->repo, &obj->oid))\n \t\t/*\n \t\t * Further recursion does not need to be performed on this\n \t\t * object since it is a promisor object (so it does not need to\n@@ -162,7 +168,7 @@ static int mark_object(struct object *obj, enum object_type type,\n \t\treturn 0;\n \n \tif (!(obj->flags & HAS_OBJ)) {\n-\t\tif (parent && !odb_has_object(the_repository->objects, &obj->oid,\n+\t\tif (parent && !odb_has_object(data->repo->objects, &obj->oid,\n \t\t\t\t\t      HAS_OBJECT_RECHECK_PACKED)) {\n \t\t\tprintf_ln(_(\"broken link from %7s %s\\n\"\n \t\t\t\t    \"              to %7s %s\"),\n@@ -179,14 +185,21 @@ static int mark_object(struct object *obj, enum object_type type,\n \treturn 0;\n }\n \n-static void mark_object_reachable(struct object *obj)\n+static void mark_object_reachable(struct repository *repo, struct object *obj)\n {\n-\tmark_object(obj, OBJ_ANY, NULL, NULL);\n+\tstruct mark_object_data data = {\n+\t\t.repo = repo,\n+\t};\n+\tmark_object(obj, OBJ_ANY, &data, NULL);\n }\n \n static int traverse_one_object(struct repository *repo, struct object *obj)\n {\n-\tint result = fsck_walk(repo, obj, obj, &fsck_walk_options);\n+\tstruct mark_object_data data = {\n+\t\t.repo = repo,\n+\t\t.parent = obj,\n+\t};\n+\tint result = fsck_walk(repo, obj, &data, &fsck_walk_options);\n \n \tif (obj->type == OBJ_TREE) {\n \t\tstruct tree *tree = (struct tree *)obj;\n@@ -222,10 +235,11 @@ static int mark_used(struct object *obj, enum object_type type UNUSED,\n \n static int mark_unreachable_referents(const struct object_id *oid,\n \t\t\t\t      struct object_info *oi UNUSED,\n-\t\t\t\t      void *data UNUSED)\n+\t\t\t\t      void *data)\n {\n+\tstruct repository *repo = data;\n \tstruct fsck_options options = FSCK_OPTIONS_DEFAULT;\n-\tstruct object *obj = lookup_object(the_repository, oid);\n+\tstruct object *obj = lookup_object(repo, oid);\n \n \tif (!obj || !(obj->flags & HAS_OBJ))\n \t\treturn 0; /* not part of our original set */\n@@ -237,14 +251,14 @@ static int mark_unreachable_referents(const struct object_id *oid,\n \t * (and we want to avoid parsing blobs).\n \t */\n \tif (obj->type == OBJ_NONE) {\n-\t\tenum object_type type = odb_read_object_info(the_repository->objects,\n+\t\tenum object_type type = odb_read_object_info(repo->objects,\n \t\t\t\t\t\t\t     &obj->oid, NULL);\n \t\tif (type > 0)\n \t\t\tobject_as_type(obj, type, 0);\n \t}\n \n \toptions.walk = mark_used;\n-\tfsck_walk(the_repository, obj, NULL, &options);\n+\tfsck_walk(repo, obj, NULL, &options);\n \tif (obj->type == OBJ_TREE)\n \t\tfree_tree_buffer((struct tree *)obj);\n \n@@ -384,7 +398,7 @@ static void check_connectivity(struct repository *repo)\n \t\t * traversal.\n \t\t */\n \t\todb_for_each_object(repo->objects, NULL,\n-\t\t\t\t    mark_unreachable_referents, NULL, 0);\n+\t\t\t\t    mark_unreachable_referents, repo, 0);\n \t}\n \n \t/* Look up all the requirements, warn about missing objects.. */\n@@ -484,7 +498,7 @@ static void fsck_handle_reflog_oid(struct repository *repo,\n \t\t\t\t\t\t     \"%s@{%\"PRItime\"}\",\n \t\t\t\t\t\t     refname, timestamp);\n \t\t\tobj->flags |= USED;\n-\t\t\tmark_object_reachable(obj);\n+\t\t\tmark_object_reachable(repo, obj);\n \t\t} else if (!is_promisor_object(repo, oid)) {\n \t\t\terror(_(\"%s: invalid reflog entry %s\"),\n \t\t\t      refname, oid_to_hex(oid));\n@@ -589,7 +603,7 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data)\n \tobj->flags |= USED;\n \tfsck_put_object_name(&fsck_walk_options,\n \t\t\t     ref->oid, \"%s\", ref->name);\n-\tmark_object_reachable(obj);\n+\tmark_object_reachable(repo, obj);\n \n \treturn 0;\n }\n@@ -829,7 +843,7 @@ static int fsck_cache_tree(struct repository *repo, struct cache_tree *it,\n \t\t}\n \t\tobj->flags |= USED;\n \t\tfsck_put_object_name(&fsck_walk_options, &it->oid, \":\");\n-\t\tmark_object_reachable(obj);\n+\t\tmark_object_reachable(repo, obj);\n \t\tif (obj->type != OBJ_TREE)\n \t\t\terr |= objerror(obj, _(\"non-tree in cache-tree\"));\n \t}\n@@ -871,7 +885,7 @@ static int fsck_resolve_undo(struct index_state *istate,\n \t\t\tobj->flags |= USED;\n \t\t\tfsck_put_object_name(&fsck_walk_options, &ru->oid[i],\n \t\t\t\t\t     \":(%d):%s\", i, path);\n-\t\t\tmark_object_reachable(obj);\n+\t\t\tmark_object_reachable(istate->repo, obj);\n \t\t}\n \t}\n \treturn 0;\n@@ -902,7 +916,7 @@ static void fsck_index(struct index_state *istate, const char *index_path,\n \t\t\t\t     \"%s:%s\",\n \t\t\t\t     is_current_worktree ? \"\" : index_path,\n \t\t\t\t     istate->cache[i]->name);\n-\t\tmark_object_reachable(obj);\n+\t\tmark_object_reachable(istate->repo, obj);\n \t}\n \tif (istate->cache_tree)\n \t\tfsck_cache_tree(istate->repo, istate->cache_tree, index_path);\n@@ -911,9 +925,10 @@ static void fsck_index(struct index_state *istate, const char *index_path,\n \n static int mark_object_for_connectivity(const struct object_id *oid,\n \t\t\t\t\tstruct object_info *oi UNUSED,\n-\t\t\t\t\tvoid *cb_data UNUSED)\n+\t\t\t\t\tvoid *cb_data)\n {\n-\tstruct object *obj = lookup_unknown_object(the_repository, oid);\n+\tstruct repository *repo = cb_data;\n+\tstruct object *obj = lookup_unknown_object(repo, oid);\n \tobj->flags |= HAS_OBJ;\n \treturn 0;\n }\n@@ -1064,7 +1079,7 @@ int cmd_fsck(int argc,\n \n \tif (connectivity_only) {\n \t\todb_for_each_object(repo->objects, NULL,\n-\t\t\t\t    mark_object_for_connectivity, NULL, 0);\n+\t\t\t\t    mark_object_for_connectivity, repo, 0);\n \t} else {\n \t\todb_prepare_alternates(repo->objects);\n \t\tfor (source = repo->objects->sources; source; source = source->next)\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539526","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-13-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 13/14] fsck: provide repository in `struct fsck_report_object`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:18Z","receivedAt":"2026-03-20T11:48:21Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"The `report()` function invokes a callback function with a `struct\nfsck_report_object` for every object that failed at least one of our\nchecks. This report doesn't give the caller enough context though, and\nthus they have to rely on global state for `the_repository`.\n\nRefactor the code and inject the repository into the report.\n\nAs this requires us to touch up all callsites, rename the `report()`\nfunction to `fsck_report_object()` to align it with `fsck_report_ref()`.\nWe have already discussed this rename in the past, but we decided to not\ndo it because it required us to adapt too many callsites.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n fsck.c | 365 ++++++++++++++++++++++++++++++++++++++---------------------------\n fsck.h |   1 +\n 2 files changed, 214 insertions(+), 152 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex edf7a0618f..59b9163598 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -258,13 +258,15 @@ static int fsck_vreport(struct fsck_options *options,\n \treturn result;\n }\n \n-__attribute__((format (printf, 5, 6)))\n-static int report(struct fsck_options *options,\n-\t\t  const struct object_id *oid, enum object_type object_type,\n-\t\t  enum fsck_msg_id msg_id, const char *fmt, ...)\n+__attribute__((format (printf, 6, 7)))\n+static int fsck_report_object(struct repository *repo,\n+\t\t\t      struct fsck_options *options,\n+\t\t\t      const struct object_id *oid, enum object_type object_type,\n+\t\t\t      enum fsck_msg_id msg_id, const char *fmt, ...)\n {\n \tva_list ap;\n \tstruct fsck_object_report report = {\n+\t\t.repo = repo,\n \t\t.oid = oid,\n \t\t.object_type = object_type\n \t};\n@@ -626,7 +628,8 @@ static int verify_ordered(unsigned mode1, const char *name1,\n \treturn c1 < c2 ? 0 : TREE_UNORDERED;\n }\n \n-static int fsck_tree(const struct object_id *tree_oid,\n+static int fsck_tree(struct repository *repo,\n+\t\t     const struct object_id *tree_oid,\n \t\t     const char *buffer, unsigned long size,\n \t\t     struct fsck_options *options)\n {\n@@ -649,9 +652,9 @@ static int fsck_tree(const struct object_id *tree_oid,\n \n \tif (init_tree_desc_gently(&desc, tree_oid, buffer, size,\n \t\t\t\t  TREE_DESC_RAW_MODES)) {\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_BAD_TREE,\n-\t\t\t\t \"cannot be parsed as a tree\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_BAD_TREE,\n+\t\t\t\t\t     \"cannot be parsed as a tree\");\n \t\treturn retval;\n \t}\n \n@@ -679,10 +682,10 @@ static int fsck_tree(const struct object_id *tree_oid,\n \t\t\t\toidset_insert(&options->gitmodules_found,\n \t\t\t\t\t      entry_oid);\n \t\t\telse\n-\t\t\t\tretval += report(options,\n-\t\t\t\t\t\t tree_oid, OBJ_TREE,\n-\t\t\t\t\t\t FSCK_MSG_GITMODULES_SYMLINK,\n-\t\t\t\t\t\t \".gitmodules is a symbolic link\");\n+\t\t\t\tretval += fsck_report_object(repo, options,\n+\t\t\t\t\t\t\t     tree_oid, OBJ_TREE,\n+\t\t\t\t\t\t\t     FSCK_MSG_GITMODULES_SYMLINK,\n+\t\t\t\t\t\t\t     \".gitmodules is a symbolic link\");\n \t\t}\n \n \t\tif (is_hfs_dotgitattributes(name) || is_ntfs_dotgitattributes(name)) {\n@@ -690,22 +693,22 @@ static int fsck_tree(const struct object_id *tree_oid,\n \t\t\t\toidset_insert(&options->gitattributes_found,\n \t\t\t\t\t      entry_oid);\n \t\t\telse\n-\t\t\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t\t\t FSCK_MSG_GITATTRIBUTES_SYMLINK,\n-\t\t\t\t\t\t \".gitattributes is a symlink\");\n+\t\t\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t\t\t     FSCK_MSG_GITATTRIBUTES_SYMLINK,\n+\t\t\t\t\t\t\t     \".gitattributes is a symlink\");\n \t\t}\n \n \t\tif (S_ISLNK(mode)) {\n \t\t\tif (is_hfs_dotgitignore(name) ||\n \t\t\t    is_ntfs_dotgitignore(name))\n-\t\t\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t\t\t FSCK_MSG_GITIGNORE_SYMLINK,\n-\t\t\t\t\t\t \".gitignore is a symlink\");\n+\t\t\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t\t\t     FSCK_MSG_GITIGNORE_SYMLINK,\n+\t\t\t\t\t\t\t     \".gitignore is a symlink\");\n \t\t\tif (is_hfs_dotmailmap(name) ||\n \t\t\t    is_ntfs_dotmailmap(name))\n-\t\t\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t\t\t FSCK_MSG_MAILMAP_SYMLINK,\n-\t\t\t\t\t\t \".mailmap is a symlink\");\n+\t\t\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t\t\t     FSCK_MSG_MAILMAP_SYMLINK,\n+\t\t\t\t\t\t\t     \".mailmap is a symlink\");\n \t\t}\n \n \t\tif ((backslash = strchr(name, '\\\\'))) {\n@@ -717,18 +720,18 @@ static int fsck_tree(const struct object_id *tree_oid,\n \t\t\t\t\t\toidset_insert(&options->gitmodules_found,\n \t\t\t\t\t\t\t      entry_oid);\n \t\t\t\t\telse\n-\t\t\t\t\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t\t\t\t\t FSCK_MSG_GITMODULES_SYMLINK,\n-\t\t\t\t\t\t\t\t \".gitmodules is a symbolic link\");\n+\t\t\t\t\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t\t\t\t\t     FSCK_MSG_GITMODULES_SYMLINK,\n+\t\t\t\t\t\t\t\t\t     \".gitmodules is a symbolic link\");\n \t\t\t\t}\n \t\t\t\tbackslash = strchr(backslash, '\\\\');\n \t\t\t}\n \t\t}\n \n \t\tif (update_tree_entry_gently(&desc)) {\n-\t\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t\t FSCK_MSG_BAD_TREE,\n-\t\t\t\t\t \"cannot be parsed as a tree\");\n+\t\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t\t     FSCK_MSG_BAD_TREE,\n+\t\t\t\t\t\t     \"cannot be parsed as a tree\");\n \t\t\tbreak;\n \t\t}\n \n@@ -776,49 +779,49 @@ static int fsck_tree(const struct object_id *tree_oid,\n \tname_stack_clear(&df_dup_candidates);\n \n \tif (has_null_sha1)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_NULL_SHA1,\n-\t\t\t\t \"contains entries pointing to null sha1\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_NULL_SHA1,\n+\t\t\t\t\t     \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_FULL_PATHNAME,\n-\t\t\t\t \"contains full pathnames\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_FULL_PATHNAME,\n+\t\t\t\t\t     \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_EMPTY_NAME,\n-\t\t\t\t \"contains empty pathname\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_EMPTY_NAME,\n+\t\t\t\t\t     \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_HAS_DOT,\n-\t\t\t\t \"contains '.'\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_HAS_DOT,\n+\t\t\t\t\t     \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_HAS_DOTDOT,\n-\t\t\t\t \"contains '..'\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_HAS_DOTDOT,\n+\t\t\t\t\t     \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_HAS_DOTGIT,\n-\t\t\t\t \"contains '.git'\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_HAS_DOTGIT,\n+\t\t\t\t\t     \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_ZERO_PADDED_FILEMODE,\n-\t\t\t\t \"contains zero-padded file modes\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_ZERO_PADDED_FILEMODE,\n+\t\t\t\t\t     \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_BAD_FILEMODE,\n-\t\t\t\t \"contains bad file modes\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_BAD_FILEMODE,\n+\t\t\t\t\t     \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_DUPLICATE_ENTRIES,\n-\t\t\t\t \"contains duplicate file entries\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_DUPLICATE_ENTRIES,\n+\t\t\t\t\t     \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_TREE_NOT_SORTED,\n-\t\t\t\t \"not properly sorted\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_TREE_NOT_SORTED,\n+\t\t\t\t\t     \"not properly sorted\");\n \tif (has_large_name)\n-\t\tretval += report(options, tree_oid, OBJ_TREE,\n-\t\t\t\t FSCK_MSG_LARGE_PATHNAME,\n-\t\t\t\t \"contains excessively large pathname\");\n+\t\tretval += fsck_report_object(repo, options, tree_oid, OBJ_TREE,\n+\t\t\t\t\t     FSCK_MSG_LARGE_PATHNAME,\n+\t\t\t\t\t     \"contains excessively large pathname\");\n \treturn retval;\n }\n \n@@ -839,7 +842,8 @@ static int fsck_tree(const struct object_id *tree_oid,\n  *     case hit the newline we found here as the end-of-header. This makes it\n  *     OK for them to use helpers like parse_oid_hex(), or even skip_prefix().\n  */\n-static int verify_headers(const void *data, unsigned long size,\n+static int verify_headers(struct repository *repo,\n+\t\t\t  const void *data, unsigned long size,\n \t\t\t  const struct object_id *oid, enum object_type type,\n \t\t\t  struct fsck_options *options)\n {\n@@ -849,9 +853,9 @@ static int verify_headers(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn report(options, oid, type,\n-\t\t\t\tFSCK_MSG_NUL_IN_HEADER,\n-\t\t\t\t\"unterminated header: NUL at offset %ld\", i);\n+\t\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t\t  FSCK_MSG_NUL_IN_HEADER,\n+\t\t\t\t\t\t  \"unterminated header: NUL at offset %ld\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n \t\t\t\treturn 0;\n@@ -867,8 +871,9 @@ static int verify_headers(const void *data, unsigned long size,\n \tif (size && buffer[size - 1] == '\\n')\n \t\treturn 0;\n \n-\treturn report(options, oid, type,\n-\t\tFSCK_MSG_UNTERMINATED_HEADER, \"unterminated header\");\n+\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t  FSCK_MSG_UNTERMINATED_HEADER,\n+\t\t\t\t  \"unterminated header\");\n }\n \n static timestamp_t parse_timestamp_from_buf(const char **start, const char *end)\n@@ -887,7 +892,8 @@ static timestamp_t parse_timestamp_from_buf(const char **start, const char *end)\n \treturn parse_timestamp(buf, NULL, 10);\n }\n \n-static int fsck_ident(const char **ident, const char *ident_end,\n+static int fsck_ident(struct repository *repo,\n+\t\t      const char **ident, const char *ident_end,\n \t\t      const struct object_id *oid, enum object_type type,\n \t\t      struct fsck_options *options)\n {\n@@ -900,12 +906,18 @@ static int fsck_ident(const char **ident, const char *ident_end,\n \t*ident = nl + 1;\n \n \tif (*p == '<')\n-\t\treturn report(options, oid, type, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n+\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t  FSCK_MSG_MISSING_NAME_BEFORE_EMAIL,\n+\t\t\t\t\t  \"invalid author/committer line - missing space before email\");\n \tfor (;;) {\n \t\tif (p >= ident_end || *p == '\\n')\n-\t\t\treturn report(options, oid, type, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n+\t\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t\t  FSCK_MSG_MISSING_EMAIL,\n+\t\t\t\t\t\t  \"invalid author/committer line - missing email\");\n \t\tif (*p == '>')\n-\t\t\treturn report(options, oid, type, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n+\t\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t\t  FSCK_MSG_BAD_NAME,\n+\t\t\t\t\t\t  \"invalid author/committer line - bad name\");\n \t\tif (*p == '<')\n \t\t\tbreak; /* end of name, beginning of email */\n \n@@ -913,11 +925,15 @@ static int fsck_ident(const char **ident, const char *ident_end,\n \t\tp++;\n \t}\n \tif (p[-1] != ' ')\n-\t\treturn report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n+\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t  FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL,\n+\t\t\t\t\t  \"invalid author/committer line - missing space before email\");\n \tp++; /* skip past '<' we found */\n \tfor (;;) {\n \t\tif (p >= ident_end || *p == '<' || *p == '\\n')\n-\t\t\treturn report(options, oid, type, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n+\t\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t\t  FSCK_MSG_BAD_EMAIL,\n+\t\t\t\t\t\t  \"invalid author/committer line - bad email\");\n \t\tif (*p == '>')\n \t\t\tbreak; /* end of email */\n \n@@ -926,7 +942,9 @@ static int fsck_ident(const char **ident, const char *ident_end,\n \t}\n \tp++; /* skip past '>' we found */\n \tif (*p != ' ')\n-\t\treturn report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n+\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t  FSCK_MSG_MISSING_SPACE_BEFORE_DATE,\n+\t\t\t\t\t  \"invalid author/committer line - missing space before date\");\n \tp++;\n \t/*\n \t * Our timestamp parser is based on the C strto*() functions, which\n@@ -940,14 +958,21 @@ static int fsck_ident(const char **ident, const char *ident_end,\n \twhile (*p == ' ' || *p == '\\t')\n \t\tp++;\n \tif (!isdigit(*p))\n-\t\treturn report(options, oid, type, FSCK_MSG_BAD_DATE,\n-\t\t\t      \"invalid author/committer line - bad date\");\n+\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t  FSCK_MSG_BAD_DATE,\n+\t\t\t\t\t  \"invalid author/committer line - bad date\");\n \tif (*p == '0' && p[1] != ' ')\n-\t\treturn report(options, oid, type, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n+\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t  FSCK_MSG_ZERO_PADDED_DATE,\n+\t\t\t\t\t  \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(parse_timestamp_from_buf(&p, ident_end)))\n-\t\treturn report(options, oid, type, FSCK_MSG_BAD_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t  FSCK_MSG_BAD_DATE_OVERFLOW,\n+\t\t\t\t\t  \"invalid author/committer line - date causes integer overflow\");\n \tif (*p != ' ')\n-\t\treturn report(options, oid, type, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n+\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t  FSCK_MSG_BAD_DATE,\n+\t\t\t\t\t  \"invalid author/committer line - bad date\");\n \tp++;\n \tif ((*p != '+' && *p != '-') ||\n \t    !isdigit(p[1]) ||\n@@ -955,7 +980,9 @@ static int fsck_ident(const char **ident, const char *ident_end,\n \t    !isdigit(p[3]) ||\n \t    !isdigit(p[4]) ||\n \t    (p[5] != '\\n'))\n-\t\treturn report(options, oid, type, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n+\t\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t\t  FSCK_MSG_BAD_TIMEZONE,\n+\t\t\t\t\t  \"invalid author/committer line - bad time zone\");\n \tp += 6;\n \treturn 0;\n }\n@@ -977,20 +1004,26 @@ static int fsck_commit(struct repository *repo,\n \t * memory safety of the rest of the function depends on it. See the\n \t * comment above the definition of verify_headers() for more details.\n \t */\n-\tif (verify_headers(buffer, size, oid, OBJ_COMMIT, options))\n+\tif (verify_headers(repo, buffer, size, oid, OBJ_COMMIT, options))\n \t\treturn -1;\n \n \tif (buffer >= buffer_end || !skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n+\t\treturn fsck_report_object(repo, options, oid, OBJ_COMMIT,\n+\t\t\t\t\t  FSCK_MSG_MISSING_TREE,\n+\t\t\t\t\t  \"invalid format - expected 'tree' line\");\n \tif (parse_oid_hex_algop(buffer, &tree_oid, &p, repo->hash_algo) || *p != '\\n') {\n-\t\terr = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\t\terr = fsck_report_object(repo, options, oid, OBJ_COMMIT,\n+\t\t\t\t\t FSCK_MSG_BAD_TREE_SHA1,\n+\t\t\t\t\t \"invalid 'tree' line format - bad sha1\");\n \t\tif (err)\n \t\t\treturn err;\n \t}\n \tbuffer = p + 1;\n \twhile (buffer < buffer_end && skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (parse_oid_hex_algop(buffer, &parent_oid, &p, repo->hash_algo) || *p != '\\n') {\n-\t\t\terr = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\t\terr = fsck_report_object(repo, options, oid, OBJ_COMMIT,\n+\t\t\t\t\t\t FSCK_MSG_BAD_PARENT_SHA1,\n+\t\t\t\t\t\t \"invalid 'parent' line format - bad sha1\");\n \t\t\tif (err)\n \t\t\t\treturn err;\n \t\t}\n@@ -999,24 +1032,31 @@ static int fsck_commit(struct repository *repo,\n \tauthor_count = 0;\n \twhile (buffer < buffer_end && skip_prefix(buffer, \"author \", &buffer)) {\n \t\tauthor_count++;\n-\t\terr = fsck_ident(&buffer, buffer_end, oid, OBJ_COMMIT, options);\n+\t\terr = fsck_ident(repo, &buffer, buffer_end, oid, OBJ_COMMIT, options);\n \t\tif (err)\n \t\t\treturn err;\n \t}\n \tif (author_count < 1)\n-\t\terr = report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n+\t\terr = fsck_report_object(repo, options, oid, OBJ_COMMIT,\n+\t\t\t\t\t FSCK_MSG_MISSING_AUTHOR,\n+\t\t\t\t\t \"invalid format - expected 'author' line\");\n \telse if (author_count > 1)\n-\t\terr = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n+\t\terr = fsck_report_object(repo, options, oid, OBJ_COMMIT,\n+\t\t\t\t\t FSCK_MSG_MULTIPLE_AUTHORS,\n+\t\t\t\t\t \"invalid format - multiple 'author' lines\");\n \tif (err)\n \t\treturn err;\n \tif (buffer >= buffer_end || !skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n-\terr = fsck_ident(&buffer, buffer_end, oid, OBJ_COMMIT, options);\n+\t\treturn fsck_report_object(repo, options, oid, OBJ_COMMIT,\n+\t\t\t\t\t  FSCK_MSG_MISSING_COMMITTER,\n+\t\t\t\t\t  \"invalid format - expected 'committer' line\");\n+\terr = fsck_ident(repo, &buffer, buffer_end, oid, OBJ_COMMIT, options);\n \tif (err)\n \t\treturn err;\n \tif (memchr(buffer_begin, '\\0', size)) {\n-\t\terr = report(options, oid, OBJ_COMMIT, FSCK_MSG_NUL_IN_COMMIT,\n-\t\t\t     \"NUL byte in the commit object body\");\n+\t\terr = fsck_report_object(repo, options, oid, OBJ_COMMIT,\n+\t\t\t\t\t FSCK_MSG_NUL_IN_COMMIT,\n+\t\t\t\t\t \"NUL byte in the commit object body\");\n \t\tif (err)\n \t\t\treturn err;\n \t}\n@@ -1050,52 +1090,66 @@ int fsck_tag_standalone(struct repository *repo,\n \t * memory safety of the rest of the function depends on it. See the\n \t * comment above the definition of verify_headers() for more details.\n \t */\n-\tret = verify_headers(buffer, size, oid, OBJ_TAG, options);\n+\tret = verify_headers(repo, buffer, size, oid, OBJ_TAG, options);\n \tif (ret)\n \t\tgoto done;\n \n \tif (buffer >= buffer_end || !skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n+\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t FSCK_MSG_MISSING_OBJECT,\n+\t\t\t\t\t \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (parse_oid_hex_algop(buffer, tagged_oid, &p, repo->hash_algo) || *p != '\\n') {\n-\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n+\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t FSCK_MSG_BAD_OBJECT_SHA1,\n+\t\t\t\t\t \"invalid 'object' line format - bad sha1\");\n \t\tif (ret)\n \t\t\tgoto done;\n \t}\n \tbuffer = p + 1;\n \n \tif (buffer >= buffer_end || !skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE_ENTRY, \"invalid format - expected 'type' line\");\n+\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t FSCK_MSG_MISSING_TYPE_ENTRY,\n+\t\t\t\t\t \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = memchr(buffer, '\\n', buffer_end - buffer);\n \tif (!eol) {\n-\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t FSCK_MSG_MISSING_TYPE,\n+\t\t\t\t\t \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \t*tagged_type = type_from_string_gently(buffer, eol - buffer, 1);\n \tif (*tagged_type < 0)\n-\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, \"invalid 'type' value\");\n+\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t FSCK_MSG_BAD_TYPE,\n+\t\t\t\t\t \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (buffer >= buffer_end || !skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG_ENTRY, \"invalid format - expected 'tag' line\");\n+\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t FSCK_MSG_MISSING_TAG_ENTRY,\n+\t\t\t\t\t \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = memchr(buffer, '\\n', buffer_end - buffer);\n \tif (!eol) {\n-\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t FSCK_MSG_MISSING_TAG,\n+\t\t\t\t\t \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0)) {\n-\t\tret = report(options, oid, OBJ_TAG,\n-\t\t\t     FSCK_MSG_BAD_TAG_NAME,\n-\t\t\t     \"invalid 'tag' name: %.*s\",\n-\t\t\t     (int)(eol - buffer), buffer);\n+\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t FSCK_MSG_BAD_TAG_NAME,\n+\t\t\t\t\t \"invalid 'tag' name: %.*s\",\n+\t\t\t\t\t (int)(eol - buffer), buffer);\n \t\tif (ret)\n \t\t\tgoto done;\n \t}\n@@ -1103,17 +1157,21 @@ int fsck_tag_standalone(struct repository *repo,\n \n \tif (buffer >= buffer_end || !skip_prefix(buffer, \"tagger \", &buffer)) {\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t FSCK_MSG_MISSING_TAGGER_ENTRY,\n+\t\t\t\t\t \"invalid format - expected 'tagger' line\");\n \t\tif (ret)\n \t\t\tgoto done;\n \t}\n \telse\n-\t\tret = fsck_ident(&buffer, buffer_end, oid, OBJ_TAG, options);\n+\t\tret = fsck_ident(repo, &buffer, buffer_end, oid, OBJ_TAG, options);\n \n \tif (buffer < buffer_end && (skip_prefix(buffer, \"gpgsig \", &buffer) || skip_prefix(buffer, \"gpgsig-sha256 \", &buffer))) {\n \t\teol = memchr(buffer, '\\n', buffer_end - buffer);\n \t\tif (!eol) {\n-\t\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_GPGSIG, \"invalid format - unexpected end after 'gpgsig' or 'gpgsig-sha256' line\");\n+\t\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t\t FSCK_MSG_BAD_GPGSIG,\n+\t\t\t\t\t\t \"invalid format - unexpected end after 'gpgsig' or 'gpgsig-sha256' line\");\n \t\t\tgoto done;\n \t\t}\n \t\tbuffer = eol + 1;\n@@ -1121,7 +1179,9 @@ int fsck_tag_standalone(struct repository *repo,\n \t\twhile (buffer < buffer_end && starts_with(buffer, \" \")) {\n \t\t\teol = memchr(buffer, '\\n', buffer_end - buffer);\n \t\t\tif (!eol) {\n-\t\t\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_HEADER_CONTINUATION, \"invalid format - unexpected end in 'gpgsig' or 'gpgsig-sha256' continuation line\");\n+\t\t\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t\t\t FSCK_MSG_BAD_HEADER_CONTINUATION,\n+\t\t\t\t\t\t\t \"invalid format - unexpected end in 'gpgsig' or 'gpgsig-sha256' continuation line\");\n \t\t\t\tgoto done;\n \t\t\t}\n \t\t\tbuffer = eol + 1;\n@@ -1136,7 +1196,9 @@ int fsck_tag_standalone(struct repository *repo,\n \t\t * garbage\" could be a custom header. E.g. \"mktag\"\n \t\t * doesn't want any unknown headers.\n \t\t */\n-\t\tret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_ENTRY, \"invalid format - extra header(s) after 'tagger'\");\n+\t\tret = fsck_report_object(repo, options, oid, OBJ_TAG,\n+\t\t\t\t\t FSCK_MSG_EXTRA_HEADER_ENTRY,\n+\t\t\t\t\t \"invalid format - extra header(s) after 'tagger'\");\n \t\tif (ret)\n \t\t\tgoto done;\n \t}\n@@ -1147,6 +1209,7 @@ int fsck_tag_standalone(struct repository *repo,\n }\n \n struct fsck_gitmodules_data {\n+\tstruct repository *repo;\n \tconst struct object_id *oid;\n \tstruct fsck_options *options;\n \tint ret;\n@@ -1157,6 +1220,7 @@ static int fsck_gitmodules_fn(const char *var, const char *value,\n \t\t\t      void *vdata)\n {\n \tstruct fsck_gitmodules_data *data = vdata;\n+\tstruct repository *repo = data->repo;\n \tconst char *subsection, *key;\n \tsize_t subsection_len;\n \tchar *name;\n@@ -1167,37 +1231,31 @@ static int fsck_gitmodules_fn(const char *var, const char *value,\n \n \tname = xmemdupz(subsection, subsection_len);\n \tif (check_submodule_name(name) < 0)\n-\t\tdata->ret |= report(data->options,\n-\t\t\t\t    data->oid, OBJ_BLOB,\n-\t\t\t\t    FSCK_MSG_GITMODULES_NAME,\n-\t\t\t\t    \"disallowed submodule name: %s\",\n-\t\t\t\t    name);\n+\t\tdata->ret |= fsck_report_object(repo, data->options, data->oid, OBJ_BLOB,\n+\t\t\t\t\t\tFSCK_MSG_GITMODULES_NAME,\n+\t\t\t\t\t\t\"disallowed submodule name: %s\", name);\n \tif (!strcmp(key, \"url\") && value &&\n \t    check_submodule_url(value) < 0)\n-\t\tdata->ret |= report(data->options,\n-\t\t\t\t    data->oid, OBJ_BLOB,\n-\t\t\t\t    FSCK_MSG_GITMODULES_URL,\n-\t\t\t\t    \"disallowed submodule url: %s\",\n-\t\t\t\t    value);\n+\t\tdata->ret |= fsck_report_object(repo, data->options, data->oid, OBJ_BLOB,\n+\t\t\t\t\t\tFSCK_MSG_GITMODULES_URL,\n+\t\t\t\t\t\t\"disallowed submodule url: %s\", value);\n \tif (!strcmp(key, \"path\") && value &&\n \t    looks_like_command_line_option(value))\n-\t\tdata->ret |= report(data->options,\n-\t\t\t\t    data->oid, OBJ_BLOB,\n-\t\t\t\t    FSCK_MSG_GITMODULES_PATH,\n-\t\t\t\t    \"disallowed submodule path: %s\",\n-\t\t\t\t    value);\n+\t\tdata->ret |= fsck_report_object(repo, data->options, data->oid, OBJ_BLOB,\n+\t\t\t\t\t\tFSCK_MSG_GITMODULES_PATH,\n+\t\t\t\t\t\t\"disallowed submodule path: %s\", value);\n \tif (!strcmp(key, \"update\") && value &&\n \t    parse_submodule_update_type(value) == SM_UPDATE_COMMAND)\n-\t\tdata->ret |= report(data->options, data->oid, OBJ_BLOB,\n-\t\t\t\t    FSCK_MSG_GITMODULES_UPDATE,\n-\t\t\t\t    \"disallowed submodule update setting: %s\",\n-\t\t\t\t    value);\n+\t\tdata->ret |= fsck_report_object(repo, data->options, data->oid, OBJ_BLOB,\n+\t\t\t\t\t\tFSCK_MSG_GITMODULES_UPDATE,\n+\t\t\t\t\t\t\"disallowed submodule update setting: %s\", value);\n \tfree(name);\n \n \treturn 0;\n }\n \n-static int fsck_blob(const struct object_id *oid, const char *buf,\n+static int fsck_blob(struct repository *repo,\n+\t\t     const struct object_id *oid, const char *buf,\n \t\t     unsigned long size, struct fsck_options *options)\n {\n \tint ret = 0;\n@@ -1217,11 +1275,12 @@ static int fsck_blob(const struct object_id *oid, const char *buf,\n \t\t\t * blob too gigantic to load into memory. Let's just consider\n \t\t\t * that an error.\n \t\t\t */\n-\t\t\treturn report(options, oid, OBJ_BLOB,\n-\t\t\t\t\tFSCK_MSG_GITMODULES_LARGE,\n-\t\t\t\t\t\".gitmodules too large to parse\");\n+\t\t\treturn fsck_report_object(repo, options, oid, OBJ_BLOB,\n+\t\t\t\t\t\t  FSCK_MSG_GITMODULES_LARGE,\n+\t\t\t\t\t\t  \".gitmodules too large to parse\");\n \t\t}\n \n+\t\tdata.repo = repo;\n \t\tdata.oid = oid;\n \t\tdata.options = options;\n \t\tdata.ret = 0;\n@@ -1229,9 +1288,9 @@ static int fsck_blob(const struct object_id *oid, const char *buf,\n \t\tif (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,\n \t\t\t\t\t\".gitmodules\", buf, size, &data,\n \t\t\t\t\tCONFIG_SCOPE_UNKNOWN, &config_opts))\n-\t\t\tdata.ret |= report(options, oid, OBJ_BLOB,\n-\t\t\t\t\tFSCK_MSG_GITMODULES_PARSE,\n-\t\t\t\t\t\"could not parse gitmodules blob\");\n+\t\t\tdata.ret |= fsck_report_object(repo, options, oid, OBJ_BLOB,\n+\t\t\t\t\t\t       FSCK_MSG_GITMODULES_PARSE,\n+\t\t\t\t\t\t       \"could not parse gitmodules blob\");\n \t\tret |= data.ret;\n \t}\n \n@@ -1246,17 +1305,17 @@ static int fsck_blob(const struct object_id *oid, const char *buf,\n \t\t\t * blob too gigantic to load into memory. Let's just consider\n \t\t\t * that an error.\n \t\t\t */\n-\t\t\treturn report(options, oid, OBJ_BLOB,\n-\t\t\t\t\tFSCK_MSG_GITATTRIBUTES_LARGE,\n-\t\t\t\t\t\".gitattributes too large to parse\");\n+\t\t\treturn fsck_report_object(repo, options, oid, OBJ_BLOB,\n+\t\t\t\t\t\t  FSCK_MSG_GITATTRIBUTES_LARGE,\n+\t\t\t\t\t\t  \".gitattributes too large to parse\");\n \t\t}\n \n \t\tfor (ptr = buf; *ptr; ) {\n \t\t\tconst char *eol = strchrnul(ptr, '\\n');\n \t\t\tif (eol - ptr >= ATTR_MAX_LINE_LENGTH) {\n-\t\t\t\tret |= report(options, oid, OBJ_BLOB,\n-\t\t\t\t\t      FSCK_MSG_GITATTRIBUTES_LINE_LENGTH,\n-\t\t\t\t\t      \".gitattributes has too long lines to parse\");\n+\t\t\t\tret |= fsck_report_object(repo, options, oid, OBJ_BLOB,\n+\t\t\t\t\t\t\t  FSCK_MSG_GITATTRIBUTES_LINE_LENGTH,\n+\t\t\t\t\t\t\t  \".gitattributes has too long lines to parse\");\n \t\t\t\tbreak;\n \t\t\t}\n \n@@ -1272,7 +1331,9 @@ int fsck_object(struct repository *repo,\n \tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn report(options, NULL, OBJ_NONE, FSCK_MSG_BAD_OBJECT_SHA1, \"no valid object to fsck\");\n+\t\treturn fsck_report_object(repo, options, NULL, OBJ_NONE,\n+\t\t\t\t\t  FSCK_MSG_BAD_OBJECT_SHA1,\n+\t\t\t\t\t  \"no valid object to fsck\");\n \n \treturn fsck_buffer(repo, &obj->oid, obj->type, data, size, options);\n }\n@@ -1283,18 +1344,18 @@ int fsck_buffer(struct repository *repo,\n \t\tstruct fsck_options *options)\n {\n \tif (type == OBJ_BLOB)\n-\t\treturn fsck_blob(oid, data, size, options);\n+\t\treturn fsck_blob(repo, oid, data, size, options);\n \tif (type == OBJ_TREE)\n-\t\treturn fsck_tree(oid, data, size, options);\n+\t\treturn fsck_tree(repo, oid, data, size, options);\n \tif (type == OBJ_COMMIT)\n \t\treturn fsck_commit(repo, oid, data, size, options);\n \tif (type == OBJ_TAG)\n \t\treturn fsck_tag(repo, oid, data, size, options);\n \n-\treturn report(options, oid, type,\n-\t\t      FSCK_MSG_UNKNOWN_TYPE,\n-\t\t      \"unknown type '%d' (internal fsck error)\",\n-\t\t      type);\n+\treturn fsck_report_object(repo, options, oid, type,\n+\t\t\t\t  FSCK_MSG_UNKNOWN_TYPE,\n+\t\t\t\t  \"unknown type '%d' (internal fsck error)\",\n+\t\t\t\t  type);\n }\n \n int fsck_objects_error_function(struct fsck_options *o,\n@@ -1357,17 +1418,17 @@ static int fsck_blobs(struct repository *repo,\n \t\tif (!buf) {\n \t\t\tif (is_promisor_object(repo, oid))\n \t\t\t\tcontinue;\n-\t\t\tret |= report(options,\n-\t\t\t\t      oid, OBJ_BLOB, msg_missing,\n-\t\t\t\t      \"unable to read %s blob\", blob_type);\n+\t\t\tret |= fsck_report_object(repo, options,\n+\t\t\t\t\t\t  oid, OBJ_BLOB, msg_missing,\n+\t\t\t\t\t\t  \"unable to read %s blob\", blob_type);\n \t\t\tcontinue;\n \t\t}\n \n \t\tif (type == OBJ_BLOB)\n-\t\t\tret |= fsck_blob(oid, buf, size, options);\n+\t\t\tret |= fsck_blob(repo, oid, buf, size, options);\n \t\telse\n-\t\t\tret |= report(options, oid, type, msg_type,\n-\t\t\t\t      \"non-blob found at %s\", blob_type);\n+\t\t\tret |= fsck_report_object(repo, options, oid, type, msg_type,\n+\t\t\t\t\t\t  \"non-blob found at %s\", blob_type);\n \t\tfree(buf);\n \t}\n \ndiff --git a/fsck.h b/fsck.h\nindex 62b7c3c93a..5d05996dd0 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -159,6 +159,7 @@ int fsck_refs_error_function(struct fsck_options *options,\n \t\t\t     const char *message);\n \n struct fsck_object_report {\n+\tstruct repository *repo;\n \tconst struct object_id *oid;\n \tenum object_type object_type;\n };\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539527","messageId":"20260320-b4-pks-fsck-without-the-repository-v1-14-6594f997926b@pks.im","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-0-6594f997926b@pks.im","subject":"[PATCH 14/14] builtin/fsck: stop using `the_repository` in error reporting","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-20T11:47:19Z","receivedAt":"2026-03-20T11:48:23Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"In the preceding commit we have introduced the repository into `struct\nfsck_object_report`. This allows us to drop remaining uses of the global\n`the_repository` variable.\n\nDrop them and remove `USE_THE_REPOSITORY_VARIABLE`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fsck.c | 36 ++++++++++++++++++------------------\n 1 file changed, 18 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex b15708d155..24d8ec6d42 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -1,4 +1,3 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n #include \"builtin.h\"\n #include \"gettext.h\"\n #include \"hex.h\"\n@@ -66,13 +65,14 @@ static const char *describe_object(const struct object_id *oid)\n \treturn fsck_describe_object(&fsck_walk_options, oid);\n }\n \n-static const char *printable_type(const struct object_id *oid,\n+static const char *printable_type(struct repository *repo,\n+\t\t\t\t  const struct object_id *oid,\n \t\t\t\t  enum object_type type)\n {\n \tconst char *ret;\n \n \tif (type == OBJ_NONE)\n-\t\ttype = odb_read_object_info(the_repository->objects,\n+\t\ttype = odb_read_object_info(repo->objects,\n \t\t\t\t\t    oid, NULL);\n \n \tret = type_name(type);\n@@ -82,12 +82,12 @@ static const char *printable_type(const struct object_id *oid,\n \treturn ret;\n }\n \n-static int objerror(struct object *obj, const char *err)\n+static int objerror(struct repository *repo, struct object *obj, const char *err)\n {\n \terrors_found |= ERROR_OBJECT;\n \t/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */\n \tfprintf_ln(stderr, _(\"error in %s %s: %s\"),\n-\t\t   printable_type(&obj->oid, obj->type),\n+\t\t   printable_type(repo, &obj->oid, obj->type),\n \t\t   describe_object(&obj->oid), err);\n \treturn -1;\n }\n@@ -106,13 +106,13 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED,\n \tcase FSCK_WARN:\n \t\t/* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */\n \t\tfprintf_ln(stderr, _(\"warning in %s %s: %s\"),\n-\t\t\t   printable_type(oid, object_type),\n+\t\t\t   printable_type(report->repo, oid, object_type),\n \t\t\t   describe_object(oid), message);\n \t\treturn 0;\n \tcase FSCK_ERROR:\n \t\t/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */\n \t\tfprintf_ln(stderr, _(\"error in %s %s: %s\"),\n-\t\t\t   printable_type(oid, object_type),\n+\t\t\t   printable_type(report->repo, oid, object_type),\n \t\t\t   describe_object(oid), message);\n \t\treturn 1;\n \tdefault:\n@@ -142,7 +142,7 @@ static int mark_object(struct object *obj, enum object_type type,\n \tif (!obj) {\n \t\t/* ... these references to parent->fld are safe here */\n \t\tprintf_ln(_(\"broken link from %7s %s\"),\n-\t\t\t  printable_type(&parent->oid, parent->type),\n+\t\t\t  printable_type(data->repo, &parent->oid, parent->type),\n \t\t\t  describe_object(&parent->oid));\n \t\tprintf_ln(_(\"broken link from %7s %s\"),\n \t\t\t  (type == OBJ_ANY ? _(\"unknown\") : type_name(type)),\n@@ -153,7 +153,7 @@ static int mark_object(struct object *obj, enum object_type type,\n \n \tif (type != OBJ_ANY && obj->type != type)\n \t\t/* ... and the reference to parent is safe here */\n-\t\tobjerror(parent, _(\"wrong object type in link\"));\n+\t\tobjerror(data->repo, parent, _(\"wrong object type in link\"));\n \n \tif (obj->flags & REACHABLE)\n \t\treturn 0;\n@@ -172,9 +172,9 @@ static int mark_object(struct object *obj, enum object_type type,\n \t\t\t\t\t      HAS_OBJECT_RECHECK_PACKED)) {\n \t\t\tprintf_ln(_(\"broken link from %7s %s\\n\"\n \t\t\t\t    \"              to %7s %s\"),\n-\t\t\t\t  printable_type(&parent->oid, parent->type),\n+\t\t\t\t  printable_type(data->repo, &parent->oid, parent->type),\n \t\t\t\t  describe_object(&parent->oid),\n-\t\t\t\t  printable_type(&obj->oid, obj->type),\n+\t\t\t\t  printable_type(data->repo, &obj->oid, obj->type),\n \t\t\t\t  describe_object(&obj->oid));\n \t\t\terrors_found |= ERROR_REACHABLE;\n \t\t}\n@@ -281,7 +281,7 @@ static void check_reachable_object(struct repository *repo, struct object *obj)\n \t\tif (has_object_pack(repo, &obj->oid))\n \t\t\treturn; /* it is in pack - forget about it */\n \t\tprintf_ln(_(\"missing %s %s\"),\n-\t\t\t  printable_type(&obj->oid, obj->type),\n+\t\t\t  printable_type(repo, &obj->oid, obj->type),\n \t\t\t  describe_object(&obj->oid));\n \t\terrors_found |= ERROR_REACHABLE;\n \t\treturn;\n@@ -308,7 +308,7 @@ static void check_unreachable_object(struct repository *repo, struct object *obj\n \t */\n \tif (show_unreachable) {\n \t\tprintf_ln(_(\"unreachable %s %s\"),\n-\t\t\t  printable_type(&obj->oid, obj->type),\n+\t\t\t  printable_type(repo, &obj->oid, obj->type),\n \t\t\t  describe_object(&obj->oid));\n \t\treturn;\n \t}\n@@ -328,7 +328,7 @@ static void check_unreachable_object(struct repository *repo, struct object *obj\n \tif (!(obj->flags & USED)) {\n \t\tif (show_dangling)\n \t\t\tprintf_ln(_(\"dangling %s %s\"),\n-\t\t\t\t  printable_type(&obj->oid, obj->type),\n+\t\t\t\t  printable_type(repo, &obj->oid, obj->type),\n \t\t\t\t  describe_object(&obj->oid));\n \t\tif (write_lost_and_found) {\n \t\t\tchar *filename = repo_git_path(repo, \"lost-found/%s/%s\",\n@@ -426,11 +426,11 @@ static int fsck_obj(struct repository *repo,\n \n \tif (verbose)\n \t\tfprintf_ln(stderr, _(\"Checking %s %s\"),\n-\t\t\t   printable_type(&obj->oid, obj->type),\n+\t\t\t   printable_type(repo, &obj->oid, obj->type),\n \t\t\t   describe_object(&obj->oid));\n \n \tif (fsck_walk(repo, obj, NULL, &fsck_obj_options))\n-\t\tobjerror(obj, _(\"broken links\"));\n+\t\tobjerror(repo, obj, _(\"broken links\"));\n \terr = fsck_object(repo, obj, buffer, size, &fsck_obj_options);\n \tif (err)\n \t\tgoto out;\n@@ -448,7 +448,7 @@ static int fsck_obj(struct repository *repo,\n \n \t\tif (show_tags && tag->tagged) {\n \t\t\tprintf_ln(_(\"tagged %s %s (%s) in %s\"),\n-\t\t\t\t  printable_type(&tag->tagged->oid, tag->tagged->type),\n+\t\t\t\t  printable_type(repo, &tag->tagged->oid, tag->tagged->type),\n \t\t\t\t  describe_object(&tag->tagged->oid),\n \t\t\t\t  tag->tag,\n \t\t\t\t  describe_object(&tag->object.oid));\n@@ -845,7 +845,7 @@ static int fsck_cache_tree(struct repository *repo, struct cache_tree *it,\n \t\tfsck_put_object_name(&fsck_walk_options, &it->oid, \":\");\n \t\tmark_object_reachable(repo, obj);\n \t\tif (obj->type != OBJ_TREE)\n-\t\t\terr |= objerror(obj, _(\"non-tree in cache-tree\"));\n+\t\t\terr |= objerror(repo, obj, _(\"non-tree in cache-tree\"));\n \t}\n \tfor (i = 0; i < it->subtree_nr; i++)\n \t\terr |= fsck_cache_tree(repo, it->down[i]->cache_tree, index_path);\n\n-- \n2.53.0.1055.ga2ffed1127.dirty\n\n"},{"id":"539581","messageId":"xmqq341udrob.fsf@gitster.g","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-1-6594f997926b@pks.im","subject":"Re: [PATCH 01/14] fsck: drop `the_repository` in `fsck_walk()`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-20T23:09:24Z","receivedAt":"2026-03-20T23:09:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":null},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The function `fsck_walk()` and its object type specific functions\n> `fsck_walk_tree()` et al implicitly rely on `the_repository`. Remove\n> this dependency by injecting the repository as a parameter instead.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  builtin/fsck.c           |  6 +++---\n>  builtin/index-pack.c     |  2 +-\n>  builtin/unpack-objects.c |  2 +-\n>  fsck.c                   | 40 ++++++++++++++++++++++++++--------------\n>  fsck.h                   | 15 ++++++++++++---\n>  5 files changed, 43 insertions(+), 22 deletions(-)\n\nOK.\n\nWhile the conversion is cleanly done, the resulting code makes me\nwonder if fsck_options struct\n\n        struct fsck_options {\n                fsck_walk_func walk;\n                fsck_error error_func;\n                unsigned strict;\n                unsigned verbose;\n                enum fsck_msg_type *msg_type;\n                struct oidset skip_oids;\n                struct oidset gitmodules_found;\n                struct oidset gitmodules_done;\n                struct oidset gitattributes_found;\n                struct oidset gitattributes_done;\n                kh_oid_map_t *object_names;\n        };\n\nshould gain a repository pointer.  After all, these oidset and oidmap\nare about objects in one particular repository.\n\n"},{"id":"539582","messageId":"xmqqy0jmccx2.fsf@gitster.g","threadId":"65317","inReplyTo":"20260320-b4-pks-fsck-without-the-repository-v1-13-6594f997926b@pks.im","subject":"Re: [PATCH 13/14] fsck: provide repository in `struct fsck_report_object`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-20T23:13:29Z","receivedAt":"2026-03-20T23:13:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":null},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> The `report()` function invokes a callback function with a `struct\n> fsck_report_object` for every object that failed at least one of our\n> checks. This report doesn't give the caller enough context though, and\n> thus they have to rely on global state for `the_repository`.\n>\n> Refactor the code and inject the repository into the report.\n>\n> As this requires us to touch up all callsites, rename the `report()`\n> function to `fsck_report_object()` to align it with `fsck_report_ref()`.\n> We have already discussed this rename in the past, but we decided to not\n> do it because it required us to adapt too many callsites.\n>\n> Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> ---\n>  fsck.c | 365 ++++++++++++++++++++++++++++++++++++++---------------------------\n>  fsck.h |   1 +\n>  2 files changed, 214 insertions(+), 152 deletions(-)\n>\n> diff --git a/fsck.c b/fsck.c\n> index edf7a0618f..59b9163598 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -258,13 +258,15 @@ static int fsck_vreport(struct fsck_options *options,\n>  \treturn result;\n>  }\n>  \n> -__attribute__((format (printf, 5, 6)))\n> -static int report(struct fsck_options *options,\n> -\t\t  const struct object_id *oid, enum object_type object_type,\n> -\t\t  enum fsck_msg_id msg_id, const char *fmt, ...)\n> +__attribute__((format (printf, 6, 7)))\n> +static int fsck_report_object(struct repository *repo,\n> +\t\t\t      struct fsck_options *options,\n> +\t\t\t      const struct object_id *oid, enum object_type object_type,\n> +\t\t\t      enum fsck_msg_id msg_id, const char *fmt, ...)\n\nEarlier I wondered if struct repository *repo should be added to\nfsck_options; I didn't comment on individual patches, but almost all\npatches I saw since that remark made me suspect that fsck_options\nwould be a good place to pass repository throughout the callchain\neven more.\n"},{"id":"539731","messageId":"acEwis6mGCaY4WtR@pks.im","threadId":"65317","inReplyTo":"xmqq341udrob.fsf@gitster.g","subject":"Re: [PATCH 01/14] fsck: drop `the_repository` in `fsck_walk()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-03-23T12:22:34Z","receivedAt":"2026-03-23T12:22:45Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":null},"body":"On Fri, Mar 20, 2026 at 04:09:24PM -0700, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > The function `fsck_walk()` and its object type specific functions\n> > `fsck_walk_tree()` et al implicitly rely on `the_repository`. Remove\n> > this dependency by injecting the repository as a parameter instead.\n> >\n> > Signed-off-by: Patrick Steinhardt <ps@pks.im>\n> > ---\n> >  builtin/fsck.c           |  6 +++---\n> >  builtin/index-pack.c     |  2 +-\n> >  builtin/unpack-objects.c |  2 +-\n> >  fsck.c                   | 40 ++++++++++++++++++++++++++--------------\n> >  fsck.h                   | 15 ++++++++++++---\n> >  5 files changed, 43 insertions(+), 22 deletions(-)\n> \n> OK.\n> \n> While the conversion is cleanly done, the resulting code makes me\n> wonder if fsck_options struct\n> \n>         struct fsck_options {\n>                 fsck_walk_func walk;\n>                 fsck_error error_func;\n>                 unsigned strict;\n>                 unsigned verbose;\n>                 enum fsck_msg_type *msg_type;\n>                 struct oidset skip_oids;\n>                 struct oidset gitmodules_found;\n>                 struct oidset gitmodules_done;\n>                 struct oidset gitattributes_found;\n>                 struct oidset gitattributes_done;\n>                 kh_oid_map_t *object_names;\n>         };\n> \n> should gain a repository pointer.  After all, these oidset and oidmap\n> are about objects in one particular repository.\n\nThat's fair, and indeed this was my first attempt at solving the\ndependency on `the_repository`. The reason why I discarded that approach\nthough is the way that we currenlty initialize `struct fsck_options` via\na set of macros. And as we often do this in file-static scope we don't\nhave a repository available there.\n\nI had a second look at this though and have now introduced a new\nfunction to initialize the options instead. It's a tiny bit ugly, and\nideally we'd just not have the options in global scope in the first\nplace. But I guess that overall it's okayish.\n\nWill update and send the revised version soonish.\n\nThanks!\n\nPatrick\n"}]}