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

Patrick Steinhardt committed Dec 11, 2025 at 10:30 UTC 1660496fc400b3956b4abe7bfc40351c9eddc168
1 file changed +40 -30
odb.c
+40 -30
@@ -216,39 +216,50 @@ static struct odb_source *link_alt_odb_entry(struct object_database *odb,
216 return alternate;
217 }
218
219 -static const char *parse_alt_odb_entry(const char *string,
220 - int sep,
221 - struct strbuf *out)
219 +static void parse_alternates(const char *string,
220 + int sep,
221 + struct strvec *out)
222 {
223 - const char *end;
223 + struct strbuf buf = STRBUF_INIT;
224
225 - strbuf_reset(out);
225 + while (*string) {
226 + const char *end;
227 +
228 + strbuf_reset(&buf);
229 +
230 + if (*string == '#') {
231 + /* comment; consume up to next separator */
232 + end = strchrnul(string, sep);
233 + } else if (*string == '"' && !unquote_c_style(&buf, string, &end)) {
234 + /*
235 + * quoted path; unquote_c_style has copied the
236 + * data for us and set "end". Broken quoting (e.g.,
237 + * an entry that doesn't end with a quote) falls
238 + * back to the unquoted case below.
239 + */
240 + } else {
241 + /* normal, unquoted path */
242 + end = strchrnul(string, sep);
243 + strbuf_add(&buf, string, end - string);
244 + }
245
227 - if (*string == '#') {
228 - /* comment; consume up to next separator */
229 - end = strchrnul(string, sep);
230 - } else if (*string == '"' && !unquote_c_style(out, string, &end)) {
231 - /*
232 - * quoted path; unquote_c_style has copied the
233 - * data for us and set "end". Broken quoting (e.g.,
234 - * an entry that doesn't end with a quote) falls
235 - * back to the unquoted case below.
236 - */
237 - } else {
238 - /* normal, unquoted path */
239 - end = strchrnul(string, sep);
240 - strbuf_add(out, string, end - string);
246 + if (*end)
247 + end++;
248 + string = end;
249 +
250 + if (!buf.len)
251 + continue;
252 +
253 + strvec_push(out, buf.buf);
254 }
255
243 - if (*end)
244 - end++;
245 - return end;
256 + strbuf_release(&buf);
257 }
258
259 static void link_alt_odb_entries(struct object_database *odb, const char *alt,
260 int sep, const char *relative_base, int depth)
261 {
251 - struct strbuf dir = STRBUF_INIT;
262 + struct strvec alternates = STRVEC_INIT;
263
264 if (!alt || !*alt)
265 return;
@@ -259,13 +270,12 @@ static void link_alt_odb_entries(struct object_database *odb, const char *alt,
270 return;
271 }
272
262 - while (*alt) {
263 - alt = parse_alt_odb_entry(alt, sep, &dir);
264 - if (!dir.len)
265 - continue;
266 - link_alt_odb_entry(odb, dir.buf, relative_base, depth);
267 - }
268 - strbuf_release(&dir);
273 + parse_alternates(alt, sep, &alternates);
274 +
275 + for (size_t i = 0; i < alternates.nr; i++)
276 + link_alt_odb_entry(odb, alternates.v[i], relative_base, depth);
277 +
278 + strvec_clear(&alternates);
279 }
280
281 static void read_info_alternates(struct object_database *odb,