From bd925d865e4edaa0004f7995ea80b58e8fbaaec0 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 6 Dec 2025 12:27:57 +0100 Subject: [PATCH 1/9] Refactor handling of alternates to work via sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hi, this patch series refactors how we handle alternate object directories so that the interface is structured around the object database source. Next to being simpler to reason about, it also allows us to eventually abstract handling of alternates to use different mechanisms based on the specific backend used. In a world of pluggable object databases not every backend may use a physical directory, so it may not be possible to read alternates via "objects/info/alternates". Consequently, formats may need a different mechanism entirely to make this list available. Changes in v3: - Fix commit messages that still refer to `odb_add_source()`. - Fix intermediate commit that still refers to `odb_add_source()`. - Link to v2: https://lore.kernel.org/r/20251210-b4-pks-odb-alternates-via-source-v2-0-eb336815f9ab@pks.im Changes in v2: - Rename `odb_add_source()` to `odb_add_alternates_recursive()` to highlight that this function is recursive. - Link to v1: https://lore.kernel.org/r/20251208-b4-pks-odb-alternates-via-source-v1-0-e7ebb8b18c03@pks.im Thanks! Patrick To: git@vger.kernel.org Cc: Justin Tobler Cc: SZEDER Gábor --- b4-submit-tracking --- # This section is used internally by b4 prep for tracking purposes. { "series": { "revision": 3, "change-id": "20251206-b4-pks-odb-alternates-via-source-802d87cbbda5", "prefixes": [], "history": { "v1": [ "20251208-b4-pks-odb-alternates-via-source-v1-0-e7ebb8b18c03@pks.im" ], "v2": [ "20251210-b4-pks-odb-alternates-via-source-v2-0-eb336815f9ab@pks.im" ] } } } -- GitLab From 4a85139a75b5e65cd9112116f1b53785b594519b Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 6 Dec 2025 10:37:23 +0100 Subject: [PATCH 2/9] odb: refactor parsing of alternates to be self-contained Parsing of the alternates file and environment variable is currently split up across multiple different functions and is entangled with `link_alt_odb_entries()`, which is responsible for linking the parsed object database sources. This results in two downsides: - We have mutual recursion between parsing alternates and linking them into the object database. This is because we also parse alternates that the newly added sources may have. - We mix up the actual logic to parse the data and to link them into place. Refactor the logic so that parsing of the alternates file is entirely self-contained. Note that this doesn't yet fix the above two issues, but it is a necessary step to get there. Signed-off-by: Patrick Steinhardt --- odb.c | 70 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/odb.c b/odb.c index dc8f292f3d..9785f62cb6 100644 --- a/odb.c +++ b/odb.c @@ -216,39 +216,50 @@ static struct odb_source *link_alt_odb_entry(struct object_database *odb, return alternate; } -static const char *parse_alt_odb_entry(const char *string, - int sep, - struct strbuf *out) +static void parse_alternates(const char *string, + int sep, + struct strvec *out) { - const char *end; + struct strbuf buf = STRBUF_INIT; - strbuf_reset(out); + while (*string) { + const char *end; + + strbuf_reset(&buf); + + if (*string == '#') { + /* comment; consume up to next separator */ + end = strchrnul(string, sep); + } else if (*string == '"' && !unquote_c_style(&buf, string, &end)) { + /* + * quoted path; unquote_c_style has copied the + * data for us and set "end". Broken quoting (e.g., + * an entry that doesn't end with a quote) falls + * back to the unquoted case below. + */ + } else { + /* normal, unquoted path */ + end = strchrnul(string, sep); + strbuf_add(&buf, string, end - string); + } - if (*string == '#') { - /* comment; consume up to next separator */ - end = strchrnul(string, sep); - } else if (*string == '"' && !unquote_c_style(out, string, &end)) { - /* - * quoted path; unquote_c_style has copied the - * data for us and set "end". Broken quoting (e.g., - * an entry that doesn't end with a quote) falls - * back to the unquoted case below. - */ - } else { - /* normal, unquoted path */ - end = strchrnul(string, sep); - strbuf_add(out, string, end - string); + if (*end) + end++; + string = end; + + if (!buf.len) + continue; + + strvec_push(out, buf.buf); } - if (*end) - end++; - return end; + strbuf_release(&buf); } static void link_alt_odb_entries(struct object_database *odb, const char *alt, int sep, const char *relative_base, int depth) { - struct strbuf dir = STRBUF_INIT; + struct strvec alternates = STRVEC_INIT; if (!alt || !*alt) return; @@ -259,13 +270,12 @@ static void link_alt_odb_entries(struct object_database *odb, const char *alt, return; } - while (*alt) { - alt = parse_alt_odb_entry(alt, sep, &dir); - if (!dir.len) - continue; - link_alt_odb_entry(odb, dir.buf, relative_base, depth); - } - strbuf_release(&dir); + parse_alternates(alt, sep, &alternates); + + for (size_t i = 0; i < alternates.nr; i++) + link_alt_odb_entry(odb, alternates.v[i], relative_base, depth); + + strvec_clear(&alternates); } static void read_info_alternates(struct object_database *odb, -- GitLab From 1b16c0a164dd37762b93ba05b2115f8eb362ea17 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 6 Dec 2025 10:54:20 +0100 Subject: [PATCH 3/9] odb: resolve relative alternative paths when parsing Parsing alternates and resolving potential relative paths is currently handled in two separate steps. This has the effect that the logic to retrieve alternates is not entirely self-contained. We want it to be just that though so that we can eventually move the logic to list alternates into the `struct odb_source`. Move the logic to resolve relative alternative paths into `parse_alternates()`. Besides bringing us a step closer towards the above goal, it also neatly separates concerns of generating the list of alternatives and linking them into the object database. Note that we ignore any errors when the relative path cannot be resolved. This isn't really a change in behaviour though: if the path cannot be resolved to a directory then `alt_odb_usable()` still knows to bail out. While at it, rename the function to `odb_add_alternate_recursively()` to more clearly indicate what its intent is and to align it with modern terminology. Signed-off-by: Patrick Steinhardt --- odb.c | 64 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/odb.c b/odb.c index 9785f62cb6..699bdbffd1 100644 --- a/odb.c +++ b/odb.c @@ -159,44 +159,21 @@ static struct odb_source *odb_source_new(struct object_database *odb, return source; } -static struct odb_source *link_alt_odb_entry(struct object_database *odb, - const char *dir, - const char *relative_base, - int depth) +static struct odb_source *odb_add_alternate_recursively(struct object_database *odb, + const char *source, + int depth) { struct odb_source *alternate = NULL; - struct strbuf pathbuf = STRBUF_INIT; struct strbuf tmp = STRBUF_INIT; khiter_t pos; int ret; - if (!is_absolute_path(dir) && relative_base) { - strbuf_realpath(&pathbuf, relative_base, 1); - strbuf_addch(&pathbuf, '/'); - } - strbuf_addstr(&pathbuf, dir); - - if (!strbuf_realpath(&tmp, pathbuf.buf, 0)) { - error(_("unable to normalize alternate object path: %s"), - pathbuf.buf); - goto error; - } - strbuf_swap(&pathbuf, &tmp); - - /* - * The trailing slash after the directory name is given by - * this function at the end. Remove duplicates. - */ - while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/') - strbuf_setlen(&pathbuf, pathbuf.len - 1); - - strbuf_reset(&tmp); strbuf_realpath(&tmp, odb->sources->path, 1); - if (!alt_odb_usable(odb, pathbuf.buf, tmp.buf)) + if (!alt_odb_usable(odb, source, tmp.buf)) goto error; - alternate = odb_source_new(odb, pathbuf.buf, false); + alternate = odb_source_new(odb, source, false); /* add the alternate entry */ *odb->sources_tail = alternate; @@ -212,20 +189,22 @@ static struct odb_source *link_alt_odb_entry(struct object_database *odb, error: strbuf_release(&tmp); - strbuf_release(&pathbuf); return alternate; } static void parse_alternates(const char *string, int sep, + const char *relative_base, struct strvec *out) { + struct strbuf pathbuf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT; while (*string) { const char *end; strbuf_reset(&buf); + strbuf_reset(&pathbuf); if (*string == '#') { /* comment; consume up to next separator */ @@ -250,9 +229,30 @@ static void parse_alternates(const char *string, if (!buf.len) continue; + if (!is_absolute_path(buf.buf) && relative_base) { + strbuf_realpath(&pathbuf, relative_base, 1); + strbuf_addch(&pathbuf, '/'); + } + strbuf_addbuf(&pathbuf, &buf); + + strbuf_reset(&buf); + if (!strbuf_realpath(&buf, pathbuf.buf, 0)) { + error(_("unable to normalize alternate object path: %s"), + pathbuf.buf); + continue; + } + + /* + * The trailing slash after the directory name is given by + * this function at the end. Remove duplicates. + */ + while (buf.len && buf.buf[buf.len - 1] == '/') + strbuf_setlen(&buf, buf.len - 1); + strvec_push(out, buf.buf); } + strbuf_release(&pathbuf); strbuf_release(&buf); } @@ -270,10 +270,10 @@ static void link_alt_odb_entries(struct object_database *odb, const char *alt, return; } - parse_alternates(alt, sep, &alternates); + parse_alternates(alt, sep, relative_base, &alternates); for (size_t i = 0; i < alternates.nr; i++) - link_alt_odb_entry(odb, alternates.v[i], relative_base, depth); + odb_add_alternate_recursively(odb, alternates.v[i], depth); strvec_clear(&alternates); } @@ -348,7 +348,7 @@ struct odb_source *odb_add_to_alternates_memory(struct object_database *odb, * overwritten when they are. */ odb_prepare_alternates(odb); - return link_alt_odb_entry(odb, dir, NULL, 0); + return odb_add_alternate_recursively(odb, dir, 0); } struct odb_source *odb_set_temporary_primary_source(struct object_database *odb, -- GitLab From ceb6e8494cb5b9ea04008ead36ac9a1d59c0c56e Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 6 Dec 2025 11:01:31 +0100 Subject: [PATCH 4/9] odb: move computation of normalized objdir into `alt_odb_usable()` The function `alt_odb_usable()` receives as input the object database, the path it's supposed to determine usability for as well as the normalized path of the main object directory of the repository. The last part is derived by the function's caller from the object database. As we already pass the object database to `alt_odb_usable()` it is redundant information. Drop the extra parameter and compute the normalized object directory in the function itself. While at it, rename the function to `odb_is_source_usable()` to align it with modern terminology. Signed-off-by: Patrick Steinhardt --- odb.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/odb.c b/odb.c index 699bdbffd1..e314f86c3b 100644 --- a/odb.c +++ b/odb.c @@ -89,17 +89,20 @@ int odb_mkstemp(struct object_database *odb, /* * Return non-zero iff the path is usable as an alternate object database. */ -static int alt_odb_usable(struct object_database *o, const char *path, - const char *normalized_objdir) +static bool odb_is_source_usable(struct object_database *o, const char *path) { int r; + struct strbuf normalized_objdir = STRBUF_INIT; + bool usable = false; + + strbuf_realpath(&normalized_objdir, o->sources->path, 1); /* Detect cases where alternate disappeared */ if (!is_directory(path)) { error(_("object directory %s does not exist; " "check .git/objects/info/alternates"), path); - return 0; + goto out; } /* @@ -116,13 +119,17 @@ static int alt_odb_usable(struct object_database *o, const char *path, kh_value(o->source_by_path, p) = o->sources; } - if (fspatheq(path, normalized_objdir)) - return 0; + if (fspatheq(path, normalized_objdir.buf)) + goto out; if (kh_get_odb_path_map(o->source_by_path, path) < kh_end(o->source_by_path)) - return 0; + goto out; + + usable = true; - return 1; +out: + strbuf_release(&normalized_objdir); + return usable; } /* @@ -164,13 +171,10 @@ static struct odb_source *odb_add_alternate_recursively(struct object_database * int depth) { struct odb_source *alternate = NULL; - struct strbuf tmp = STRBUF_INIT; khiter_t pos; int ret; - strbuf_realpath(&tmp, odb->sources->path, 1); - - if (!alt_odb_usable(odb, source, tmp.buf)) + if (!odb_is_source_usable(odb, source)) goto error; alternate = odb_source_new(odb, source, false); @@ -188,7 +192,6 @@ static struct odb_source *odb_add_alternate_recursively(struct object_database * read_info_alternates(odb, alternate->path, depth + 1); error: - strbuf_release(&tmp); return alternate; } -- GitLab From 99dbd11c486499d5adf9ddcc358c5e4b2e0831e9 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 6 Dec 2025 11:17:02 +0100 Subject: [PATCH 5/9] odb: stop splitting alternate in `odb_add_to_alternates_file()` When calling `odb_add_to_alternates_file()` we know to add the newly added source to the object database in case we have already loaded alternates. This is done so that we can make its objects accessible immediately without having to fully reload all alternates. The way we do this though is to call `link_alt_odb_entries()`, which adds _multiple_ sources to the object database source in case we have newline-separated entries. This behaviour is not documented in the function documentation of `odb_add_to_alternates_file()`, and all callers only ever pass a single directory to it. It's thus entirely surprising and a conceptual mismatch. Fix this issue by directly calling `odb_add_alternate_recursively()` instead. Signed-off-by: Patrick Steinhardt --- odb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odb.c b/odb.c index e314f86c3b..3112eab5d0 100644 --- a/odb.c +++ b/odb.c @@ -338,7 +338,7 @@ void odb_add_to_alternates_file(struct object_database *odb, if (commit_lock_file(&lock)) die_errno(_("unable to move new alternates file into place")); if (odb->loaded_alternates) - link_alt_odb_entries(odb, dir, '\n', NULL, 0); + odb_add_alternate_recursively(odb, dir, 0); } free(alts); } -- GitLab From b9300667a6a3a2c0948e73cb2ceed107945c0503 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 9 Dec 2025 08:41:52 +0100 Subject: [PATCH 6/9] odb: remove mutual recursion when parsing alternates When adding an alternative object database source we not only have to consider the added source itself, but we also have to add _its_ sources to our database. We implement this via mutual recursion: 1. We first call `link_alt_odb_entries()`. 2. `link_alt_odb_entries()` calls `parse_alternates()`. 3. We then add each alternate via `odb_add_alternate_recursively()`. 4. `odb_add_alternate_recursively()` calls `link_alt_odb_entries()` again. This flow is somewhat hard to follow, but more importantly it means that parsing of alternates is somewhat tied to the recursive behaviour. Refactor the function to remove the mutual recursion between adding sources and parsing alternates. The parsing step thus becomes completely oblivious to the fact that there is recursive behaviour going on at all. The recursion is handled by `odb_add_alternate_recursively()` instead, which now recurses with itself. This refactoring allows us to move parsing of alternates into object database sources in a subsequent step. Signed-off-by: Patrick Steinhardt --- odb.c | 60 +++++++++++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/odb.c b/odb.c index 3112eab5d0..59944d4649 100644 --- a/odb.c +++ b/odb.c @@ -147,9 +147,8 @@ static bool odb_is_source_usable(struct object_database *o, const char *path) * of the object ID, an extra slash for the first level indirection, and * the terminating NUL. */ -static void read_info_alternates(struct object_database *odb, - const char *relative_base, - int depth); +static void read_info_alternates(const char *relative_base, + struct strvec *out); static struct odb_source *odb_source_new(struct object_database *odb, const char *path, @@ -171,6 +170,7 @@ static struct odb_source *odb_add_alternate_recursively(struct object_database * int depth) { struct odb_source *alternate = NULL; + struct strvec sources = STRVEC_INIT; khiter_t pos; int ret; @@ -189,9 +189,17 @@ static struct odb_source *odb_add_alternate_recursively(struct object_database * kh_value(odb->source_by_path, pos) = alternate; /* recursively add alternates */ - read_info_alternates(odb, alternate->path, depth + 1); + read_info_alternates(alternate->path, &sources); + if (sources.nr && depth + 1 > 5) { + error(_("%s: ignoring alternate object stores, nesting too deep"), + source); + } else { + for (size_t i = 0; i < sources.nr; i++) + odb_add_alternate_recursively(odb, sources.v[i], depth + 1); + } error: + strvec_clear(&sources); return alternate; } @@ -203,6 +211,9 @@ static void parse_alternates(const char *string, struct strbuf pathbuf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT; + if (!string || !*string) + return; + while (*string) { const char *end; @@ -259,34 +270,11 @@ static void parse_alternates(const char *string, strbuf_release(&buf); } -static void link_alt_odb_entries(struct object_database *odb, const char *alt, - int sep, const char *relative_base, int depth) +static void read_info_alternates(const char *relative_base, + struct strvec *out) { - struct strvec alternates = STRVEC_INIT; - - if (!alt || !*alt) - return; - - if (depth > 5) { - error(_("%s: ignoring alternate object stores, nesting too deep"), - relative_base); - return; - } - - parse_alternates(alt, sep, relative_base, &alternates); - - for (size_t i = 0; i < alternates.nr; i++) - odb_add_alternate_recursively(odb, alternates.v[i], depth); - - strvec_clear(&alternates); -} - -static void read_info_alternates(struct object_database *odb, - const char *relative_base, - int depth) -{ - char *path; struct strbuf buf = STRBUF_INIT; + char *path; path = xstrfmt("%s/info/alternates", relative_base); if (strbuf_read_file(&buf, path, 1024) < 0) { @@ -294,8 +282,8 @@ static void read_info_alternates(struct object_database *odb, free(path); return; } + parse_alternates(buf.buf, '\n', relative_base, out); - link_alt_odb_entries(odb, buf.buf, '\n', relative_base, depth); strbuf_release(&buf); free(path); } @@ -622,13 +610,19 @@ int odb_for_each_alternate(struct object_database *odb, void odb_prepare_alternates(struct object_database *odb) { + struct strvec sources = STRVEC_INIT; + if (odb->loaded_alternates) return; - link_alt_odb_entries(odb, odb->alternate_db, PATH_SEP, NULL, 0); + parse_alternates(odb->alternate_db, PATH_SEP, NULL, &sources); + read_info_alternates(odb->sources->path, &sources); + for (size_t i = 0; i < sources.nr; i++) + odb_add_alternate_recursively(odb, sources.v[i], 0); - read_info_alternates(odb, odb->sources->path, 0); odb->loaded_alternates = 1; + + strvec_clear(&sources); } int odb_has_alternates(struct object_database *odb) -- GitLab From 1e3a1fb08197b90f0ea5e22d733ed838ed15be8f Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 9 Dec 2025 08:43:10 +0100 Subject: [PATCH 7/9] odb: drop forward declaration of `read_info_alternates()` Now that we have removed the mutual recursion in the preceding commit it is not necessary anymore to have a forward declaration of the `read_info_alternates()` function. Move the function and its dependencies further up so that we can remove it. Note that this commit also removes the function documentation of `read_info_alternates()`. It's unclear what it's documenting, but it for sure isn't documenting the modern behaviour of the function anymore. Signed-off-by: Patrick Steinhardt --- odb.c | 125 +++++++++++++++++++++++++--------------------------------- 1 file changed, 54 insertions(+), 71 deletions(-) diff --git a/odb.c b/odb.c index 59944d4649..dcf4a62cd2 100644 --- a/odb.c +++ b/odb.c @@ -132,77 +132,6 @@ static bool odb_is_source_usable(struct object_database *o, const char *path) return usable; } -/* - * Prepare alternate object database registry. - * - * The variable alt_odb_list points at the list of struct - * odb_source. The elements on this list come from - * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT - * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates, - * whose contents is similar to that environment variable but can be - * LF separated. Its base points at a statically allocated buffer that - * contains "/the/directory/corresponding/to/.git/objects/...", while - * its name points just after the slash at the end of ".git/objects/" - * in the example above, and has enough space to hold all hex characters - * of the object ID, an extra slash for the first level indirection, and - * the terminating NUL. - */ -static void read_info_alternates(const char *relative_base, - struct strvec *out); - -static struct odb_source *odb_source_new(struct object_database *odb, - const char *path, - bool local) -{ - struct odb_source *source; - - CALLOC_ARRAY(source, 1); - source->odb = odb; - source->local = local; - source->path = xstrdup(path); - source->loose = odb_source_loose_new(source); - - return source; -} - -static struct odb_source *odb_add_alternate_recursively(struct object_database *odb, - const char *source, - int depth) -{ - struct odb_source *alternate = NULL; - struct strvec sources = STRVEC_INIT; - khiter_t pos; - int ret; - - if (!odb_is_source_usable(odb, source)) - goto error; - - alternate = odb_source_new(odb, source, false); - - /* add the alternate entry */ - *odb->sources_tail = alternate; - odb->sources_tail = &(alternate->next); - - pos = kh_put_odb_path_map(odb->source_by_path, alternate->path, &ret); - if (!ret) - BUG("source must not yet exist"); - kh_value(odb->source_by_path, pos) = alternate; - - /* recursively add alternates */ - read_info_alternates(alternate->path, &sources); - if (sources.nr && depth + 1 > 5) { - error(_("%s: ignoring alternate object stores, nesting too deep"), - source); - } else { - for (size_t i = 0; i < sources.nr; i++) - odb_add_alternate_recursively(odb, sources.v[i], depth + 1); - } - - error: - strvec_clear(&sources); - return alternate; -} - static void parse_alternates(const char *string, int sep, const char *relative_base, @@ -288,6 +217,60 @@ static void read_info_alternates(const char *relative_base, free(path); } + +static struct odb_source *odb_source_new(struct object_database *odb, + const char *path, + bool local) +{ + struct odb_source *source; + + CALLOC_ARRAY(source, 1); + source->odb = odb; + source->local = local; + source->path = xstrdup(path); + source->loose = odb_source_loose_new(source); + + return source; +} + +static struct odb_source *odb_add_alternate_recursively(struct object_database *odb, + const char *source, + int depth) +{ + struct odb_source *alternate = NULL; + struct strvec sources = STRVEC_INIT; + khiter_t pos; + int ret; + + if (!odb_is_source_usable(odb, source)) + goto error; + + alternate = odb_source_new(odb, source, false); + + /* add the alternate entry */ + *odb->sources_tail = alternate; + odb->sources_tail = &(alternate->next); + + pos = kh_put_odb_path_map(odb->source_by_path, alternate->path, &ret); + if (!ret) + BUG("source must not yet exist"); + kh_value(odb->source_by_path, pos) = alternate; + + /* recursively add alternates */ + read_info_alternates(alternate->path, &sources); + if (sources.nr && depth + 1 > 5) { + error(_("%s: ignoring alternate object stores, nesting too deep"), + source); + } else { + for (size_t i = 0; i < sources.nr; i++) + odb_add_alternate_recursively(odb, sources.v[i], depth + 1); + } + + error: + strvec_clear(&sources); + return alternate; +} + void odb_add_to_alternates_file(struct object_database *odb, const char *dir) { -- GitLab From 1d6a9b3c1bade62e598075295e503697f7bb9be1 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 6 Dec 2025 11:46:55 +0100 Subject: [PATCH 8/9] odb: read alternates via sources Adapt how we read alternates so that the interface is structured around the object database source we're reading from. This will eventually allow us to abstract away this behaviour with pluggable object databases so that every format can have its own mechanism for listing alternates. Signed-off-by: Patrick Steinhardt --- odb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/odb.c b/odb.c index dcf4a62cd2..c5ba26b85f 100644 --- a/odb.c +++ b/odb.c @@ -199,19 +199,19 @@ static void parse_alternates(const char *string, strbuf_release(&buf); } -static void read_info_alternates(const char *relative_base, - struct strvec *out) +static void odb_source_read_alternates(struct odb_source *source, + struct strvec *out) { struct strbuf buf = STRBUF_INIT; char *path; - path = xstrfmt("%s/info/alternates", relative_base); + path = xstrfmt("%s/info/alternates", source->path); if (strbuf_read_file(&buf, path, 1024) < 0) { warn_on_fopen_errors(path); free(path); return; } - parse_alternates(buf.buf, '\n', relative_base, out); + parse_alternates(buf.buf, '\n', source->path, out); strbuf_release(&buf); free(path); @@ -257,7 +257,7 @@ static struct odb_source *odb_add_alternate_recursively(struct object_database * kh_value(odb->source_by_path, pos) = alternate; /* recursively add alternates */ - read_info_alternates(alternate->path, &sources); + odb_source_read_alternates(alternate, &sources); if (sources.nr && depth + 1 > 5) { error(_("%s: ignoring alternate object stores, nesting too deep"), source); @@ -599,7 +599,7 @@ void odb_prepare_alternates(struct object_database *odb) return; parse_alternates(odb->alternate_db, PATH_SEP, NULL, &sources); - read_info_alternates(odb->sources->path, &sources); + odb_source_read_alternates(odb->sources, &sources); for (size_t i = 0; i < sources.nr; i++) odb_add_alternate_recursively(odb, sources.v[i], 0); -- GitLab From 79a053fb2be9f1d27854c25189ef724bff0e7a18 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Tue, 9 Dec 2025 08:43:35 +0100 Subject: [PATCH 9/9] odb: write alternates via sources Refactor writing of alternates so that the actual business logic is structured around the object database source we want to write the alternate to. Same as with the preceding commit, this will eventually allow us to have different logic for writing alternates depending on the backend used. Note that after the refactoring we start to call `odb_add_alternate_recursively()` unconditionally. This is fine though as we know to skip adding sources that are tracked already. Signed-off-by: Patrick Steinhardt --- odb.c | 51 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/odb.c b/odb.c index c5ba26b85f..cc7f832465 100644 --- a/odb.c +++ b/odb.c @@ -271,25 +271,28 @@ static struct odb_source *odb_add_alternate_recursively(struct object_database * return alternate; } -void odb_add_to_alternates_file(struct object_database *odb, - const char *dir) +static int odb_source_write_alternate(struct odb_source *source, + const char *alternate) { struct lock_file lock = LOCK_INIT; - char *alts = repo_git_path(odb->repo, "objects/info/alternates"); + char *path = xstrfmt("%s/%s", source->path, "info/alternates"); FILE *in, *out; int found = 0; + int ret; - hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR); + hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); out = fdopen_lock_file(&lock, "w"); - if (!out) - die_errno(_("unable to fdopen alternates lockfile")); + if (!out) { + ret = error_errno(_("unable to fdopen alternates lockfile")); + goto out; + } - in = fopen(alts, "r"); + in = fopen(path, "r"); if (in) { struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, in) != EOF) { - if (!strcmp(dir, line.buf)) { + if (!strcmp(alternate, line.buf)) { found = 1; break; } @@ -298,20 +301,36 @@ void odb_add_to_alternates_file(struct object_database *odb, strbuf_release(&line); fclose(in); + } else if (errno != ENOENT) { + ret = error_errno(_("unable to read alternates file")); + goto out; } - else if (errno != ENOENT) - die_errno(_("unable to read alternates file")); if (found) { rollback_lock_file(&lock); } else { - fprintf_or_die(out, "%s\n", dir); - if (commit_lock_file(&lock)) - die_errno(_("unable to move new alternates file into place")); - if (odb->loaded_alternates) - odb_add_alternate_recursively(odb, dir, 0); + fprintf_or_die(out, "%s\n", alternate); + if (commit_lock_file(&lock)) { + ret = error_errno(_("unable to move new alternates file into place")); + goto out; + } } - free(alts); + + ret = 0; + +out: + free(path); + return ret; +} + +void odb_add_to_alternates_file(struct object_database *odb, + const char *dir) +{ + int ret = odb_source_write_alternate(odb->sources, dir); + if (ret < 0) + die(NULL); + if (odb->loaded_alternates) + odb_add_alternate_recursively(odb, dir, 0); } struct odb_source *odb_add_to_alternates_memory(struct object_database *odb, -- GitLab