refs: make create_symref() virtual
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 5dc2473fbb6491c496a1a0b3ac43dfcf90a0000f..312c76619f75846aa1c4c363430a3ff929bf3ac3 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -9,6 +9,25 @@
 #include "object.h"
 #include "tag.h"
 
+/*
+ * List of all available backends
+ */
+static struct ref_storage_be *refs_backends = &refs_be_files;
+
+static struct ref_storage_be *find_ref_storage_backend(const char *name)
+{
+       struct ref_storage_be *be;
+       for (be = refs_backends; be; be = be->next)
+               if (!strcmp(be->name, name))
+                       return be;
+       return NULL;
+}
+
+int ref_storage_backend_exists(const char *name)
+{
+       return find_ref_storage_backend(name) != NULL;
+}
+
 /*
  * How to handle various characters in refnames:
  * 0: An acceptable character for refs
@@ -766,13 +785,33 @@ void ref_transaction_free(struct ref_transaction *transaction)
        free(transaction);
 }
 
-static struct ref_update *add_update(struct ref_transaction *transaction,
-                                    const char *refname)
+struct ref_update *ref_transaction_add_update(
+               struct ref_transaction *transaction,
+               const char *refname, unsigned int flags,
+               const unsigned char *new_sha1,
+               const unsigned char *old_sha1,
+               const char *msg)
 {
        struct ref_update *update;
+
+       if (transaction->state != REF_TRANSACTION_OPEN)
+               die("BUG: update called for transaction that is not open");
+
+       if ((flags & REF_ISPRUNING) && !(flags & REF_NODEREF))
+               die("BUG: REF_ISPRUNING set without REF_NODEREF");
+
        FLEX_ALLOC_STR(update, refname, refname);
        ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
        transaction->updates[transaction->nr++] = update;
+
+       update->flags = flags;
+
+       if (flags & REF_HAVE_NEW)
+               hashcpy(update->new_sha1, new_sha1);
+       if (flags & REF_HAVE_OLD)
+               hashcpy(update->old_sha1, old_sha1);
+       if (msg)
+               update->msg = xstrdup(msg);
        return update;
 }
 
@@ -783,35 +822,20 @@ int ref_transaction_update(struct ref_transaction *transaction,
                           unsigned int flags, const char *msg,
                           struct strbuf *err)
 {
-       struct ref_update *update;
-
        assert(err);
 
-       if (transaction->state != REF_TRANSACTION_OPEN)
-               die("BUG: update called for transaction that is not open");
-
-       if ((flags & REF_ISPRUNING) && !(flags & REF_NODEREF))
-               die("BUG: REF_ISPRUNING set without REF_NODEREF");
-
-       if (new_sha1 && !is_null_sha1(new_sha1) &&
-           check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+       if ((new_sha1 && !is_null_sha1(new_sha1)) ?
+           check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
+           !refname_is_safe(refname)) {
                strbuf_addf(err, "refusing to update ref with bad name '%s'",
                            refname);
                return -1;
        }
 
-       update = add_update(transaction, refname);
-       if (new_sha1) {
-               hashcpy(update->new_sha1, new_sha1);
-               flags |= REF_HAVE_NEW;
-       }
-       if (old_sha1) {
-               hashcpy(update->old_sha1, old_sha1);
-               flags |= REF_HAVE_OLD;
-       }
-       update->flags = flags;
-       if (msg)
-               update->msg = xstrdup(msg);
+       flags |= (new_sha1 ? REF_HAVE_NEW : 0) | (old_sha1 ? REF_HAVE_OLD : 0);
+
+       ref_transaction_add_update(transaction, refname, flags,
+                                  new_sha1, old_sha1, msg);
        return 0;
 }
 
@@ -1076,20 +1100,20 @@ const char *find_descendant_ref(const char *dirname,
        return NULL;
 }
 
-int rename_ref_available(const char *oldname, const char *newname)
+int rename_ref_available(const char *old_refname, const char *new_refname)
 {
        struct string_list skip = STRING_LIST_INIT_NODUP;
        struct strbuf err = STRBUF_INIT;
-       int ret;
+       int ok;
 
-       string_list_insert(&skip, oldname);
-       ret = !verify_refname_available(newname, NULL, &skip, &err);
-       if (!ret)
+       string_list_insert(&skip, old_refname);
+       ok = !verify_refname_available(new_refname, NULL, &skip, &err);
+       if (!ok)
                error("%s", err.buf);
 
        string_list_clear(&skip, 0);
        strbuf_release(&err);
-       return ret;
+       return ok;
 }
 
 int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
@@ -1115,6 +1139,30 @@ int head_ref(each_ref_fn fn, void *cb_data)
        return head_ref_submodule(NULL, fn, cb_data);
 }
 
+/*
+ * Call fn for each reference in the specified submodule for which the
+ * refname begins with prefix. If trim is non-zero, then trim that
+ * many characters off the beginning of each refname before passing
+ * the refname to fn. flags can be DO_FOR_EACH_INCLUDE_BROKEN to
+ * include broken references in the iteration. If fn ever returns a
+ * non-zero value, stop the iteration and return that value;
+ * otherwise, return 0.
+ */
+static int do_for_each_ref(const char *submodule, const char *prefix,
+                          each_ref_fn fn, int trim, int flags, void *cb_data)
+{
+       struct ref_store *refs = get_ref_store(submodule);
+       struct ref_iterator *iter;
+
+       if (!refs)
+               return 0;
+
+       iter = files_ref_iterator_begin(submodule, prefix, flags);
+       iter = prefix_ref_iterator_begin(iter, prefix, trim);
+
+       return do_for_each_ref_iterator(iter, fn, cb_data);
+}
+
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
        return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
@@ -1168,8 +1216,10 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
 }
 
 /* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
-                              unsigned char *sha1, int *flags)
+static const char *resolve_ref_recursively(struct ref_store *refs,
+                                          const char *refname,
+                                          int resolve_flags,
+                                          unsigned char *sha1, int *flags)
 {
        static struct strbuf sb_refname = STRBUF_INIT;
        int unused_flags;
@@ -1201,7 +1251,8 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
        for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
                unsigned int read_flags = 0;
 
-               if (read_raw_ref(refname, sha1, &sb_refname, &read_flags)) {
+               if (refs->be->read_raw_ref(refs, refname,
+                                          sha1, &sb_refname, &read_flags)) {
                        *flags |= read_flags;
                        if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
                                return NULL;
@@ -1240,3 +1291,166 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
        errno = ELOOP;
        return NULL;
 }
+
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
+                              unsigned char *sha1, int *flags)
+{
+       return resolve_ref_recursively(get_ref_store(NULL), refname,
+                                      resolve_flags, sha1, flags);
+}
+
+int resolve_gitlink_ref(const char *submodule, const char *refname,
+                       unsigned char *sha1)
+{
+       size_t len = strlen(submodule);
+       struct ref_store *refs;
+       int flags;
+
+       while (len && submodule[len - 1] == '/')
+               len--;
+
+       if (!len)
+               return -1;
+
+       if (submodule[len]) {
+               /* We need to strip off one or more trailing slashes */
+               char *stripped = xmemdupz(submodule, len);
+
+               refs = get_ref_store(stripped);
+               free(stripped);
+       } else {
+               refs = get_ref_store(submodule);
+       }
+
+       if (!refs)
+               return -1;
+
+       if (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||
+           is_null_sha1(sha1))
+               return -1;
+       return 0;
+}
+
+/* A pointer to the ref_store for the main repository: */
+static struct ref_store *main_ref_store;
+
+/* A linked list of ref_stores for submodules: */
+static struct ref_store *submodule_ref_stores;
+
+void base_ref_store_init(struct ref_store *refs,
+                        const struct ref_storage_be *be,
+                        const char *submodule)
+{
+       refs->be = be;
+       if (!submodule) {
+               if (main_ref_store)
+                       die("BUG: main_ref_store initialized twice");
+
+               refs->submodule = "";
+               refs->next = NULL;
+               main_ref_store = refs;
+       } else {
+               if (lookup_ref_store(submodule))
+                       die("BUG: ref_store for submodule '%s' initialized twice",
+                           submodule);
+
+               refs->submodule = xstrdup(submodule);
+               refs->next = submodule_ref_stores;
+               submodule_ref_stores = refs;
+       }
+}
+
+struct ref_store *ref_store_init(const char *submodule)
+{
+       const char *be_name = "files";
+       struct ref_storage_be *be = find_ref_storage_backend(be_name);
+
+       if (!be)
+               die("BUG: reference backend %s is unknown", be_name);
+
+       if (!submodule || !*submodule)
+               return be->init(NULL);
+       else
+               return be->init(submodule);
+}
+
+struct ref_store *lookup_ref_store(const char *submodule)
+{
+       struct ref_store *refs;
+
+       if (!submodule || !*submodule)
+               return main_ref_store;
+
+       for (refs = submodule_ref_stores; refs; refs = refs->next) {
+               if (!strcmp(submodule, refs->submodule))
+                       return refs;
+       }
+
+       return NULL;
+}
+
+struct ref_store *get_ref_store(const char *submodule)
+{
+       struct ref_store *refs;
+
+       if (!submodule || !*submodule) {
+               refs = lookup_ref_store(NULL);
+
+               if (!refs)
+                       refs = ref_store_init(NULL);
+       } else {
+               refs = lookup_ref_store(submodule);
+
+               if (!refs) {
+                       struct strbuf submodule_sb = STRBUF_INIT;
+
+                       strbuf_addstr(&submodule_sb, submodule);
+                       if (is_nonbare_repository_dir(&submodule_sb))
+                               refs = ref_store_init(submodule);
+                       strbuf_release(&submodule_sb);
+               }
+       }
+
+       return refs;
+}
+
+void assert_main_repository(struct ref_store *refs, const char *caller)
+{
+       if (*refs->submodule)
+               die("BUG: %s called for a submodule", caller);
+}
+
+/* backend functions */
+int pack_refs(unsigned int flags)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->pack_refs(refs, flags);
+}
+
+int create_symref(const char *ref_target, const char *refs_heads_master,
+                 const char *logmsg)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->create_symref(refs, ref_target, refs_heads_master,
+                                      logmsg);
+}
+
+int ref_transaction_commit(struct ref_transaction *transaction,
+                          struct strbuf *err)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->transaction_commit(refs, transaction, err);
+}
+
+int verify_refname_available(const char *refname,
+                            const struct string_list *extra,
+                            const struct string_list *skip,
+                            struct strbuf *err)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->verify_refname_available(refs, refname, extra, skip, err);
+}