Skip to content

Commit 2ff58de

Browse files
pks-tgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3c20bf0 commit 2ff58de

File tree

2 files changed

+110
-72
lines changed

2 files changed

+110
-72
lines changed

refs.c

Lines changed: 98 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2475,19 +2475,16 @@ int ref_transaction_commit(struct ref_transaction *transaction,
24752475
return ret;
24762476
}
24772477

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)
24842484
{
2485-
const char *slash;
2486-
const char *extra_refname;
24872485
struct strbuf dirname = STRBUF_INIT;
24882486
struct strbuf referent = STRBUF_INIT;
2489-
struct object_id oid;
2490-
unsigned int type;
2487+
struct string_list_item *item;
24912488
int ret = -1;
24922489

24932490
/*
@@ -2497,86 +2494,115 @@ int refs_verify_refname_available(struct ref_store *refs,
24972494

24982495
assert(err);
24992496

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+
}
25112540

25122541
/*
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.
25162548
*/
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+
}
25192568

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");
25262571
}
25272572

2528-
if (extras && string_list_has_string(extras, dirname.buf)) {
2573+
extra_refname = find_descendant_ref(dirname.buf, extras, skip);
2574+
if (extra_refname) {
25292575
strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
2530-
refname, dirname.buf);
2576+
refname, extra_refname);
25312577
goto cleanup;
25322578
}
25332579
}
25342580

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;
25732582

25742583
cleanup:
25752584
strbuf_release(&referent);
25762585
strbuf_release(&dirname);
25772586
return ret;
25782587
}
25792588

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+
25802606
struct do_for_each_reflog_help {
25812607
each_reflog_fn *fn;
25822608
void *cb_data;

refs.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,18 @@ int refs_verify_refname_available(struct ref_store *refs,
124124
unsigned int initial_transaction,
125125
struct strbuf *err);
126126

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+
127139
int refs_ref_exists(struct ref_store *refs, const char *refname);
128140

129141
int should_autocreate_reflog(enum log_refs_config log_all_ref_updates,

0 commit comments

Comments
 (0)