wrapper.c: add and use fopen_or_warn()
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index fbbd9e1f63a906bff562cc31eb3805afc94f5ed1..df75f8e0d695fc235c1a2497069a9b916a4a04e8 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"
@@ -171,11 +172,23 @@ int refname_is_safe(const char *refname)
        return 1;
 }
 
+char *refs_resolve_refdup(struct ref_store *refs,
+                         const char *refname, int resolve_flags,
+                         unsigned char *sha1, int *flags)
+{
+       const char *result;
+
+       result = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
+                                        sha1, flags);
+       return xstrdup_or_null(result);
+}
+
 char *resolve_refdup(const char *refname, int resolve_flags,
                     unsigned char *sha1, int *flags)
 {
-       return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
-                                                 sha1, flags));
+       return refs_resolve_refdup(get_main_ref_store(),
+                                  refname, resolve_flags,
+                                  sha1, flags);
 }
 
 /* The argument to filter_refs */
@@ -185,13 +198,20 @@ struct ref_filter {
        void *cb_data;
 };
 
-int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
+int refs_read_ref_full(struct ref_store *refs, const char *refname,
+                      int resolve_flags, unsigned char *sha1, int *flags)
 {
-       if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))
+       if (refs_resolve_ref_unsafe(refs, refname, resolve_flags, sha1, flags))
                return 0;
        return -1;
 }
 
+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
+{
+       return refs_read_ref_full(get_main_ref_store(), refname,
+                                 resolve_flags, sha1, flags);
+}
+
 int read_ref(const char *refname, unsigned char *sha1)
 {
        return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
@@ -286,34 +306,52 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_li
        for_each_rawref(warn_if_dangling_symref, &data);
 }
 
+int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_ref_in(refs, "refs/tags/", fn, cb_data);
+}
+
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/tags/", fn, cb_data);
+       return refs_for_each_tag_ref(get_main_ref_store(), fn, cb_data);
 }
 
 int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data);
+       return refs_for_each_tag_ref(get_submodule_ref_store(submodule),
+                                    fn, cb_data);
+}
+
+int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
 }
 
 int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/heads/", fn, cb_data);
+       return refs_for_each_branch_ref(get_main_ref_store(), fn, cb_data);
 }
 
 int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in_submodule(submodule, "refs/heads/", fn, cb_data);
+       return refs_for_each_branch_ref(get_submodule_ref_store(submodule),
+                                       fn, cb_data);
+}
+
+int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
 }
 
 int for_each_remote_ref(each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/remotes/", fn, cb_data);
+       return refs_for_each_remote_ref(get_main_ref_store(), fn, cb_data);
 }
 
 int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in_submodule(submodule, "refs/remotes/", fn, cb_data);
+       return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
+                                       fn, cb_data);
 }
 
 int head_ref_namespaced(each_ref_fn fn, void *cb_data)
@@ -367,11 +405,11 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
 
 const char *prettify_refname(const char *name)
 {
-       return name + (
-               starts_with(name, "refs/heads/") ? 11 :
-               starts_with(name, "refs/tags/") ? 10 :
-               starts_with(name, "refs/remotes/") ? 13 :
-               0);
+       if (skip_prefix(name, "refs/heads/", &name) ||
+           skip_prefix(name, "refs/tags/", &name) ||
+           skip_prefix(name, "refs/remotes/", &name))
+               ; /* nothing */
+       return name;
 }
 
 static const char *ref_rev_parse_rules[] = {
@@ -430,29 +468,31 @@ int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)
 {
        const char **p, *r;
        int refs_found = 0;
+       struct strbuf fullref = STRBUF_INIT;
 
        *ref = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
-               char fullref[PATH_MAX];
                unsigned char sha1_from_ref[20];
                unsigned char *this_result;
                int flag;
 
                this_result = refs_found ? sha1_from_ref : sha1;
-               mksnpath(fullref, sizeof(fullref), *p, len, str);
-               r = resolve_ref_unsafe(fullref, RESOLVE_REF_READING,
+               strbuf_reset(&fullref);
+               strbuf_addf(&fullref, *p, len, str);
+               r = resolve_ref_unsafe(fullref.buf, RESOLVE_REF_READING,
                                       this_result, &flag);
                if (r) {
                        if (!refs_found++)
                                *ref = xstrdup(r);
                        if (!warn_ambiguous_refs)
                                break;
-               } else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD")) {
-                       warning("ignoring dangling symref %s.", fullref);
-               } else if ((flag & REF_ISBROKEN) && strchr(fullref, '/')) {
-                       warning("ignoring broken ref %s.", fullref);
+               } else if ((flag & REF_ISSYMREF) && strcmp(fullref.buf, "HEAD")) {
+                       warning("ignoring dangling symref %s.", fullref.buf);
+               } else if ((flag & REF_ISBROKEN) && strchr(fullref.buf, '/')) {
+                       warning("ignoring broken ref %s.", fullref.buf);
                }
        }
+       strbuf_release(&fullref);
        return refs_found;
 }
 
@@ -461,21 +501,22 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
        char *last_branch = substitute_branch_name(&str, &len);
        const char **p;
        int logs_found = 0;
+       struct strbuf path = STRBUF_INIT;
 
        *log = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
                unsigned char hash[20];
-               char path[PATH_MAX];
                const char *ref, *it;
 
-               mksnpath(path, sizeof(path), *p, len, str);
-               ref = resolve_ref_unsafe(path, RESOLVE_REF_READING,
+               strbuf_reset(&path);
+               strbuf_addf(&path, *p, len, str);
+               ref = resolve_ref_unsafe(path.buf, RESOLVE_REF_READING,
                                         hash, NULL);
                if (!ref)
                        continue;
-               if (reflog_exists(path))
-                       it = path;
-               else if (strcmp(ref, path) && reflog_exists(ref))
+               if (reflog_exists(path.buf))
+                       it = path.buf;
+               else if (strcmp(ref, path.buf) && reflog_exists(ref))
                        it = ref;
                else
                        continue;
@@ -486,6 +527,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
                if (!warn_ambiguous_refs)
                        break;
        }
+       strbuf_release(&path);
        free(last_branch);
        return logs_found;
 }
@@ -593,16 +635,20 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1
        return 0;
 }
 
-int delete_ref(const char *msg, const char *refname,
-              const unsigned char *old_sha1, unsigned int flags)
+int refs_delete_ref(struct ref_store *refs, const char *msg,
+                   const char *refname,
+                   const unsigned char *old_sha1,
+                   unsigned int flags)
 {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
 
-       if (ref_type(refname) == REF_TYPE_PSEUDOREF)
+       if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+               assert(refs == get_main_ref_store());
                return delete_pseudoref(refname, old_sha1);
+       }
 
-       transaction = ref_transaction_begin(&err);
+       transaction = ref_store_transaction_begin(refs, &err);
        if (!transaction ||
            ref_transaction_delete(transaction, refname, old_sha1,
                                   flags, msg, &err) ||
@@ -617,6 +663,13 @@ int delete_ref(const char *msg, const char *refname,
        return 0;
 }
 
+int delete_ref(const char *msg, const char *refname,
+              const unsigned char *old_sha1, unsigned int flags)
+{
+       return refs_delete_ref(get_main_ref_store(), msg, refname,
+                              old_sha1, flags);
+}
+
 int copy_reflog_msg(char *buf, const char *msg)
 {
        char *cp = buf;
@@ -776,11 +829,20 @@ int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time,
        return 1;
 }
 
-struct ref_transaction *ref_transaction_begin(struct strbuf *err)
+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
+                                                   struct strbuf *err)
 {
+       struct ref_transaction *tr;
        assert(err);
 
-       return xcalloc(1, sizeof(struct ref_transaction));
+       tr = xcalloc(1, sizeof(struct ref_transaction));
+       tr->ref_store = refs;
+       return tr;
+}
+
+struct ref_transaction *ref_transaction_begin(struct strbuf *err)
+{
+       return ref_store_transaction_begin(get_main_ref_store(), err);
 }
 
 void ref_transaction_free(struct ref_transaction *transaction)
@@ -897,18 +959,20 @@ int update_ref_oid(const char *msg, const char *refname,
                old_oid ? old_oid->hash : NULL, flags, onerr);
 }
 
-int update_ref(const char *msg, const char *refname,
-              const unsigned char *new_sha1, const unsigned char *old_sha1,
-              unsigned int flags, enum action_on_err onerr)
+int refs_update_ref(struct ref_store *refs, const char *msg,
+                   const char *refname, const unsigned char *new_sha1,
+                   const unsigned char *old_sha1, unsigned int flags,
+                   enum action_on_err onerr)
 {
        struct ref_transaction *t = NULL;
        struct strbuf err = STRBUF_INIT;
        int ret = 0;
 
        if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+               assert(refs == get_main_ref_store());
                ret = write_pseudoref(refname, new_sha1, old_sha1, &err);
        } else {
-               t = ref_transaction_begin(&err);
+               t = ref_store_transaction_begin(refs, &err);
                if (!t ||
                    ref_transaction_update(t, refname, new_sha1, old_sha1,
                                           flags, msg, &err) ||
@@ -939,12 +1003,22 @@ int update_ref(const char *msg, const char *refname,
        return 0;
 }
 
+int update_ref(const char *msg, const char *refname,
+              const unsigned char *new_sha1,
+              const unsigned char *old_sha1,
+              unsigned int flags, enum action_on_err onerr)
+{
+       return refs_update_ref(get_main_ref_store(), msg, refname, new_sha1,
+                              old_sha1, flags, onerr);
+}
+
 char *shorten_unambiguous_ref(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) {
                /*
@@ -1003,7 +1077,6 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                 */
                for (j = 0; j < rules_to_fail; j++) {
                        const char *rule = ref_rev_parse_rules[j];
-                       char refname[PATH_MAX];
 
                        /* skip matched rule */
                        if (i == j)
@@ -1014,9 +1087,10 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                         * (with this previous rule) to a valid ref
                         * read_ref() returns 0 on success
                         */
-                       mksnpath(refname, sizeof(refname),
-                                rule, short_name_len, short_name);
-                       if (ref_exists(refname))
+                       strbuf_reset(&resolved_buf);
+                       strbuf_addf(&resolved_buf, rule,
+                                   short_name_len, short_name);
+                       if (ref_exists(resolved_buf.buf))
                                break;
                }
 
@@ -1024,10 +1098,13 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                 * short name is non-ambiguous if all previous rules
                 * haven't resolved to a valid ref
                 */
-               if (j == rules_to_fail)
+               if (j == rules_to_fail) {
+                       strbuf_release(&resolved_buf);
                        return short_name;
+               }
        }
 
+       strbuf_release(&resolved_buf);
        free(short_name);
        return xstrdup(refname);
 }
@@ -1120,14 +1197,17 @@ const char *find_descendant_ref(const char *dirname,
        return NULL;
 }
 
-int rename_ref_available(const char *old_refname, const char *new_refname)
+int refs_rename_ref_available(struct ref_store *refs,
+                             const char *old_refname,
+                             const char *new_refname)
 {
        struct string_list skip = STRING_LIST_INIT_NODUP;
        struct strbuf err = STRBUF_INIT;
        int ok;
 
        string_list_insert(&skip, old_refname);
-       ok = !verify_refname_available(new_refname, NULL, &skip, &err);
+       ok = !refs_verify_refname_available(refs, new_refname,
+                                           NULL, &skip, &err);
        if (!ok)
                error("%s", err.buf);
 
@@ -1159,6 +1239,18 @@ int head_ref(each_ref_fn fn, void *cb_data)
        return head_ref_submodule(NULL, fn, cb_data);
 }
 
+struct ref_iterator *refs_ref_iterator_begin(
+               struct ref_store *refs,
+               const char *prefix, int trim, int flags)
+{
+       struct ref_iterator *iter;
+
+       iter = refs->be->iterator_begin(refs, prefix, flags);
+       iter = prefix_ref_iterator_begin(iter, prefix, trim);
+
+       return iter;
+}
+
 /*
  * Call fn for each reference in the specified submodule for which the
  * refname begins with prefix. If trim is non-zero, then trim that
@@ -1168,34 +1260,43 @@ int head_ref(each_ref_fn fn, void *cb_data)
  * 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,
+static int do_for_each_ref(struct ref_store *refs, const char *prefix,
                           each_ref_fn fn, int trim, int flags, void *cb_data)
 {
-       struct ref_store *refs = get_submodule_ref_store(submodule);
        struct ref_iterator *iter;
 
        if (!refs)
                return 0;
 
-       iter = refs->be->iterator_begin(refs, prefix, flags);
-       iter = prefix_ref_iterator_begin(iter, prefix, trim);
+       iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
 
        return do_for_each_ref_iterator(iter, fn, cb_data);
 }
 
+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);
+}
+
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
+       return refs_for_each_ref(get_main_ref_store(), fn, cb_data);
 }
 
 int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
+       return refs_for_each_ref(get_submodule_ref_store(submodule), fn, 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);
 }
 
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+       return refs_for_each_ref_in(get_main_ref_store(), prefix, fn, cb_data);
 }
 
 int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
@@ -1204,19 +1305,23 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig
 
        if (broken)
                flag = DO_FOR_EACH_INCLUDE_BROKEN;
-       return do_for_each_ref(NULL, prefix, fn, 0, flag, cb_data);
+       return do_for_each_ref(get_main_ref_store(),
+                              prefix, fn, 0, flag, cb_data);
 }
 
 int for_each_ref_in_submodule(const char *submodule, const char *prefix,
-               each_ref_fn fn, void *cb_data)
+                             each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
+       return refs_for_each_ref_in(get_submodule_ref_store(submodule),
+                                   prefix, fn, cb_data);
 }
 
 int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, git_replace_ref_base, fn,
-                              strlen(git_replace_ref_base), 0, cb_data);
+       return do_for_each_ref(get_main_ref_store(),
+                              git_replace_ref_base, fn,
+                              strlen(git_replace_ref_base),
+                              0, cb_data);
 }
 
 int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
@@ -1224,19 +1329,32 @@ int for_each_namespaced_ref(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(NULL, buf.buf, fn, 0, 0, cb_data);
+       ret = do_for_each_ref(get_main_ref_store(),
+                             buf.buf, fn, 0, 0, cb_data);
        strbuf_release(&buf);
        return ret;
 }
 
-int for_each_rawref(each_ref_fn fn, void *cb_data)
+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, "", fn, 0,
+       return do_for_each_ref(refs, "", fn, 0,
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
+int for_each_rawref(each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_rawref(get_main_ref_store(), fn, cb_data);
+}
+
+int refs_read_raw_ref(struct ref_store *ref_store,
+                     const char *refname, unsigned char *sha1,
+                     struct strbuf *referent, unsigned int *type)
+{
+       return ref_store->be->read_raw_ref(ref_store, refname, sha1, referent, type);
+}
+
 /* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_recursively(struct ref_store *refs,
+const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                                    const char *refname,
                                    int resolve_flags,
                                    unsigned char *sha1, int *flags)
@@ -1271,8 +1389,8 @@ const char *resolve_ref_recursively(struct ref_store *refs,
        for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
                unsigned int read_flags = 0;
 
-               if (refs->be->read_raw_ref(refs, refname,
-                                          sha1, &sb_refname, &read_flags)) {
+               if (refs_read_raw_ref(refs, refname,
+                                     sha1, &sb_refname, &read_flags)) {
                        *flags |= read_flags;
                        if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
                                return NULL;
@@ -1323,7 +1441,7 @@ int refs_init_db(struct strbuf *err)
 const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
                               unsigned char *sha1, int *flags)
 {
-       return resolve_ref_recursively(get_main_ref_store(), refname,
+       return refs_resolve_ref_unsafe(get_main_ref_store(), refname,
                                       resolve_flags, sha1, flags);
 }
 
@@ -1353,7 +1471,7 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
        if (!refs)
                return -1;
 
-       if (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||
+       if (!refs_resolve_ref_unsafe(refs, refname, 0, sha1, &flags) ||
            is_null_sha1(sha1))
                return -1;
        return 0;
@@ -1506,50 +1624,144 @@ void base_ref_store_init(struct ref_store *refs,
 }
 
 /* backend functions */
-int pack_refs(unsigned int flags)
+int refs_pack_refs(struct ref_store *refs, unsigned int flags)
 {
-       struct ref_store *refs = get_main_ref_store();
-
        return refs->be->pack_refs(refs, flags);
 }
 
+int refs_peel_ref(struct ref_store *refs, const char *refname,
+                 unsigned char *sha1)
+{
+       return refs->be->peel_ref(refs, refname, sha1);
+}
+
 int peel_ref(const char *refname, unsigned char *sha1)
 {
-       struct ref_store *refs = get_main_ref_store();
+       return refs_peel_ref(get_main_ref_store(), refname, sha1);
+}
 
-       return refs->be->peel_ref(refs, refname, sha1);
+int refs_create_symref(struct ref_store *refs,
+                      const char *ref_target,
+                      const char *refs_heads_master,
+                      const char *logmsg)
+{
+       return refs->be->create_symref(refs, ref_target,
+                                      refs_heads_master,
+                                      logmsg);
 }
 
 int create_symref(const char *ref_target, const char *refs_heads_master,
                  const char *logmsg)
 {
-       struct ref_store *refs = get_main_ref_store();
-
-       return refs->be->create_symref(refs, ref_target, refs_heads_master,
-                                      logmsg);
+       return refs_create_symref(get_main_ref_store(), ref_target,
+                                 refs_heads_master, logmsg);
 }
 
 int ref_transaction_commit(struct ref_transaction *transaction,
                           struct strbuf *err)
 {
-       struct ref_store *refs = get_main_ref_store();
+       struct ref_store *refs = transaction->ref_store;
+
+       if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
+               strbuf_addstr(err,
+                             _("ref updates forbidden inside quarantine environment"));
+               return -1;
+       }
 
        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)
+int refs_verify_refname_available(struct ref_store *refs,
+                                 const char *refname,
+                                 const struct string_list *extras,
+                                 const struct string_list *skip,
+                                 struct strbuf *err)
 {
-       struct ref_store *refs = get_main_ref_store();
+       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;
+       }
 
-       return refs->be->verify_refname_available(refs, refname, extra, skip, err);
+       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 for_each_reflog(each_ref_fn fn, void *cb_data)
+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       struct ref_store *refs = get_main_ref_store();
        struct ref_iterator *iter;
 
        iter = refs->be->reflog_iterator_begin(refs);
@@ -1557,43 +1769,84 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
        return do_for_each_ref_iterator(iter, fn, cb_data);
 }
 
-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
-                               void *cb_data)
+int for_each_reflog(each_ref_fn fn, void *cb_data)
 {
-       struct ref_store *refs = get_main_ref_store();
+       return refs_for_each_reflog(get_main_ref_store(), fn, cb_data);
+}
 
+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
+                                    const char *refname,
+                                    each_reflog_ent_fn fn,
+                                    void *cb_data)
+{
        return refs->be->for_each_reflog_ent_reverse(refs, refname,
                                                     fn, cb_data);
 }
 
+int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
+                               void *cb_data)
+{
+       return refs_for_each_reflog_ent_reverse(get_main_ref_store(),
+                                               refname, fn, cb_data);
+}
+
+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
+                            each_reflog_ent_fn fn, void *cb_data)
+{
+       return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
+}
+
 int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,
                        void *cb_data)
 {
-       struct ref_store *refs = get_main_ref_store();
+       return refs_for_each_reflog_ent(get_main_ref_store(), refname,
+                                       fn, cb_data);
+}
 
-       return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
+int refs_reflog_exists(struct ref_store *refs, const char *refname)
+{
+       return refs->be->reflog_exists(refs, refname);
 }
 
 int reflog_exists(const char *refname)
 {
-       struct ref_store *refs = get_main_ref_store();
+       return refs_reflog_exists(get_main_ref_store(), refname);
+}
 
-       return refs->be->reflog_exists(refs, refname);
+int refs_create_reflog(struct ref_store *refs, const char *refname,
+                      int force_create, struct strbuf *err)
+{
+       return refs->be->create_reflog(refs, refname, force_create, err);
 }
 
 int safe_create_reflog(const char *refname, int force_create,
                       struct strbuf *err)
 {
-       struct ref_store *refs = get_main_ref_store();
+       return refs_create_reflog(get_main_ref_store(), refname,
+                                 force_create, err);
+}
 
-       return refs->be->create_reflog(refs, refname, force_create, err);
+int refs_delete_reflog(struct ref_store *refs, const char *refname)
+{
+       return refs->be->delete_reflog(refs, refname);
 }
 
 int delete_reflog(const char *refname)
 {
-       struct ref_store *refs = get_main_ref_store();
+       return refs_delete_reflog(get_main_ref_store(), refname);
+}
 
-       return refs->be->delete_reflog(refs, refname);
+int refs_reflog_expire(struct ref_store *refs,
+                      const char *refname, const unsigned char *sha1,
+                      unsigned int flags,
+                      reflog_expiry_prepare_fn prepare_fn,
+                      reflog_expiry_should_prune_fn should_prune_fn,
+                      reflog_expiry_cleanup_fn cleanup_fn,
+                      void *policy_cb_data)
+{
+       return refs->be->reflog_expire(refs, refname, sha1, flags,
+                                      prepare_fn, should_prune_fn,
+                                      cleanup_fn, policy_cb_data);
 }
 
 int reflog_expire(const char *refname, const unsigned char *sha1,
@@ -1603,31 +1856,38 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
                  reflog_expiry_cleanup_fn cleanup_fn,
                  void *policy_cb_data)
 {
-       struct ref_store *refs = get_main_ref_store();
-
-       return refs->be->reflog_expire(refs, refname, sha1, flags,
-                                      prepare_fn, should_prune_fn,
-                                      cleanup_fn, policy_cb_data);
+       return refs_reflog_expire(get_main_ref_store(),
+                                 refname, sha1, flags,
+                                 prepare_fn, should_prune_fn,
+                                 cleanup_fn, policy_cb_data);
 }
 
 int initial_ref_transaction_commit(struct ref_transaction *transaction,
                                   struct strbuf *err)
 {
-       struct ref_store *refs = get_main_ref_store();
+       struct ref_store *refs = transaction->ref_store;
 
        return refs->be->initial_transaction_commit(refs, transaction, err);
 }
 
-int delete_refs(struct string_list *refnames, unsigned int flags)
+int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,
+                    unsigned int flags)
 {
-       struct ref_store *refs = get_main_ref_store();
-
        return refs->be->delete_refs(refs, refnames, flags);
 }
 
-int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+int delete_refs(struct string_list *refnames, unsigned int flags)
 {
-       struct ref_store *refs = get_main_ref_store();
+       return refs_delete_refs(get_main_ref_store(), refnames, flags);
+}
 
+int refs_rename_ref(struct ref_store *refs, const char *oldref,
+                   const char *newref, const char *logmsg)
+{
        return refs->be->rename_ref(refs, oldref, newref, logmsg);
 }
+
+int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+       return refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg);
+}