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 <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Dec 11, 2025 at 10:30 UTC 84cec5276e70bdabd651a3d0a250d006434d639f
1 file changed +32 -32
odb.c
+32 -32
@@ -159,44 +159,21 @@ static struct odb_source *odb_source_new(struct object_database *odb,
159 return source;
160 }
161
162 -static struct odb_source *link_alt_odb_entry(struct object_database *odb,
163 - const char *dir,
164 - const char *relative_base,
165 - int depth)
162 +static struct odb_source *odb_add_alternate_recursively(struct object_database *odb,
163 + const char *source,
164 + int depth)
165 {
166 struct odb_source *alternate = NULL;
168 - struct strbuf pathbuf = STRBUF_INIT;
167 struct strbuf tmp = STRBUF_INIT;
168 khiter_t pos;
169 int ret;
170
173 - if (!is_absolute_path(dir) && relative_base) {
174 - strbuf_realpath(&pathbuf, relative_base, 1);
175 - strbuf_addch(&pathbuf, '/');
176 - }
177 - strbuf_addstr(&pathbuf, dir);
178 -
179 - if (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {
180 - error(_("unable to normalize alternate object path: %s"),
181 - pathbuf.buf);
182 - goto error;
183 - }
184 - strbuf_swap(&pathbuf, &tmp);
185 -
186 - /*
187 - * The trailing slash after the directory name is given by
188 - * this function at the end. Remove duplicates.
189 - */
190 - while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
191 - strbuf_setlen(&pathbuf, pathbuf.len - 1);
192 -
193 - strbuf_reset(&tmp);
171 strbuf_realpath(&tmp, odb->sources->path, 1);
172
196 - if (!alt_odb_usable(odb, pathbuf.buf, tmp.buf))
173 + if (!alt_odb_usable(odb, source, tmp.buf))
174 goto error;
175
199 - alternate = odb_source_new(odb, pathbuf.buf, false);
176 + alternate = odb_source_new(odb, source, false);
177
178 /* add the alternate entry */
179 *odb->sources_tail = alternate;
@@ -212,20 +189,22 @@ static struct odb_source *link_alt_odb_entry(struct object_database *odb,
189
190 error:
191 strbuf_release(&tmp);
215 - strbuf_release(&pathbuf);
192 return alternate;
193 }
194
195 static void parse_alternates(const char *string,
196 int sep,
197 + const char *relative_base,
198 struct strvec *out)
199 {
200 + struct strbuf pathbuf = STRBUF_INIT;
201 struct strbuf buf = STRBUF_INIT;
202
203 while (*string) {
204 const char *end;
205
206 strbuf_reset(&buf);
207 + strbuf_reset(&pathbuf);
208
209 if (*string == '#') {
210 /* comment; consume up to next separator */
@@ -250,9 +229,30 @@ static void parse_alternates(const char *string,
229 if (!buf.len)
230 continue;
231
232 + if (!is_absolute_path(buf.buf) && relative_base) {
233 + strbuf_realpath(&pathbuf, relative_base, 1);
234 + strbuf_addch(&pathbuf, '/');
235 + }
236 + strbuf_addbuf(&pathbuf, &buf);
237 +
238 + strbuf_reset(&buf);
239 + if (!strbuf_realpath(&buf, pathbuf.buf, 0)) {
240 + error(_("unable to normalize alternate object path: %s"),
241 + pathbuf.buf);
242 + continue;
243 + }
244 +
245 + /*
246 + * The trailing slash after the directory name is given by
247 + * this function at the end. Remove duplicates.
248 + */
249 + while (buf.len && buf.buf[buf.len - 1] == '/')
250 + strbuf_setlen(&buf, buf.len - 1);
251 +
252 strvec_push(out, buf.buf);
253 }
254
255 + strbuf_release(&pathbuf);
256 strbuf_release(&buf);
257 }
258
@@ -270,10 +270,10 @@ static void link_alt_odb_entries(struct object_database *odb, const char *alt,
270 return;
271 }
272
273 - parse_alternates(alt, sep, &alternates);
273 + parse_alternates(alt, sep, relative_base, &alternates);
274
275 for (size_t i = 0; i < alternates.nr; i++)
276 - link_alt_odb_entry(odb, alternates.v[i], relative_base, depth);
276 + odb_add_alternate_recursively(odb, alternates.v[i], depth);
277
278 strvec_clear(&alternates);
279 }
@@ -348,7 +348,7 @@ struct odb_source *odb_add_to_alternates_memory(struct object_database *odb,
348 * overwritten when they are.
349 */
350 odb_prepare_alternates(odb);
351 - return link_alt_odb_entry(odb, dir, NULL, 0);
351 + return odb_add_alternate_recursively(odb, dir, 0);
352 }
353
354 struct odb_source *odb_set_temporary_primary_source(struct object_database *odb,