refs_verify_refname_available(): implement once for all backends
authorMichael Haggerty <mhagger@alum.mit.edu>
Sun, 16 Apr 2017 06:41:26 +0000 (08:41 +0200)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Apr 2017 04:32:45 +0000 (21:32 -0700)
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>
refs.c
refs.h
refs/files-backend.c
refs/refs-internal.h
diff --git a/refs.c b/refs.c
index 2ae18097d4d66346fa702665c6fe21b5c4770207..e841248b0152f8a602f89d33ab267a4a3410803e 100644 (file)
--- 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)
diff --git a/refs.h b/refs.h
index 49e97d7d5fdf1ce385ea8917bda00a4cce68561f..07cf4cd41b15ee5dafdb5c99983b136db84aaa81 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -97,7 +97,7 @@ int read_ref(const char *refname, unsigned char *sha1);
 
 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);
 
index 2a0538dea797b18ace2f3bccb194ed8b6bd34702..4185025efa46ab1cc2a408dd82c5dd17095bf0ab 100644 (file)
@@ -1724,10 +1724,9 @@ static int lock_raw_ref(struct files_ref_store *refs,
                                goto error_return;
                        } else if (remove_dir_recursively(&ref_file,
                                                          REMOVE_DIR_EMPTY_ONLY)) {
-                               if (verify_refname_available_dir(
-                                                   refname, extras, skip,
-                                                   get_loose_refs(refs),
-                                                   err)) {
+                               if (refs_verify_refname_available(
+                                                   &refs->base, refname,
+                                                   extras, skip, err)) {
                                        /*
                                         * The error message set by
                                         * verify_refname_available() is OK.
@@ -2094,9 +2093,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
                 */
                if (remove_empty_directories(&ref_file)) {
                        last_errno = errno;
-                       if (!verify_refname_available_dir(
-                                           refname, extras, skip,
-                                           get_loose_refs(refs), err))
+                       if (!refs_verify_refname_available(
+                                           &refs->base,
+                                           refname, extras, skip, err))
                                strbuf_addf(err, "there are still refs under '%s'",
                                            refname);
                        goto error_return;
@@ -2108,9 +2107,8 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
        if (!resolved) {
                last_errno = errno;
                if (last_errno != ENOTDIR ||
-                   !verify_refname_available_dir(
-                                   refname, extras, skip,
-                                   get_loose_refs(refs), err))
+                   !refs_verify_refname_available(&refs->base, refname,
+                                                  extras, skip, err))
                        strbuf_addf(err, "unable to resolve reference '%s': %s",
                                    refname, strerror(last_errno));
 
@@ -2606,26 +2604,6 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
        return ret;
 }
 
-static int files_verify_refname_available(struct ref_store *ref_store,
-                                         const char *newname,
-                                         const struct string_list *extras,
-                                         const struct string_list *skip,
-                                         struct strbuf *err)
-{
-       struct files_ref_store *refs =
-               files_downcast(ref_store, REF_STORE_READ, "verify_refname_available");
-       struct ref_dir *packed_refs = get_packed_refs(refs);
-       struct ref_dir *loose_refs = get_loose_refs(refs);
-
-       if (verify_refname_available_dir(newname, extras, skip,
-                                        packed_refs, err) ||
-           verify_refname_available_dir(newname, extras, skip,
-                                        loose_refs, err))
-               return -1;
-
-       return 0;
-}
-
 static int write_ref_to_lockfile(struct ref_lock *lock,
                                 const unsigned char *sha1, struct strbuf *err);
 static int commit_ref_update(struct files_ref_store *refs,
@@ -4288,7 +4266,6 @@ struct ref_storage_be refs_be_files = {
 
        files_ref_iterator_begin,
        files_read_raw_ref,
-       files_verify_refname_available,
 
        files_reflog_iterator_begin,
        files_for_each_reflog_ent,
index 545989ae7f7d7e85fc58667297cb9706c8013c73..3d46131efbcc0a76cc3054a97a669bc5bdd036e7 100644 (file)
@@ -590,12 +590,6 @@ typedef int read_raw_ref_fn(struct ref_store *ref_store,
                            const char *refname, unsigned char *sha1,
                            struct strbuf *referent, unsigned int *type);
 
-typedef int verify_refname_available_fn(struct ref_store *ref_store,
-                                       const char *newname,
-                                       const struct string_list *extras,
-                                       const struct string_list *skip,
-                                       struct strbuf *err);
-
 struct ref_storage_be {
        struct ref_storage_be *next;
        const char *name;
@@ -612,7 +606,6 @@ struct ref_storage_be {
 
        ref_iterator_begin_fn *iterator_begin;
        read_raw_ref_fn *read_raw_ref;
-       verify_refname_available_fn *verify_refname_available;
 
        reflog_iterator_begin_fn *reflog_iterator_begin;
        for_each_reflog_ent_fn *for_each_reflog_ent;