Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.kernel.org/pub/scm/git/git.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/refs.c
diff options
context:
space:
mode:
Diffstat (limited to 'refs.c')
-rw-r--r--refs.c432
1 files changed, 304 insertions, 128 deletions
diff --git a/refs.c b/refs.c
index c89d558892..20e8f1ff1f 100644
--- a/refs.c
+++ b/refs.c
@@ -2,40 +2,64 @@
* The backend-independent part of the reference module.
*/
-#include "cache.h"
+#include "git-compat-util.h"
+#include "advice.h"
#include "config.h"
+#include "environment.h"
#include "hashmap.h"
+#include "gettext.h"
+#include "hex.h"
#include "lockfile.h"
#include "iterator.h"
#include "refs.h"
#include "refs/refs-internal.h"
#include "run-command.h"
#include "hook.h"
-#include "object-store.h"
+#include "object-name.h"
+#include "object-store-ll.h"
#include "object.h"
+#include "path.h"
#include "tag.h"
#include "submodule.h"
#include "worktree.h"
#include "strvec.h"
#include "repository.h"
+#include "setup.h"
#include "sigchain.h"
#include "date.h"
#include "commit.h"
+#include "wildmatch.h"
/*
* List of all available backends
*/
-static struct ref_storage_be *refs_backends = &refs_be_files;
+static const struct ref_storage_be *refs_backends[] = {
+ [REF_STORAGE_FORMAT_FILES] = &refs_be_files,
+};
-static struct ref_storage_be *find_ref_storage_backend(const char *name)
+static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
{
- struct ref_storage_be *be;
- for (be = refs_backends; be; be = be->next)
- if (!strcmp(be->name, name))
- return be;
+ if (ref_storage_format < ARRAY_SIZE(refs_backends))
+ return refs_backends[ref_storage_format];
return NULL;
}
+unsigned int ref_storage_format_by_name(const char *name)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
+ if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
+ return i;
+ return REF_STORAGE_FORMAT_UNKNOWN;
+}
+
+const char *ref_storage_format_to_name(unsigned int ref_storage_format)
+{
+ const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
+ if (!be)
+ return "unknown";
+ return be->name;
+}
+
/*
* How to handle various characters in refnames:
* 0: An acceptable character for refs
@@ -367,8 +391,8 @@ char *resolve_refdup(const char *refname, int resolve_flags,
oid, flags);
}
-/* The argument to filter_refs */
-struct ref_filter {
+/* The argument to for_each_filter_refs */
+struct for_each_ref_filter {
const char *pattern;
const char *prefix;
each_ref_fn *fn;
@@ -401,10 +425,11 @@ int ref_exists(const char *refname)
return refs_ref_exists(get_main_ref_store(the_repository), refname);
}
-static int filter_refs(const char *refname, const struct object_id *oid,
- int flags, void *data)
+static int for_each_filter_refs(const char *refname,
+ const struct object_id *oid,
+ int flags, void *data)
{
- struct ref_filter *filter = (struct ref_filter *)data;
+ struct for_each_ref_filter *filter = data;
if (wildmatch(filter->pattern, refname, 0))
return 0;
@@ -561,7 +586,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data)
{
struct strbuf real_pattern = STRBUF_INIT;
- struct ref_filter filter;
+ struct for_each_ref_filter filter;
int ret;
if (!prefix && !starts_with(pattern, "refs/"))
@@ -581,7 +606,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
filter.prefix = prefix;
filter.fn = fn;
filter.cb_data = cb_data;
- ret = for_each_ref(filter_refs, &filter);
+ ret = for_each_ref(for_each_filter_refs, &filter);
strbuf_release(&real_pattern);
return ret;
@@ -811,7 +836,7 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)
return repo_dwim_log(the_repository, str, len, oid, log);
}
-static int is_per_worktree_ref(const char *refname)
+int is_per_worktree_ref(const char *refname)
{
return starts_with(refname, "refs/worktree/") ||
starts_with(refname, "refs/bisect/") ||
@@ -827,37 +852,63 @@ static int is_pseudoref_syntax(const char *refname)
return 0;
}
+ /*
+ * HEAD is not a pseudoref, but it certainly uses the
+ * pseudoref syntax.
+ */
return 1;
}
-static int is_main_pseudoref_syntax(const char *refname)
-{
- return skip_prefix(refname, "main-worktree/", &refname) &&
- *refname &&
- is_pseudoref_syntax(refname);
+static int is_current_worktree_ref(const char *ref) {
+ return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref);
}
-static int is_other_pseudoref_syntax(const char *refname)
+enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref,
+ const char **worktree_name, int *worktree_name_length,
+ const char **bare_refname)
{
- if (!skip_prefix(refname, "worktrees/", &refname))
- return 0;
- refname = strchr(refname, '/');
- if (!refname || !refname[1])
- return 0;
- return is_pseudoref_syntax(refname + 1);
-}
+ const char *name_dummy;
+ int name_length_dummy;
+ const char *ref_dummy;
-enum ref_type ref_type(const char *refname)
-{
- if (is_per_worktree_ref(refname))
- return REF_TYPE_PER_WORKTREE;
- if (is_pseudoref_syntax(refname))
- return REF_TYPE_PSEUDOREF;
- if (is_main_pseudoref_syntax(refname))
- return REF_TYPE_MAIN_PSEUDOREF;
- if (is_other_pseudoref_syntax(refname))
- return REF_TYPE_OTHER_PSEUDOREF;
- return REF_TYPE_NORMAL;
+ if (!worktree_name)
+ worktree_name = &name_dummy;
+ if (!worktree_name_length)
+ worktree_name_length = &name_length_dummy;
+ if (!bare_refname)
+ bare_refname = &ref_dummy;
+
+ if (skip_prefix(maybe_worktree_ref, "worktrees/", bare_refname)) {
+ const char *slash = strchr(*bare_refname, '/');
+
+ *worktree_name = *bare_refname;
+ if (!slash) {
+ *worktree_name_length = strlen(*worktree_name);
+
+ /* This is an error condition, and the caller tell because the bare_refname is "" */
+ *bare_refname = *worktree_name + *worktree_name_length;
+ return REF_WORKTREE_OTHER;
+ }
+
+ *worktree_name_length = slash - *bare_refname;
+ *bare_refname = slash + 1;
+
+ if (is_current_worktree_ref(*bare_refname))
+ return REF_WORKTREE_OTHER;
+ }
+
+ *worktree_name = NULL;
+ *worktree_name_length = 0;
+
+ if (skip_prefix(maybe_worktree_ref, "main-worktree/", bare_refname)
+ && is_current_worktree_ref(*bare_refname))
+ return REF_WORKTREE_MAIN;
+
+ *bare_refname = maybe_worktree_ref;
+ if (is_current_worktree_ref(maybe_worktree_ref))
+ return REF_WORKTREE_CURRENT;
+
+ return REF_WORKTREE_SHARED;
}
long get_files_ref_lock_timeout_ms(void)
@@ -1284,65 +1335,68 @@ int update_ref(const char *msg, const char *refname,
old_oid, flags, onerr);
}
+/*
+ * Check that the string refname matches a rule of the form
+ * "{prefix}%.*s{suffix}". So "foo/bar/baz" would match the rule
+ * "foo/%.*s/baz", and return the string "bar".
+ */
+static const char *match_parse_rule(const char *refname, const char *rule,
+ size_t *len)
+{
+ /*
+ * Check that rule matches refname up to the first percent in the rule.
+ * We can bail immediately if not, but otherwise we leave "rule" at the
+ * %-placeholder, and "refname" at the start of the potential matched
+ * name.
+ */
+ while (*rule != '%') {
+ if (!*rule)
+ BUG("rev-parse rule did not have percent");
+ if (*refname++ != *rule++)
+ return NULL;
+ }
+
+ /*
+ * Check that our "%" is the expected placeholder. This assumes there
+ * are no other percents (placeholder or quoted) in the string, but
+ * that is sufficient for our rev-parse rules.
+ */
+ if (!skip_prefix(rule, "%.*s", &rule))
+ return NULL;
+
+ /*
+ * And now check that our suffix (if any) matches.
+ */
+ if (!strip_suffix(refname, rule, len))
+ return NULL;
+
+ return refname; /* len set by strip_suffix() */
+}
+
char *refs_shorten_unambiguous_ref(struct ref_store *refs,
const char *refname, int strict)
{
int i;
- static char **scanf_fmts;
- static int nr_rules;
- char *short_name;
struct strbuf resolved_buf = STRBUF_INIT;
- if (!nr_rules) {
- /*
- * Pre-generate scanf formats from ref_rev_parse_rules[].
- * Generate a format suitable for scanf from a
- * ref_rev_parse_rules rule by interpolating "%s" at the
- * location of the "%.*s".
- */
- size_t total_len = 0;
- size_t offset = 0;
-
- /* the rule list is NULL terminated, count them first */
- for (nr_rules = 0; ref_rev_parse_rules[nr_rules]; nr_rules++)
- /* -2 for strlen("%.*s") - strlen("%s"); +1 for NUL */
- total_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;
-
- scanf_fmts = xmalloc(st_add(st_mult(sizeof(char *), nr_rules), total_len));
-
- offset = 0;
- for (i = 0; i < nr_rules; i++) {
- assert(offset < total_len);
- scanf_fmts[i] = (char *)&scanf_fmts[nr_rules] + offset;
- offset += xsnprintf(scanf_fmts[i], total_len - offset,
- ref_rev_parse_rules[i], 2, "%s") + 1;
- }
- }
-
- /* bail out if there are no rules */
- if (!nr_rules)
- return xstrdup(refname);
-
- /* buffer for scanf result, at most refname must fit */
- short_name = xstrdup(refname);
-
/* skip first rule, it will always match */
- for (i = nr_rules - 1; i > 0 ; --i) {
+ for (i = NUM_REV_PARSE_RULES - 1; i > 0 ; --i) {
int j;
int rules_to_fail = i;
- int short_name_len;
+ const char *short_name;
+ size_t short_name_len;
- if (1 != sscanf(refname, scanf_fmts[i], short_name))
+ short_name = match_parse_rule(refname, ref_rev_parse_rules[i],
+ &short_name_len);
+ if (!short_name)
continue;
- short_name_len = strlen(short_name);
-
/*
* in strict mode, all (except the matched one) rules
* must fail to resolve to a valid non-ambiguous ref
*/
if (strict)
- rules_to_fail = nr_rules;
+ rules_to_fail = NUM_REV_PARSE_RULES;
/*
* check if the short name resolves to a valid ref,
@@ -1362,7 +1416,8 @@ char *refs_shorten_unambiguous_ref(struct ref_store *refs,
*/
strbuf_reset(&resolved_buf);
strbuf_addf(&resolved_buf, rule,
- short_name_len, short_name);
+ cast_size_t_to_int(short_name_len),
+ short_name);
if (refs_ref_exists(refs, resolved_buf.buf))
break;
}
@@ -1373,12 +1428,11 @@ char *refs_shorten_unambiguous_ref(struct ref_store *refs,
*/
if (j == rules_to_fail) {
strbuf_release(&resolved_buf);
- return short_name;
+ return xmemdupz(short_name, short_name_len);
}
}
strbuf_release(&resolved_buf);
- free(short_name);
return xstrdup(refname);
}
@@ -1388,9 +1442,8 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
refname, strict);
}
-static struct string_list *hide_refs;
-
-int parse_hide_refs_config(const char *var, const char *value, const char *section)
+int parse_hide_refs_config(const char *var, const char *value, const char *section,
+ struct strvec *hide_refs)
{
const char *key;
if (!strcmp("transfer.hiderefs", var) ||
@@ -1401,27 +1454,23 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
if (!value)
return config_error_nonbool(var);
- ref = xstrdup(value);
+
+ /* drop const to remove trailing '/' characters */
+ ref = (char *)strvec_push(hide_refs, value);
len = strlen(ref);
while (len && ref[len - 1] == '/')
ref[--len] = '\0';
- if (!hide_refs) {
- CALLOC_ARRAY(hide_refs, 1);
- hide_refs->strdup_strings = 1;
- }
- string_list_append(hide_refs, ref);
}
return 0;
}
-int ref_is_hidden(const char *refname, const char *refname_full)
+int ref_is_hidden(const char *refname, const char *refname_full,
+ const struct strvec *hide_refs)
{
int i;
- if (!hide_refs)
- return 0;
for (i = hide_refs->nr - 1; i >= 0; i--) {
- const char *match = hide_refs->items[i].string;
+ const char *match = hide_refs->v[i];
const char *subject;
int neg = 0;
const char *p;
@@ -1447,6 +1496,30 @@ int ref_is_hidden(const char *refname, const char *refname_full)
return 0;
}
+const char **hidden_refs_to_excludes(const struct strvec *hide_refs)
+{
+ const char **pattern;
+ for (pattern = hide_refs->v; *pattern; pattern++) {
+ /*
+ * We can't feed any excludes from hidden refs config
+ * sections, since later rules may override previous
+ * ones. For example, with rules "refs/foo" and
+ * "!refs/foo/bar", we should show "refs/foo/bar" (and
+ * everything underneath it), but the earlier exclusion
+ * would cause us to skip all of "refs/foo". We
+ * likewise don't implement the namespace stripping
+ * required for '^' rules.
+ *
+ * Both are possible to do, but complicated, so avoid
+ * populating the jump list at all if we see either of
+ * these patterns.
+ */
+ if (**pattern == '!' || **pattern == '^')
+ return NULL;
+ }
+ return hide_refs->v;
+}
+
const char *find_descendant_ref(const char *dirname,
const struct string_list *extras,
const struct string_list *skip)
@@ -1494,7 +1567,9 @@ int head_ref(each_ref_fn fn, void *cb_data)
struct ref_iterator *refs_ref_iterator_begin(
struct ref_store *refs,
- const char *prefix, int trim,
+ const char *prefix,
+ const char **exclude_patterns,
+ int trim,
enum do_for_each_ref_flags flags)
{
struct ref_iterator *iter;
@@ -1510,8 +1585,7 @@ struct ref_iterator *refs_ref_iterator_begin(
}
}
- iter = refs->be->iterator_begin(refs, prefix, flags);
-
+ iter = refs->be->iterator_begin(refs, prefix, exclude_patterns, flags);
/*
* `iterator_begin()` already takes care of prefix, but we
* might need to do some trimming:
@@ -1545,7 +1619,7 @@ static int do_for_each_repo_ref(struct repository *r, const char *prefix,
if (!refs)
return 0;
- iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+ iter = refs_ref_iterator_begin(refs, prefix, NULL, trim, flags);
return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
}
@@ -1555,7 +1629,7 @@ struct do_for_each_ref_help {
void *cb_data;
};
-static int do_for_each_ref_helper(struct repository *r,
+static int do_for_each_ref_helper(struct repository *r UNUSED,
const char *refname,
const struct object_id *oid,
int flags,
@@ -1567,6 +1641,7 @@ static int do_for_each_ref_helper(struct repository *r,
}
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
+ const char **exclude_patterns,
each_ref_fn fn, int trim,
enum do_for_each_ref_flags flags, void *cb_data)
{
@@ -1576,7 +1651,8 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
if (!refs)
return 0;
- iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+ iter = refs_ref_iterator_begin(refs, prefix, exclude_patterns, trim,
+ flags);
return do_for_each_repo_ref_iterator(the_repository, iter,
do_for_each_ref_helper, &hp);
@@ -1584,7 +1660,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(refs, "", fn, 0, 0, cb_data);
+ return do_for_each_ref(refs, "", NULL, fn, 0, 0, cb_data);
}
int for_each_ref(each_ref_fn fn, void *cb_data)
@@ -1595,7 +1671,7 @@ int for_each_ref(each_ref_fn fn, void *cb_data)
int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);
+ return do_for_each_ref(refs, prefix, NULL, fn, strlen(prefix), 0, cb_data);
}
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
@@ -1606,13 +1682,14 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data)
{
return do_for_each_ref(get_main_ref_store(the_repository),
- prefix, fn, 0, 0, cb_data);
+ prefix, NULL, fn, 0, 0, cb_data);
}
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+ const char **exclude_patterns,
each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(refs, prefix, fn, 0, 0, cb_data);
+ return do_for_each_ref(refs, prefix, exclude_patterns, fn, 0, 0, cb_data);
}
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
@@ -1623,20 +1700,21 @@ int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
-int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
+int for_each_namespaced_ref(const char **exclude_patterns,
+ each_ref_fn fn, void *cb_data)
{
struct strbuf buf = STRBUF_INIT;
int ret;
strbuf_addf(&buf, "%srefs/", get_git_namespace());
ret = do_for_each_ref(get_main_ref_store(the_repository),
- buf.buf, fn, 0, 0, cb_data);
+ buf.buf, exclude_patterns, fn, 0, 0, cb_data);
strbuf_release(&buf);
return ret;
}
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(refs, "", fn, 0,
+ return do_for_each_ref(refs, "", NULL, fn, 0,
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
@@ -1703,9 +1781,11 @@ static void find_longest_prefixes(struct string_list *out,
strbuf_release(&prefix);
}
-int for_each_fullref_in_prefixes(const char *namespace,
- const char **patterns,
- each_ref_fn fn, void *cb_data)
+int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
+ const char *namespace,
+ const char **patterns,
+ const char **exclude_patterns,
+ each_ref_fn fn, void *cb_data)
{
struct string_list prefixes = STRING_LIST_INIT_DUP;
struct string_list_item *prefix;
@@ -1720,7 +1800,8 @@ int for_each_fullref_in_prefixes(const char *namespace,
for_each_string_list_item(prefix, &prefixes) {
strbuf_addstr(&buf, prefix->string);
- ret = for_each_fullref_in(buf.buf, fn, cb_data);
+ ret = refs_for_each_fullref_in(ref_store, buf.buf,
+ exclude_patterns, fn, cb_data);
if (ret)
break;
strbuf_setlen(&buf, namespace_len);
@@ -1741,8 +1822,10 @@ static int refs_read_special_head(struct ref_store *ref_store,
int result = -1;
strbuf_addf(&full_path, "%s/%s", ref_store->gitdir, refname);
- if (strbuf_read_file(&content, full_path.buf, 0) < 0)
+ if (strbuf_read_file(&content, full_path.buf, 0) < 0) {
+ *failure_errno = errno;
goto done;
+ }
result = parse_loose_ref_contents(content.buf, oid, referent, type,
failure_errno);
@@ -1753,15 +1836,66 @@ done:
return result;
}
+static int is_special_ref(const char *refname)
+{
+ /*
+ * Special references get written and read directly via the filesystem
+ * by the subsystems that create them. Thus, they must not go through
+ * the reference backend but must instead be read directly. It is
+ * arguable whether this behaviour is sensible, or whether it's simply
+ * a leaky abstraction enabled by us only having a single reference
+ * backend implementation. But at least for a subset of references it
+ * indeed does make sense to treat them specially:
+ *
+ * - FETCH_HEAD may contain multiple object IDs, and each one of them
+ * carries additional metadata like where it came from.
+ *
+ * - MERGE_HEAD may contain multiple object IDs when merging multiple
+ * heads.
+ *
+ * There are some exceptions that you might expect to see on this list
+ * but which are handled exclusively via the reference backend:
+ *
+ * - BISECT_EXPECTED_REV
+ *
+ * - CHERRY_PICK_HEAD
+ *
+ * - HEAD
+ *
+ * - ORIG_HEAD
+ *
+ * - "rebase-apply/" and "rebase-merge/" contain all of the state for
+ * rebases, including some reference-like files. These are
+ * exclusively read and written via the filesystem and never go
+ * through the refdb.
+ *
+ * Writing or deleting references must consistently go either through
+ * the filesystem (special refs) or through the reference backend
+ * (normal ones).
+ */
+ static const char * const special_refs[] = {
+ "AUTO_MERGE",
+ "FETCH_HEAD",
+ "MERGE_AUTOSTASH",
+ "MERGE_HEAD",
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(special_refs); i++)
+ if (!strcmp(refname, special_refs[i]))
+ return 1;
+
+ return 0;
+}
+
int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
struct object_id *oid, struct strbuf *referent,
unsigned int *type, int *failure_errno)
{
assert(failure_errno);
- if (!strcmp(refname, "FETCH_HEAD") || !strcmp(refname, "MERGE_HEAD")) {
+ if (is_special_ref(refname))
return refs_read_special_head(ref_store, refname, oid, referent,
type, failure_errno);
- }
return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
type, failure_errno);
@@ -1797,7 +1931,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
return NULL;
/*
- * dwim_ref() uses REF_ISBROKEN to distinguish between
+ * repo_dwim_ref() uses REF_ISBROKEN to distinguish between
* missing refs and refs that were present but invalid,
* to complain about the latter to stderr.
*
@@ -1964,12 +2098,12 @@ static struct ref_store *ref_store_init(struct repository *repo,
const char *gitdir,
unsigned int flags)
{
- const char *be_name = "files";
- struct ref_storage_be *be = find_ref_storage_backend(be_name);
+ const struct ref_storage_be *be;
struct ref_store *refs;
+ be = find_ref_storage_backend(repo->ref_storage_format);
if (!be)
- BUG("reference backend %s is unknown", be_name);
+ BUG("reference backend is unknown");
refs = be->init(repo, gitdir, flags);
return refs;
@@ -2100,9 +2234,9 @@ void base_ref_store_init(struct ref_store *refs, struct repository *repo,
}
/* backend functions */
-int refs_pack_refs(struct ref_store *refs, unsigned int flags)
+int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts)
{
- return refs->be->pack_refs(refs, flags);
+ return refs->be->pack_refs(refs, opts);
}
int peel_iterated_oid(const struct object_id *base, struct object_id *peeled)
@@ -2375,7 +2509,7 @@ int refs_verify_refname_available(struct ref_store *refs,
strbuf_addstr(&dirname, refname + dirname.len);
strbuf_addch(&dirname, '/');
- iter = refs_ref_iterator_begin(refs, dirname.buf, 0,
+ iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
DO_FOR_EACH_INCLUDE_BROKEN);
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
if (skip &&
@@ -2534,13 +2668,55 @@ void ref_transaction_for_each_queued_update(struct ref_transaction *transaction,
int refs_delete_refs(struct ref_store *refs, const char *logmsg,
struct string_list *refnames, unsigned int flags)
{
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
+ struct string_list_item *item;
+ int ret = 0, failures = 0;
char *msg;
- int retval;
+
+ if (!refnames->nr)
+ return 0;
msg = normalize_reflog_message(logmsg);
- retval = refs->be->delete_refs(refs, msg, refnames, flags);
+
+ /*
+ * Since we don't check the references' old_oids, the
+ * individual updates can't fail, so we can pack all of the
+ * updates into a single transaction.
+ */
+ transaction = ref_store_transaction_begin(refs, &err);
+ if (!transaction) {
+ ret = error("%s", err.buf);
+ goto out;
+ }
+
+ for_each_string_list_item(item, refnames) {
+ ret = ref_transaction_delete(transaction, item->string,
+ NULL, flags, msg, &err);
+ if (ret) {
+ warning(_("could not delete reference %s: %s"),
+ item->string, err.buf);
+ strbuf_reset(&err);
+ failures = 1;
+ }
+ }
+
+ ret = ref_transaction_commit(transaction, &err);
+ if (ret) {
+ if (refnames->nr == 1)
+ error(_("could not delete reference %s: %s"),
+ refnames->items[0].string, err.buf);
+ else
+ error(_("could not delete references: %s"), err.buf);
+ }
+
+out:
+ if (!ret && failures)
+ ret = -1;
+ ref_transaction_free(transaction);
+ strbuf_release(&err);
free(msg);
- return retval;
+ return ret;
}
int delete_refs(const char *msg, struct string_list *refnames,