refs: introduce function to batch refname availability checks

The `refs_verify_refname_available()` functions checks whether a reference update can be committed or whether it would conflict with either a prefix or suffix thereof. This function needs to be called once per reference that one wants to check, which requires us to redo a couple of checks every time the function is called. Introduce a new function `refs_verify_refnames_available()` that does the same, but for a list of references. For now, the new function uses the exact same implementation, except that we loop through all refnames provided by the caller. This will be tuned in subsequent commits. The existing `refs_verify_refname_available()` function is reimplemented on top of the new function. As such, the diff is best viewed with the `--ignore-space-change option`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Mar 12, 2025 at 16:56 UTC 2ff58dec493ab5bebb6943b814461ba4e9937e15
2 files changed +110 -72
refs.c
+98 -72
@@ -2475,19 +2475,16 @@ int ref_transaction_commit(struct ref_transaction *transaction,
2475 return ret;
2476 }
2477
2478 -int refs_verify_refname_available(struct ref_store *refs,
2479 - const char *refname,
2480 - const struct string_list *extras,
2481 - const struct string_list *skip,
2482 - unsigned int initial_transaction,
2483 - struct strbuf *err)
2478 +int refs_verify_refnames_available(struct ref_store *refs,
2479 + const struct string_list *refnames,
2480 + const struct string_list *extras,
2481 + const struct string_list *skip,
2482 + unsigned int initial_transaction,
2483 + struct strbuf *err)
2484 {
2485 - const char *slash;
2486 - const char *extra_refname;
2485 struct strbuf dirname = STRBUF_INIT;
2486 struct strbuf referent = STRBUF_INIT;
2489 - struct object_id oid;
2490 - unsigned int type;
2487 + struct string_list_item *item;
2488 int ret = -1;
2489
2490 /*
@@ -2497,79 +2494,91 @@ int refs_verify_refname_available(struct ref_store *refs,
2494
2495 assert(err);
2496
2500 - strbuf_grow(&dirname, strlen(refname) + 1);
2501 - for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
2502 - /*
2503 - * Just saying "Is a directory" when we e.g. can't
2504 - * lock some multi-level ref isn't very informative,
2505 - * the user won't be told *what* is a directory, so
2506 - * let's not use strerror() below.
2507 - */
2508 - int ignore_errno;
2509 - /* Expand dirname to the new prefix, not including the trailing slash: */
2510 - strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
2497 + for_each_string_list_item(item, refnames) {
2498 + const char *refname = item->string;
2499 + const char *extra_refname;
2500 + struct object_id oid;
2501 + unsigned int type;
2502 + const char *slash;
2503 +
2504 + strbuf_reset(&dirname);
2505 +
2506 + for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
2507 + /*
2508 + * Just saying "Is a directory" when we e.g. can't
2509 + * lock some multi-level ref isn't very informative,
2510 + * the user won't be told *what* is a directory, so
2511 + * let's not use strerror() below.
2512 + */
2513 + int ignore_errno;
2514 +
2515 + /* Expand dirname to the new prefix, not including the trailing slash: */
2516 + strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
2517 +
2518 + /*
2519 + * We are still at a leading dir of the refname (e.g.,
2520 + * "refs/foo"; if there is a reference with that name,
2521 + * it is a conflict, *unless* it is in skip.
2522 + */
2523 + if (skip && string_list_has_string(skip, dirname.buf))
2524 + continue;
2525 +
2526 + if (!initial_transaction &&
2527 + !refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
2528 + &type, &ignore_errno)) {
2529 + strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
2530 + dirname.buf, refname);
2531 + goto cleanup;
2532 + }
2533 +
2534 + if (extras && string_list_has_string(extras, dirname.buf)) {
2535 + strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
2536 + refname, dirname.buf);
2537 + goto cleanup;
2538 + }
2539 + }
2540
2541 /*
2513 - * We are still at a leading dir of the refname (e.g.,
2514 - * "refs/foo"; if there is a reference with that name,
2515 - * it is a conflict, *unless* it is in skip.
2542 + * We are at the leaf of our refname (e.g., "refs/foo/bar").
2543 + * There is no point in searching for a reference with that
2544 + * name, because a refname isn't considered to conflict with
2545 + * itself. But we still need to check for references whose
2546 + * names are in the "refs/foo/bar/" namespace, because they
2547 + * *do* conflict.
2548 */
2517 - if (skip && string_list_has_string(skip, dirname.buf))
2518 - continue;
2549 + strbuf_addstr(&dirname, refname + dirname.len);
2550 + strbuf_addch(&dirname, '/');
2551 +
2552 + if (!initial_transaction) {
2553 + struct ref_iterator *iter;
2554 + int ok;
2555 +
2556 + iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
2557 + DO_FOR_EACH_INCLUDE_BROKEN);
2558 + while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
2559 + if (skip &&
2560 + string_list_has_string(skip, iter->refname))
2561 + continue;
2562 +
2563 + strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
2564 + iter->refname, refname);
2565 + ref_iterator_abort(iter);
2566 + goto cleanup;
2567 + }
2568
2520 - if (!initial_transaction &&
2521 - !refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
2522 - &type, &ignore_errno)) {
2523 - strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
2524 - dirname.buf, refname);
2525 - goto cleanup;
2569 + if (ok != ITER_DONE)
2570 + BUG("error while iterating over references");
2571 }
2572
2528 - if (extras && string_list_has_string(extras, dirname.buf)) {
2573 + extra_refname = find_descendant_ref(dirname.buf, extras, skip);
2574 + if (extra_refname) {
2575 strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
2530 - refname, dirname.buf);
2576 + refname, extra_refname);
2577 goto cleanup;
2578 }
2579 }
2580
2535 - /*
2536 - * We are at the leaf of our refname (e.g., "refs/foo/bar").
2537 - * There is no point in searching for a reference with that
2538 - * name, because a refname isn't considered to conflict with
2539 - * itself. But we still need to check for references whose
2540 - * names are in the "refs/foo/bar/" namespace, because they
2541 - * *do* conflict.
2542 - */
2543 - strbuf_addstr(&dirname, refname + dirname.len);
2544 - strbuf_addch(&dirname, '/');
2545 -
2546 - if (!initial_transaction) {
2547 - struct ref_iterator *iter;
2548 - int ok;
2549 -
2550 - iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
2551 - DO_FOR_EACH_INCLUDE_BROKEN);
2552 - while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
2553 - if (skip &&
2554 - string_list_has_string(skip, iter->refname))
2555 - continue;
2556 -
2557 - strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
2558 - iter->refname, refname);
2559 - ref_iterator_abort(iter);
2560 - goto cleanup;
2561 - }
2562 -
2563 - if (ok != ITER_DONE)
2564 - BUG("error while iterating over references");
2565 - }
2566 -
2567 - extra_refname = find_descendant_ref(dirname.buf, extras, skip);
2568 - if (extra_refname)
2569 - strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
2570 - refname, extra_refname);
2571 - else
2572 - ret = 0;
2581 + ret = 0;
2582
2583 cleanup:
2584 strbuf_release(&referent);
@@ -2577,6 +2586,23 @@ cleanup:
2586 return ret;
2587 }
2588
2589 +int refs_verify_refname_available(struct ref_store *refs,
2590 + const char *refname,
2591 + const struct string_list *extras,
2592 + const struct string_list *skip,
2593 + unsigned int initial_transaction,
2594 + struct strbuf *err)
2595 +{
2596 + struct string_list_item item = { .string = (char *) refname };
2597 + struct string_list refnames = {
2598 + .items = &item,
2599 + .nr = 1,
2600 + };
2601 +
2602 + return refs_verify_refnames_available(refs, &refnames, extras, skip,
2603 + initial_transaction, err);
2604 +}
2605 +
2606 struct do_for_each_reflog_help {
2607 each_reflog_fn *fn;
2608 void *cb_data;
refs.h
+12
@@ -124,6 +124,18 @@ int refs_verify_refname_available(struct ref_store *refs,
124 unsigned int initial_transaction,
125 struct strbuf *err);
126
127 +/*
128 + * Same as `refs_verify_refname_available()`, but checking for a list of
129 + * refnames instead of only a single item. This is more efficient in the case
130 + * where one needs to check multiple refnames.
131 + */
132 +int refs_verify_refnames_available(struct ref_store *refs,
133 + const struct string_list *refnames,
134 + const struct string_list *extras,
135 + const struct string_list *skip,
136 + unsigned int initial_transaction,
137 + struct strbuf *err);
138 +
139 int refs_ref_exists(struct ref_store *refs, const char *refname);
140
141 int should_autocreate_reflog(enum log_refs_config log_all_ref_updates,