From: Toon Claes <toon@iotcl.com>
To: Patrick Steinhardt <ps@pks.im>, git@vger.kernel.org
Subject: Re: [PATCH 1/2] odb/source: generalize `reprepare()` callback
Date: Fri, 26 Jun 2026 14:10:32 +0200 [thread overview]
Message-ID: <87ldc14i4n.fsf@emacs.iotcl.com> (raw)
In-Reply-To: <20260622-b4-pks-odb-generalize-prepare-v1-1-d2a5c5d13144@pks.im>
Patrick Steinhardt <ps@pks.im> writes:
> The `reprepare()` callback function can be used to flush caches of a
> given object source and then prepare it anew. This is for example used
> when a concurrent process may have written new objects. Ultimately, this
> can be seen as doing two separate steps:
>
> 1. We drop any caches.
>
> 2. We prepare the source.
>
> We have one callsite in git-grep(1) though that really only want to do
> (2). This is done by reaching into the "files" backend directly and then
> calling `odb_source_packed_prepare()`, which of course may not work with
> alternate backends.
>
> We could in theory just call `reprepare()` here, and that would likely
> not have any significant downside. But this would certainly feel like a
> code smell.
>
> Instead, generalize the `reprepare()` callback to `prepare()` with a
> flag that optionally instructs the backend to also flush the caches,
> which allows us to drop the external `odb_source_packed_prepare()`
> declaration.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> builtin/grep.c | 9 +++------
> midx.c | 2 +-
> odb.c | 2 +-
> odb.h | 8 ++++++++
> odb/source-files.c | 9 +++++----
> odb/source-inmemory.c | 5 +++--
> odb/source-loose.c | 8 +++++---
> odb/source-packed.c | 34 ++++++++++++++++------------------
> odb/source-packed.h | 9 ---------
> odb/source.h | 16 +++++++++-------
> packfile.c | 2 +-
> 11 files changed, 52 insertions(+), 52 deletions(-)
>
> diff --git a/builtin/grep.c b/builtin/grep.c
> index 8080d1bf5e..7361bf071e 100644
> --- a/builtin/grep.c
> +++ b/builtin/grep.c
> @@ -25,12 +25,11 @@
> #include "setup.h"
> #include "submodule.h"
> #include "submodule-config.h"
> -#include "object-file.h"
> #include "object-name.h"
> #include "odb.h"
> +#include "odb/source.h"
> #include "oid-array.h"
> #include "oidset.h"
> -#include "packfile.h"
> #include "pager.h"
> #include "path.h"
> #include "promisor-remote.h"
> @@ -1361,10 +1360,8 @@ int cmd_grep(int argc,
> struct odb_source *source;
>
> odb_prepare_alternates(the_repository->objects);
> - for (source = the_repository->objects->sources; source; source = source->next) {
> - struct odb_source_files *files = odb_source_files_downcast(source);
So you're downcasting inside the implementation by the backends itself.
That makes sense, but would it be worth to say something about that in
the commit message?
> - odb_source_packed_prepare(files->packed);
> - }
> + for (source = the_repository->objects->sources; source; source = source->next)
> + odb_source_prepare(source, 0);
> }
>
> start_threads(&opt);
> diff --git a/midx.c b/midx.c
> index cc6b94f9dd..76c3f92cc3 100644
> --- a/midx.c
> +++ b/midx.c
> @@ -101,7 +101,7 @@ static int midx_read_object_offsets(const unsigned char *chunk_start,
>
> struct multi_pack_index *get_multi_pack_index(struct odb_source_packed *source)
> {
> - odb_source_packed_prepare(source);
> + odb_source_prepare(&source->base, 0);
> return source->midx;
> }
>
> diff --git a/odb.c b/odb.c
> index 965ef68e4e..7b45390e12 100644
> --- a/odb.c
> +++ b/odb.c
> @@ -1086,7 +1086,7 @@ void odb_reprepare(struct object_database *o)
> odb_prepare_alternates(o);
>
> for (source = o->sources; source; source = source->next)
> - odb_source_reprepare(source);
> + odb_source_prepare(source, ODB_PREPARE_FLUSH_CACHES);
>
> o->object_count_valid = 0;
>
> diff --git a/odb.h b/odb.h
> index 0030467a52..c14c9030e4 100644
> --- a/odb.h
> +++ b/odb.h
> @@ -124,6 +124,14 @@ void odb_free(struct object_database *o);
> */
> void odb_close(struct object_database *o);
>
> +enum odb_prepare_flags {
> + /*
> + * Flush caches, reload alternates and then re-prepare each object
> + * source so that new objects may become accessible.
> + */
> + ODB_PREPARE_FLUSH_CACHES = (1 << 0),
> +};
> +
> /*
> * Clear caches, reload alternates and then reload object sources so that new
> * objects may become accessible.
> diff --git a/odb/source-files.c b/odb/source-files.c
> index 3bc6419dd7..ad9e0b52f9 100644
> --- a/odb/source-files.c
> +++ b/odb/source-files.c
> @@ -41,11 +41,12 @@ static void odb_source_files_close(struct odb_source *source)
> odb_source_close(&files->packed->base);
> }
>
> -static void odb_source_files_reprepare(struct odb_source *source)
> +static void odb_source_files_prepare(struct odb_source *source,
> + enum odb_prepare_flags flags)
> {
> struct odb_source_files *files = odb_source_files_downcast(source);
> - odb_source_reprepare(&files->loose->base);
> - odb_source_reprepare(&files->packed->base);
> + odb_source_prepare(&files->loose->base, flags);
> + odb_source_prepare(&files->packed->base, flags);
> }
>
> static int odb_source_files_read_object_info(struct odb_source *source,
> @@ -273,7 +274,7 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb,
>
> files->base.free = odb_source_files_free;
> files->base.close = odb_source_files_close;
> - files->base.reprepare = odb_source_files_reprepare;
> + files->base.prepare = odb_source_files_prepare;
> files->base.read_object_info = odb_source_files_read_object_info;
> files->base.read_object_stream = odb_source_files_read_object_stream;
> files->base.for_each_object = odb_source_files_for_each_object;
> diff --git a/odb/source-inmemory.c b/odb/source-inmemory.c
> index e004566d76..cc5e9e62cb 100644
> --- a/odb/source-inmemory.c
> +++ b/odb/source-inmemory.c
> @@ -325,7 +325,8 @@ static void odb_source_inmemory_close(struct odb_source *source UNUSED)
> {
> }
>
> -static void odb_source_inmemory_reprepare(struct odb_source *source UNUSED)
> +static void odb_source_inmemory_prepare(struct odb_source *source UNUSED,
> + enum odb_prepare_flags flags UNUSED)
> {
> }
>
> @@ -365,7 +366,7 @@ struct odb_source_inmemory *odb_source_inmemory_new(struct object_database *odb)
>
> source->base.free = odb_source_inmemory_free;
> source->base.close = odb_source_inmemory_close;
> - source->base.reprepare = odb_source_inmemory_reprepare;
> + source->base.prepare = odb_source_inmemory_prepare;
> source->base.read_object_info = odb_source_inmemory_read_object_info;
> source->base.read_object_stream = odb_source_inmemory_read_object_stream;
> source->base.for_each_object = odb_source_inmemory_for_each_object;
> diff --git a/odb/source-loose.c b/odb/source-loose.c
> index 7d7ea2fb84..af46316e35 100644
> --- a/odb/source-loose.c
> +++ b/odb/source-loose.c
> @@ -672,10 +672,12 @@ static void odb_source_loose_clear_cache(struct odb_source_loose *loose)
> sizeof(loose->subdir_seen));
> }
>
> -static void odb_source_loose_reprepare(struct odb_source *source)
> +static void odb_source_loose_prepare(struct odb_source *source,
> + enum odb_prepare_flags flags)
> {
> struct odb_source_loose *loose = odb_source_loose_downcast(source);
> - odb_source_loose_clear_cache(loose);
> + if (flags & ODB_PREPARE_FLUSH_CACHES)
> + odb_source_loose_clear_cache(loose);
> }
>
> static void odb_source_loose_close(struct odb_source *source UNUSED)
> @@ -716,7 +718,7 @@ struct odb_source_loose *odb_source_loose_new(struct object_database *odb,
>
> loose->base.free = odb_source_loose_free;
> loose->base.close = odb_source_loose_close;
> - loose->base.reprepare = odb_source_loose_reprepare;
> + loose->base.prepare = odb_source_loose_prepare;
> loose->base.read_object_info = odb_source_loose_read_object_info;
> loose->base.read_object_stream = odb_source_loose_read_object_stream;
> loose->base.for_each_object = odb_source_loose_for_each_object;
> diff --git a/odb/source-packed.c b/odb/source-packed.c
> index 42c28fba0e..fa5a072488 100644
> --- a/odb/source-packed.c
> +++ b/odb/source-packed.c
> @@ -15,7 +15,7 @@ static int find_pack_entry(struct odb_source_packed *store,
> {
> struct packfile_list_entry *l;
>
> - odb_source_packed_prepare(store);
> + odb_source_prepare(&store->base, 0);
Why are you not using ODB_PREPARE_FLUSH_CACHES here? It used to do
before?
> if (store->midx && fill_midx_entry(store->midx, oid, e))
> return 1;
>
> @@ -47,7 +47,7 @@ static int odb_source_packed_read_object_info(struct odb_source *source,
> * been added since the last time we have prepared the packfile store.
> */
> if (flags & OBJECT_INFO_SECOND_READ)
> - odb_source_reprepare(source);
> + odb_source_prepare(source, ODB_PREPARE_FLUSH_CACHES);
I think the new code is correct, but why wasn't `packed` used here in
the past? The old odb_source_reprepare() expected a downcasted, didn't
it?
>
> if (!find_pack_entry(packed, oid, &e))
> return 1;
> @@ -668,27 +668,25 @@ static int sort_pack(const struct packfile_list_entry *a,
> return -1;
> }
>
> -void odb_source_packed_prepare(struct odb_source_packed *source)
> +static void odb_source_packed_prepare(struct odb_source *source,
> + enum odb_prepare_flags flags)
> {
> - if (source->initialized)
> + struct odb_source_packed *packed = odb_source_packed_downcast(source);
> +
> + if (flags & ODB_PREPARE_FLUSH_CACHES)
> + packed->initialized = false;
> + if (packed->initialized)
> return;
>
> - prepare_multi_pack_index_one(source);
> - prepare_packed_git_one(source);
> + prepare_multi_pack_index_one(packed);
> + prepare_packed_git_one(packed);
>
> - sort_packs(&source->packs.head, sort_pack);
> - for (struct packfile_list_entry *e = source->packs.head; e; e = e->next)
> + sort_packs(&packed->packs.head, sort_pack);
> + for (struct packfile_list_entry *e = packed->packs.head; e; e = e->next)
> if (!e->next)
> - source->packs.tail = e;
> + packed->packs.tail = e;
>
> - source->initialized = true;
> -}
> -
> -static void odb_source_packed_reprepare(struct odb_source *source)
> -{
> - struct odb_source_packed *packed = odb_source_packed_downcast(source);
> - packed->initialized = false;
> - odb_source_packed_prepare(packed);
> + packed->initialized = true;
> }
>
> static void odb_source_packed_reparent(const char *name UNUSED,
> @@ -744,7 +742,7 @@ struct odb_source_packed *odb_source_packed_new(struct object_database *odb,
>
> packed->base.free = odb_source_packed_free;
> packed->base.close = odb_source_packed_close;
> - packed->base.reprepare = odb_source_packed_reprepare;
> + packed->base.prepare = odb_source_packed_prepare;
> packed->base.read_object_info = odb_source_packed_read_object_info;
> packed->base.read_object_stream = odb_source_packed_read_object_stream;
> packed->base.for_each_object = odb_source_packed_for_each_object;
> diff --git a/odb/source-packed.h b/odb/source-packed.h
> index 88994098c1..d5230ac68c 100644
> --- a/odb/source-packed.h
> +++ b/odb/source-packed.h
> @@ -82,13 +82,4 @@ static inline struct odb_source_packed *odb_source_packed_downcast(struct odb_so
> return container_of(source, struct odb_source_packed, base);
> }
>
> -/*
> - * Prepare the source by loading packfiles and multi-pack indices for
> - * all alternates. This becomes a no-op if the source is already prepared.
> - *
> - * It shouldn't typically be necessary to call this function directly, as
> - * functions that access the source know to prepare it.
> - */
> -void odb_source_packed_prepare(struct odb_source_packed *source);
> -
> #endif
> diff --git a/odb/source.h b/odb/source.h
> index b9a7642b2c..bbf1da3819 100644
> --- a/odb/source.h
> +++ b/odb/source.h
> @@ -83,11 +83,12 @@ struct odb_source {
> void (*close)(struct odb_source *source);
>
> /*
> - * This callback is expected to clear underlying caches of the object
> - * database source. The function is called when the repository has for
> - * example just been repacked so that new objects will become visible.
> + * This callback is expected to prepare the source so that it becomes
> + * ready for use. It optionally clears underlying caches of the object
> + * database source.
> */
> - void (*reprepare)(struct odb_source *source);
> + void (*prepare)(struct odb_source *source,
> + enum odb_prepare_flags flags);
>
> /*
> * This callback is expected to read object information from the object
> @@ -308,13 +309,14 @@ static inline void odb_source_close(struct odb_source *source)
> }
>
> /*
> - * Reprepare the object database source and clear any caches. Depending on the
> + * Prepare the object database source and clear any caches. Depending on the
> * backend used this may have the effect that concurrently-written objects
> * become visible.
> */
> -static inline void odb_source_reprepare(struct odb_source *source)
> +static inline void odb_source_prepare(struct odb_source *source,
> + enum odb_prepare_flags flags)
> {
> - source->reprepare(source);
> + source->prepare(source, flags);
> }
>
> /*
> diff --git a/packfile.c b/packfile.c
> index 59cee7925d..d78fae981a 100644
> --- a/packfile.c
> +++ b/packfile.c
> @@ -855,7 +855,7 @@ void for_each_file_in_pack_dir(const char *objdir,
>
> struct packfile_list_entry *packfile_store_get_packs(struct odb_source_packed *store)
> {
> - odb_source_packed_prepare(store);
> + odb_source_prepare(&store->base, 0);
>
> if (store->midx) {
> struct multi_pack_index *m = store->midx;
>
> --
> 2.55.0.rc1.745.g43192e7977.dirty
>
>
--
Cheers,
Toon
next prev parent reply other threads:[~2026-06-26 12:10 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-22 8:47 [PATCH 0/2] odb: generalize `reprepare()` callback Patrick Steinhardt
2026-06-22 8:47 ` [PATCH 1/2] odb/source: " Patrick Steinhardt
2026-06-26 12:10 ` Toon Claes [this message]
2026-06-22 8:47 ` [PATCH 2/2] odb: introduce `odb_prepare()` Patrick Steinhardt
2026-06-26 12:09 ` Toon Claes
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87ldc14i4n.fsf@emacs.iotcl.com \
--to=toon@iotcl.com \
--cc=git@vger.kernel.org \
--cc=ps@pks.im \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox