aboutsummaryrefslogtreecommitdiff
path: root/refs.c
diff options
context:
space:
mode:
authorMichael Haggerty <mhagger@alum.mit.edu>2017-04-16 08:41:26 +0200
committerJunio C Hamano <gitster@pobox.com>2017-04-16 21:32:45 -0700
commitb05855b5bcaf07fe8a636e621bd9d12d51963b5a (patch)
tree01832b86c7e5bf8defc66694362bddd561c8bdab /refs.c
parente121b9cb5f358c9779e02a2bf96fc545a896ab13 (diff)
downloadgit-b05855b5bcaf07fe8a636e621bd9d12d51963b5a.tar.gz
git-b05855b5bcaf07fe8a636e621bd9d12d51963b5a.tar.xz
refs_verify_refname_available(): implement once for all backends
It turns out that we can now implement `refs_verify_refname_available()` based on the other virtual functions, so there is no need for it to be defined at the backend level. Instead, define it once in `refs.c` and remove the `files_backend` definition. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'refs.c')
-rw-r--r--refs.c85
1 files changed, 83 insertions, 2 deletions
diff --git a/refs.c b/refs.c
index 2ae18097d..e841248b0 100644
--- a/refs.c
+++ b/refs.c
@@ -5,6 +5,7 @@
#include "cache.h"
#include "hashmap.h"
#include "lockfile.h"
+#include "iterator.h"
#include "refs.h"
#include "refs/refs-internal.h"
#include "object.h"
@@ -1658,11 +1659,91 @@ int ref_transaction_commit(struct ref_transaction *transaction,
int refs_verify_refname_available(struct ref_store *refs,
const char *refname,
- const struct string_list *extra,
+ const struct string_list *extras,
const struct string_list *skip,
struct strbuf *err)
{
- return refs->be->verify_refname_available(refs, refname, extra, skip, err);
+ const char *slash;
+ const char *extra_refname;
+ struct strbuf dirname = STRBUF_INIT;
+ struct strbuf referent = STRBUF_INIT;
+ struct object_id oid;
+ unsigned int type;
+ struct ref_iterator *iter;
+ int ok;
+ int ret = -1;
+
+ /*
+ * For the sake of comments in this function, suppose that
+ * refname is "refs/foo/bar".
+ */
+
+ assert(err);
+
+ strbuf_grow(&dirname, strlen(refname) + 1);
+ for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+ /* Expand dirname to the new prefix, not including the trailing slash: */
+ strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
+
+ /*
+ * We are still at a leading dir of the refname (e.g.,
+ * "refs/foo"; if there is a reference with that name,
+ * it is a conflict, *unless* it is in skip.
+ */
+ if (skip && string_list_has_string(skip, dirname.buf))
+ continue;
+
+ if (!refs_read_raw_ref(refs, dirname.buf, oid.hash, &referent, &type)) {
+ strbuf_addf(err, "'%s' exists; cannot create '%s'",
+ dirname.buf, refname);
+ goto cleanup;
+ }
+
+ if (extras && string_list_has_string(extras, dirname.buf)) {
+ strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
+ refname, dirname.buf);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * We are at the leaf of our refname (e.g., "refs/foo/bar").
+ * There is no point in searching for a reference with that
+ * name, because a refname isn't considered to conflict with
+ * itself. But we still need to check for references whose
+ * names are in the "refs/foo/bar/" namespace, because they
+ * *do* conflict.
+ */
+ strbuf_addstr(&dirname, refname + dirname.len);
+ strbuf_addch(&dirname, '/');
+
+ iter = refs_ref_iterator_begin(refs, dirname.buf, 0,
+ DO_FOR_EACH_INCLUDE_BROKEN);
+ while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
+ if (skip &&
+ string_list_has_string(skip, iter->refname))
+ continue;
+
+ strbuf_addf(err, "'%s' exists; cannot create '%s'",
+ iter->refname, refname);
+ ref_iterator_abort(iter);
+ goto cleanup;
+ }
+
+ if (ok != ITER_DONE)
+ die("BUG: error while iterating over references");
+
+ extra_refname = find_descendant_ref(dirname.buf, extras, skip);
+ if (extra_refname)
+ strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
+ refname, extra_refname);
+ else
+ ret = 0;
+
+cleanup:
+ strbuf_release(&referent);
+ strbuf_release(&dirname);
+ return ret;
}
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)