]> git.ipfire.org Git - thirdparty/git.git/blobdiff - refs.c
The thirteenth batch
[thirdparty/git.git] / refs.c
diff --git a/refs.c b/refs.c
index 55d2e0b2cb9e959443e98eb329fdf97eff9073a9..31032588e0efe293df33cecdcb0ba9b89d90197f 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -6,7 +6,7 @@
 #include "advice.h"
 #include "config.h"
 #include "environment.h"
-#include "hashmap.h"
+#include "strmap.h"
 #include "gettext.h"
 #include "hex.h"
 #include "lockfile.h"
@@ -19,7 +19,6 @@
 #include "object-store-ll.h"
 #include "object.h"
 #include "path.h"
-#include "tag.h"
 #include "submodule.h"
 #include "worktree.h"
 #include "strvec.h"
@@ -384,14 +383,6 @@ char *refs_resolve_refdup(struct ref_store *refs,
        return xstrdup_or_null(result);
 }
 
-char *resolve_refdup(const char *refname, int resolve_flags,
-                    struct object_id *oid, int *flags)
-{
-       return refs_resolve_refdup(get_main_ref_store(the_repository),
-                                  refname, resolve_flags,
-                                  oid, flags);
-}
-
 /* The argument to for_each_filter_refs */
 struct for_each_ref_filter {
        const char *pattern;
@@ -400,19 +391,18 @@ struct for_each_ref_filter {
        void *cb_data;
 };
 
-int read_ref_full(const char *refname, int resolve_flags, struct object_id *oid, int *flags)
+int refs_read_ref_full(struct ref_store *refs, const char *refname,
+                      int resolve_flags, struct object_id *oid, int *flags)
 {
-       struct ref_store *refs = get_main_ref_store(the_repository);
-
        if (refs_resolve_ref_unsafe(refs, refname, resolve_flags,
                                    oid, flags))
                return 0;
        return -1;
 }
 
-int read_ref(const char *refname, struct object_id *oid)
+int refs_read_ref(struct ref_store *refs, const char *refname, struct object_id *oid)
 {
-       return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL);
+       return refs_read_ref_full(refs, refname, RESOLVE_REF_READING, oid, NULL);
 }
 
 int refs_ref_exists(struct ref_store *refs, const char *refname)
@@ -421,11 +411,6 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)
                                         NULL, NULL);
 }
 
-int ref_exists(const char *refname)
-{
-       return refs_ref_exists(get_main_ref_store(the_repository), refname);
-}
-
 static int for_each_filter_refs(const char *refname,
                                const struct object_id *oid,
                                int flags, void *data)
@@ -439,28 +424,8 @@ static int for_each_filter_refs(const char *refname,
        return filter->fn(refname, oid, flags, filter->cb_data);
 }
 
-enum peel_status peel_object(const struct object_id *name, struct object_id *oid)
-{
-       struct object *o = lookup_unknown_object(the_repository, name);
-
-       if (o->type == OBJ_NONE) {
-               int type = oid_object_info(the_repository, name, NULL);
-               if (type < 0 || !object_as_type(o, type, 0))
-                       return PEEL_INVALID;
-       }
-
-       if (o->type != OBJ_TAG)
-               return PEEL_NON_TAG;
-
-       o = deref_tag_noverify(o);
-       if (!o)
-               return PEEL_INVALID;
-
-       oidcpy(oid, &o->oid);
-       return PEEL_PEELED;
-}
-
 struct warn_if_dangling_data {
+       struct ref_store *refs;
        FILE *fp;
        const char *refname;
        const struct string_list *refnames;
@@ -477,7 +442,7 @@ static int warn_if_dangling_symref(const char *refname,
        if (!(flags & REF_ISSYMREF))
                return 0;
 
-       resolves_to = resolve_ref_unsafe(refname, 0, NULL, NULL);
+       resolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL);
        if (!resolves_to
            || (d->refname
                ? strcmp(resolves_to, d->refname)
@@ -490,26 +455,28 @@ static int warn_if_dangling_symref(const char *refname,
        return 0;
 }
 
-void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
+void refs_warn_dangling_symref(struct ref_store *refs, FILE *fp,
+                              const char *msg_fmt, const char *refname)
 {
-       struct warn_if_dangling_data data;
-
-       data.fp = fp;
-       data.refname = refname;
-       data.refnames = NULL;
-       data.msg_fmt = msg_fmt;
-       for_each_rawref(warn_if_dangling_symref, &data);
+       struct warn_if_dangling_data data = {
+               .refs = refs,
+               .fp = fp,
+               .refname = refname,
+               .msg_fmt = msg_fmt,
+       };
+       refs_for_each_rawref(refs, warn_if_dangling_symref, &data);
 }
 
-void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
+void refs_warn_dangling_symrefs(struct ref_store *refs, FILE *fp,
+                               const char *msg_fmt, const struct string_list *refnames)
 {
-       struct warn_if_dangling_data data;
-
-       data.fp = fp;
-       data.refname = NULL;
-       data.refnames = refnames;
-       data.msg_fmt = msg_fmt;
-       for_each_rawref(warn_if_dangling_symref, &data);
+       struct warn_if_dangling_data data = {
+               .refs = refs,
+               .fp = fp,
+               .refnames = refnames,
+               .msg_fmt = msg_fmt,
+       };
+       refs_for_each_rawref(refs, warn_if_dangling_symref, &data);
 }
 
 int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
@@ -517,32 +484,17 @@ 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 refs_for_each_tag_ref(get_main_ref_store(the_repository), 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 refs_for_each_branch_ref(get_main_ref_store(the_repository), 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 refs_for_each_remote_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
-int head_ref_namespaced(each_ref_fn fn, void *cb_data)
+int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        struct strbuf buf = STRBUF_INIT;
        int ret = 0;
@@ -550,7 +502,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
        int flag;
 
        strbuf_addf(&buf, "%sHEAD", get_git_namespace());
-       if (!read_ref_full(buf.buf, RESOLVE_REF_READING, &oid, &flag))
+       if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))
                ret = fn(buf.buf, &oid, flag, cb_data);
        strbuf_release(&buf);
 
@@ -583,8 +535,8 @@ void normalize_glob_ref(struct string_list_item *item, const char *prefix,
        strbuf_release(&normalized_pattern);
 }
 
-int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
-       const char *prefix, void *cb_data)
+int refs_for_each_glob_ref_in(struct ref_store *refs, each_ref_fn fn,
+                             const char *pattern, const char *prefix, void *cb_data)
 {
        struct strbuf real_pattern = STRBUF_INIT;
        struct for_each_ref_filter filter;
@@ -607,15 +559,16 @@ 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(for_each_filter_refs, &filter);
+       ret = refs_for_each_ref(refs, for_each_filter_refs, &filter);
 
        strbuf_release(&real_pattern);
        return ret;
 }
 
-int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
+int refs_for_each_glob_ref(struct ref_store *refs, each_ref_fn fn,
+                          const char *pattern, void *cb_data)
 {
-       return for_each_glob_ref_in(fn, pattern, NULL, cb_data);
+       return refs_for_each_glob_ref_in(refs, fn, pattern, NULL, cb_data);
 }
 
 const char *prettify_refname(const char *name)
@@ -711,16 +664,6 @@ char *repo_default_branch_name(struct repository *r, int quiet)
        return ret;
 }
 
-const char *git_default_branch_name(int quiet)
-{
-       static char *ret;
-
-       if (!ret)
-               ret = repo_default_branch_name(the_repository, quiet);
-
-       return ret;
-}
-
 /*
  * *string and *len will only be substituted, and *string returned (for
  * later free()ing) if the string passed in is a magic short-hand form
@@ -832,11 +775,6 @@ int repo_dwim_log(struct repository *r, const char *str, int len,
        return logs_found;
 }
 
-int dwim_log(const char *str, int len, struct object_id *oid, char **log)
-{
-       return repo_dwim_log(the_repository, str, len, oid, log);
-}
-
 int is_per_worktree_ref(const char *refname)
 {
        return starts_with(refname, "refs/worktree/") ||
@@ -844,7 +782,22 @@ int is_per_worktree_ref(const char *refname)
               starts_with(refname, "refs/rewritten/");
 }
 
-static int is_pseudoref_syntax(const char *refname)
+int is_pseudo_ref(const char *refname)
+{
+       static const char * const pseudo_refs[] = {
+               "FETCH_HEAD",
+               "MERGE_HEAD",
+       };
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(pseudo_refs); i++)
+               if (!strcmp(refname, pseudo_refs[i]))
+                       return 1;
+
+       return 0;
+}
+
+static int is_root_ref_syntax(const char *refname)
 {
        const char *c;
 
@@ -853,56 +806,37 @@ static int is_pseudoref_syntax(const char *refname)
                        return 0;
        }
 
-       /*
-        * HEAD is not a pseudoref, but it certainly uses the
-        * pseudoref syntax.
-        */
        return 1;
 }
 
-int is_pseudoref(struct ref_store *refs, const char *refname)
+int is_root_ref(const char *refname)
 {
-       static const char *const irregular_pseudorefs[] = {
+       static const char *const irregular_root_refs[] = {
+               "HEAD",
                "AUTO_MERGE",
                "BISECT_EXPECTED_REV",
                "NOTES_MERGE_PARTIAL",
                "NOTES_MERGE_REF",
                "MERGE_AUTOSTASH",
        };
-       struct object_id oid;
        size_t i;
 
-       if (!is_pseudoref_syntax(refname))
+       if (!is_root_ref_syntax(refname) ||
+           is_pseudo_ref(refname))
                return 0;
 
-       if (ends_with(refname, "_HEAD")) {
-               refs_resolve_ref_unsafe(refs, refname,
-                                       RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-                                       &oid, NULL);
-               return !is_null_oid(&oid);
-       }
-
-       for (i = 0; i < ARRAY_SIZE(irregular_pseudorefs); i++)
-               if (!strcmp(refname, irregular_pseudorefs[i])) {
-                       refs_resolve_ref_unsafe(refs, refname,
-                                               RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-                                               &oid, NULL);
-                       return !is_null_oid(&oid);
-               }
-
-       return 0;
-}
+       if (ends_with(refname, "_HEAD"))
+               return 1;
 
-int is_headref(struct ref_store *refs, const char *refname)
-{
-       if (!strcmp(refname, "HEAD"))
-               return refs_ref_exists(refs, refname);
+       for (i = 0; i < ARRAY_SIZE(irregular_root_refs); i++)
+               if (!strcmp(refname, irregular_root_refs[i]))
+                       return 1;
 
        return 0;
 }
 
 static int is_current_worktree_ref(const char *ref) {
-       return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref);
+       return is_root_ref_syntax(ref) || is_per_worktree_ref(ref);
 }
 
 enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref,
@@ -991,13 +925,6 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
        return 0;
 }
 
-int delete_ref(const char *msg, const char *refname,
-              const struct object_id *old_oid, unsigned int flags)
-{
-       return refs_delete_ref(get_main_ref_store(the_repository), msg, refname,
-                              old_oid, flags);
-}
-
 static void copy_reflog_msg(struct strbuf *sb, const char *msg)
 {
        char c;
@@ -1190,11 +1117,6 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
        return tr;
 }
 
-struct ref_transaction *ref_transaction_begin(struct strbuf *err)
-{
-       return ref_store_transaction_begin(get_main_ref_store(the_repository), err);
-}
-
 void ref_transaction_free(struct ref_transaction *transaction)
 {
        size_t i;
@@ -1217,6 +1139,8 @@ void ref_transaction_free(struct ref_transaction *transaction)
 
        for (i = 0; i < transaction->nr; i++) {
                free(transaction->updates[i]->msg);
+               free((char *)transaction->updates[i]->new_target);
+               free((char *)transaction->updates[i]->old_target);
                free(transaction->updates[i]);
        }
        free(transaction->updates);
@@ -1228,6 +1152,7 @@ struct ref_update *ref_transaction_add_update(
                const char *refname, unsigned int flags,
                const struct object_id *new_oid,
                const struct object_id *old_oid,
+               const char *new_target, const char *old_target,
                const char *msg)
 {
        struct ref_update *update;
@@ -1235,16 +1160,24 @@ struct ref_update *ref_transaction_add_update(
        if (transaction->state != REF_TRANSACTION_OPEN)
                BUG("update called for transaction that is not open");
 
+       if (old_oid && old_target)
+               BUG("only one of old_oid and old_target should be non NULL");
+       if (new_oid && new_target)
+               BUG("only one of new_oid and new_target should be non NULL");
+
        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)
+       update->new_target = xstrdup_or_null(new_target);
+       update->old_target = xstrdup_or_null(old_target);
+       if ((flags & REF_HAVE_NEW) && new_oid)
                oidcpy(&update->new_oid, new_oid);
-       if (flags & REF_HAVE_OLD)
+       if ((flags & REF_HAVE_OLD) && old_oid)
                oidcpy(&update->old_oid, old_oid);
+
        update->msg = normalize_reflog_message(msg);
        return update;
 }
@@ -1253,6 +1186,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
                           const char *refname,
                           const struct object_id *new_oid,
                           const struct object_id *old_oid,
+                          const char *new_target,
+                          const char *old_target,
                           unsigned int flags, const char *msg,
                           struct strbuf *err)
 {
@@ -1267,6 +1202,13 @@ int ref_transaction_update(struct ref_transaction *transaction,
                return -1;
        }
 
+       if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
+           is_pseudo_ref(refname)) {
+               strbuf_addf(err, _("refusing to update pseudoref '%s'"),
+                           refname);
+               return -1;
+       }
+
        if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
                BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
 
@@ -1278,9 +1220,11 @@ int ref_transaction_update(struct ref_transaction *transaction,
        flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS;
 
        flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
+       flags |= (new_target ? REF_HAVE_NEW : 0) | (old_target ? REF_HAVE_OLD : 0);
 
        ref_transaction_add_update(transaction, refname, flags,
-                                  new_oid, old_oid, msg);
+                                  new_oid, old_oid, new_target,
+                                  old_target, msg);
        return 0;
 }
 
@@ -1295,7 +1239,8 @@ int ref_transaction_create(struct ref_transaction *transaction,
                return 1;
        }
        return ref_transaction_update(transaction, refname, new_oid,
-                                     null_oid(), flags, msg, err);
+                                     null_oid(), NULL, NULL, flags,
+                                     msg, err);
 }
 
 int ref_transaction_delete(struct ref_transaction *transaction,
@@ -1308,7 +1253,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
                BUG("delete called with old_oid set to zeros");
        return ref_transaction_update(transaction, refname,
                                      null_oid(), old_oid,
-                                     flags, msg, err);
+                                     NULL, NULL, flags,
+                                     msg, err);
 }
 
 int ref_transaction_verify(struct ref_transaction *transaction,
@@ -1321,6 +1267,7 @@ int ref_transaction_verify(struct ref_transaction *transaction,
                BUG("verify called with old_oid set to NULL");
        return ref_transaction_update(transaction, refname,
                                      NULL, old_oid,
+                                     NULL, NULL,
                                      flags, NULL, err);
 }
 
@@ -1335,8 +1282,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
 
        t = ref_store_transaction_begin(refs, &err);
        if (!t ||
-           ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
-                                  &err) ||
+           ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
+                                  flags, msg, &err) ||
            ref_transaction_commit(t, &err)) {
                ret = 1;
                ref_transaction_free(t);
@@ -1363,15 +1310,6 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
        return 0;
 }
 
-int update_ref(const char *msg, const char *refname,
-              const struct object_id *new_oid,
-              const struct object_id *old_oid,
-              unsigned int flags, enum action_on_err onerr)
-{
-       return refs_update_ref(get_main_ref_store(the_repository), msg, refname, new_oid,
-                              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
@@ -1473,12 +1411,6 @@ char *refs_shorten_unambiguous_ref(struct ref_store *refs,
        return xstrdup(refname);
 }
 
-char *shorten_unambiguous_ref(const char *refname, int strict)
-{
-       return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
-                                           refname, strict);
-}
-
 int parse_hide_refs_config(const char *var, const char *value, const char *section,
                           struct strvec *hide_refs)
 {
@@ -1597,11 +1529,6 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
        return 0;
 }
 
-int head_ref(each_ref_fn fn, void *cb_data)
-{
-       return refs_head_ref(get_main_ref_store(the_repository), fn, cb_data);
-}
-
 struct ref_iterator *refs_ref_iterator_begin(
                struct ref_store *refs,
                const char *prefix,
@@ -1633,53 +1560,12 @@ struct ref_iterator *refs_ref_iterator_begin(
        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
- * 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_repo_ref(struct repository *r, const char *prefix,
-                               each_repo_ref_fn fn, int trim, int flags,
-                               void *cb_data)
-{
-       struct ref_iterator *iter;
-       struct ref_store *refs = get_main_ref_store(r);
-
-       if (!refs)
-               return 0;
-
-       iter = refs_ref_iterator_begin(refs, prefix, NULL, trim, flags);
-
-       return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
-}
-
-struct do_for_each_ref_help {
-       each_ref_fn *fn;
-       void *cb_data;
-};
-
-static int do_for_each_ref_helper(struct repository *r UNUSED,
-                                 const char *refname,
-                                 const struct object_id *oid,
-                                 int flags,
-                                 void *cb_data)
-{
-       struct do_for_each_ref_help *hp = cb_data;
-
-       return hp->fn(refname, oid, flags, hp->cb_data);
-}
-
 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)
 {
        struct ref_iterator *iter;
-       struct do_for_each_ref_help hp = { fn, cb_data };
 
        if (!refs)
                return 0;
@@ -1687,8 +1573,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
        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);
+       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)
@@ -1696,28 +1581,12 @@ int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *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)
-{
-       return refs_for_each_ref(get_main_ref_store(the_repository), 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, NULL, fn, strlen(prefix), 0, cb_data);
 }
 
-int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, 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, 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)
@@ -1725,22 +1594,22 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
        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)
+int refs_for_each_replace_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
-       return do_for_each_repo_ref(r, git_replace_ref_base, fn,
-                                   strlen(git_replace_ref_base),
-                                   DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+       return do_for_each_ref(refs, git_replace_ref_base, NULL, fn,
+                              strlen(git_replace_ref_base),
+                              DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
-int for_each_namespaced_ref(const char **exclude_patterns,
-                           each_ref_fn fn, void *cb_data)
+int refs_for_each_namespaced_ref(struct ref_store *refs,
+                                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, exclude_patterns, fn, 0, 0, cb_data);
+       ret = do_for_each_ref(refs, buf.buf, exclude_patterns, fn, 0, 0, cb_data);
        strbuf_release(&buf);
        return ret;
 }
@@ -1751,11 +1620,6 @@ int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
                               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(the_repository), fn, cb_data);
-}
-
 int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
                                    void *cb_data)
 {
@@ -1876,43 +1740,12 @@ done:
        return result;
 }
 
-static int is_special_ref(const char *refname)
-{
-       /*
-        * Special references are refs that have different semantics compared
-        * to "normal" refs. These refs can thus not be stored in the ref
-        * backend, but must always be accessed via the filesystem. The
-        * following refs are special:
-        *
-        * - 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.
-        *
-        * Reading, 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[] = {
-               "FETCH_HEAD",
-               "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 (is_special_ref(refname))
+       if (is_pseudo_ref(refname))
                return refs_read_special_head(ref_store, refname, oid, referent,
                                              type, failure_errno);
 
@@ -2016,26 +1849,19 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
 }
 
 /* backend functions */
-int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err)
+int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *err)
 {
-       return refs->be->init_db(refs, flags, err);
+       return refs->be->create_on_disk(refs, flags, err);
 }
 
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
-                              struct object_id *oid, int *flags)
-{
-       return refs_resolve_ref_unsafe(get_main_ref_store(the_repository), refname,
-                                      resolve_flags, oid, flags);
-}
-
-int resolve_gitlink_ref(const char *submodule, const char *refname,
-                       struct object_id *oid)
+int repo_resolve_gitlink_ref(struct repository *r,
+                            const char *submodule, const char *refname,
+                            struct object_id *oid)
 {
        struct ref_store *refs;
        int flags;
 
-       refs = get_submodule_ref_store(submodule);
-
+       refs = repo_get_submodule_ref_store(r, submodule);
        if (!refs)
                return -1;
 
@@ -2045,66 +1871,21 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
        return 0;
 }
 
-struct ref_store_hash_entry
-{
-       struct hashmap_entry ent;
-
-       struct ref_store *refs;
-
-       /* NUL-terminated identifier of the ref store: */
-       char name[FLEX_ARRAY];
-};
-
-static int ref_store_hash_cmp(const void *cmp_data UNUSED,
-                             const struct hashmap_entry *eptr,
-                             const struct hashmap_entry *entry_or_key,
-                             const void *keydata)
-{
-       const struct ref_store_hash_entry *e1, *e2;
-       const char *name;
-
-       e1 = container_of(eptr, const struct ref_store_hash_entry, ent);
-       e2 = container_of(entry_or_key, const struct ref_store_hash_entry, ent);
-       name = keydata ? keydata : e2->name;
-
-       return strcmp(e1->name, name);
-}
-
-static struct ref_store_hash_entry *alloc_ref_store_hash_entry(
-               const char *name, struct ref_store *refs)
-{
-       struct ref_store_hash_entry *entry;
-
-       FLEX_ALLOC_STR(entry, name, name);
-       hashmap_entry_init(&entry->ent, strhash(name));
-       entry->refs = refs;
-       return entry;
-}
-
-/* A hashmap of ref_stores, stored by submodule name: */
-static struct hashmap submodule_ref_stores;
-
-/* A hashmap of ref_stores, stored by worktree id: */
-static struct hashmap worktree_ref_stores;
-
 /*
  * Look up a ref store by name. If that ref_store hasn't been
  * registered yet, return NULL.
  */
-static struct ref_store *lookup_ref_store_map(struct hashmap *map,
+static struct ref_store *lookup_ref_store_map(struct strmap *map,
                                              const char *name)
 {
-       struct ref_store_hash_entry *entry;
-       unsigned int hash;
+       struct strmap_entry *entry;
 
-       if (!map->tablesize)
+       if (!map->map.tablesize)
                /* It's initialized on demand in register_ref_store(). */
                return NULL;
 
-       hash = strhash(name);
-       entry = hashmap_get_entry_from_hash(map, hash, name,
-                                       struct ref_store_hash_entry, ent);
-       return entry ? entry->refs : NULL;
+       entry = strmap_get_entry(map, name);
+       return entry ? entry->value : NULL;
 }
 
 /*
@@ -2126,6 +1907,12 @@ static struct ref_store *ref_store_init(struct repository *repo,
        return refs;
 }
 
+void ref_store_release(struct ref_store *ref_store)
+{
+       ref_store->be->release(ref_store);
+       free(ref_store->gitdir);
+}
+
 struct ref_store *get_main_ref_store(struct repository *r)
 {
        if (r->refs_private)
@@ -2143,22 +1930,19 @@ struct ref_store *get_main_ref_store(struct repository *r)
  * Associate a ref store with a name. It is a fatal error to call this
  * function twice for the same name.
  */
-static void register_ref_store_map(struct hashmap *map,
+static void register_ref_store_map(struct strmap *map,
                                   const char *type,
                                   struct ref_store *refs,
                                   const char *name)
 {
-       struct ref_store_hash_entry *entry;
-
-       if (!map->tablesize)
-               hashmap_init(map, ref_store_hash_cmp, NULL, 0);
-
-       entry = alloc_ref_store_hash_entry(name, refs);
-       if (hashmap_put(map, &entry->ent))
+       if (!map->map.tablesize)
+               strmap_init(map);
+       if (strmap_put(map, name, refs))
                BUG("%s ref_store '%s' initialized twice", type, name);
 }
 
-struct ref_store *get_submodule_ref_store(const char *submodule)
+struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
+                                              const char *submodule)
 {
        struct strbuf submodule_sb = STRBUF_INIT;
        struct ref_store *refs;
@@ -2179,7 +1963,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
                /* We need to strip off one or more trailing slashes */
                submodule = to_free = xmemdupz(submodule, len);
 
-       refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
+       refs = lookup_ref_store_map(&repo->submodule_ref_stores, submodule);
        if (refs)
                goto done;
 
@@ -2191,20 +1975,15 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
                goto done;
 
        subrepo = xmalloc(sizeof(*subrepo));
-       /*
-        * NEEDSWORK: Make get_submodule_ref_store() work with arbitrary
-        * superprojects other than the_repository. This probably should be
-        * done by making it take a struct repository * parameter instead of a
-        * submodule path.
-        */
-       if (repo_submodule_init(subrepo, the_repository, submodule,
+
+       if (repo_submodule_init(subrepo, repo, submodule,
                                null_oid())) {
                free(subrepo);
                goto done;
        }
        refs = ref_store_init(subrepo, submodule_sb.buf,
                              REF_STORE_READ | REF_STORE_ODB);
-       register_ref_store_map(&submodule_ref_stores, "submodule",
+       register_ref_store_map(&repo->submodule_ref_stores, "submodule",
                               refs, submodule);
 
 done:
@@ -2220,25 +1999,29 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
        const char *id;
 
        if (wt->is_current)
-               return get_main_ref_store(the_repository);
+               return get_main_ref_store(wt->repo);
 
        id = wt->id ? wt->id : "/";
-       refs = lookup_ref_store_map(&worktree_ref_stores, id);
+       refs = lookup_ref_store_map(&wt->repo->worktree_ref_stores, id);
        if (refs)
                return refs;
 
-       if (wt->id)
-               refs = ref_store_init(the_repository,
-                                     git_common_path("worktrees/%s", wt->id),
+       if (wt->id) {
+               struct strbuf common_path = STRBUF_INIT;
+               strbuf_git_common_path(&common_path, wt->repo,
+                                     "worktrees/%s", wt->id);
+               refs = ref_store_init(wt->repo, common_path.buf,
                                      REF_STORE_ALL_CAPS);
-       else
-               refs = ref_store_init(the_repository,
-                                     get_git_common_dir(),
+               strbuf_release(&common_path);
+       } else {
+               refs = ref_store_init(wt->repo, wt->repo->commondir,
                                      REF_STORE_ALL_CAPS);
+       }
 
        if (refs)
-               register_ref_store_map(&worktree_ref_stores, "worktree",
-                                      refs, id);
+               register_ref_store_map(&wt->repo->worktree_ref_stores,
+                                      "worktree", refs, id);
+
        return refs;
 }
 
@@ -2256,36 +2039,37 @@ int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts)
        return refs->be->pack_refs(refs, opts);
 }
 
-int peel_iterated_oid(const struct object_id *base, struct object_id *peeled)
+int peel_iterated_oid(struct repository *r, const struct object_id *base, struct object_id *peeled)
 {
        if (current_ref_iter &&
            (current_ref_iter->oid == base ||
             oideq(current_ref_iter->oid, base)))
                return ref_iterator_peel(current_ref_iter, peeled);
 
-       return peel_object(base, peeled) ? -1 : 0;
+       return peel_object(r, base, peeled) ? -1 : 0;
 }
 
-int refs_create_symref(struct ref_store *refs,
-                      const char *ref_target,
-                      const char *refs_heads_master,
-                      const char *logmsg)
+int refs_update_symref(struct ref_store *refs, const char *ref,
+                      const char *target, const char *logmsg)
 {
-       char *msg;
-       int retval;
+       struct ref_transaction *transaction;
+       struct strbuf err = STRBUF_INIT;
+       int ret = 0;
 
-       msg = normalize_reflog_message(logmsg);
-       retval = refs->be->create_symref(refs, ref_target, refs_heads_master,
-                                        msg);
-       free(msg);
-       return retval;
-}
+       transaction = ref_store_transaction_begin(refs, &err);
+       if (!transaction ||
+           ref_transaction_update(transaction, ref, NULL, NULL,
+                                  target, NULL, REF_NO_DEREF,
+                                  logmsg, &err) ||
+           ref_transaction_commit(transaction, &err)) {
+               ret = error("%s", err.buf);
+       }
 
-int create_symref(const char *ref_target, const char *refs_heads_master,
-                 const char *logmsg)
-{
-       return refs_create_symref(get_main_ref_store(the_repository), ref_target,
-                                 refs_heads_master, logmsg);
+       strbuf_release(&err);
+       if (transaction)
+               ref_transaction_free(transaction);
+
+       return ret;
 }
 
 int ref_update_reject_duplicates(struct string_list *refnames,
@@ -2338,10 +2122,22 @@ static int run_transaction_hook(struct ref_transaction *transaction,
                struct ref_update *update = transaction->updates[i];
 
                strbuf_reset(&buf);
-               strbuf_addf(&buf, "%s %s %s\n",
-                           oid_to_hex(&update->old_oid),
-                           oid_to_hex(&update->new_oid),
-                           update->refname);
+
+               if (!(update->flags & REF_HAVE_OLD))
+                       strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
+               else if (update->old_target)
+                       strbuf_addf(&buf, "ref:%s ", update->old_target);
+               else
+                       strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid));
+
+               if (!(update->flags & REF_HAVE_NEW))
+                       strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
+               else if (update->new_target)
+                       strbuf_addf(&buf, "ref:%s ", update->new_target);
+               else
+                       strbuf_addf(&buf, "%s ", oid_to_hex(&update->new_oid));
+
+               strbuf_addf(&buf, "%s\n", update->refname);
 
                if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
                        if (errno != EPIPE) {
@@ -2560,8 +2356,7 @@ struct do_for_each_reflog_help {
        void *cb_data;
 };
 
-static int do_for_each_reflog_helper(struct repository *r UNUSED,
-                                    const char *refname,
+static int do_for_each_reflog_helper(const char *refname,
                                     const struct object_id *oid UNUSED,
                                     int flags,
                                     void *cb_data)
@@ -2577,13 +2372,7 @@ int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_dat
 
        iter = refs->be->reflog_iterator_begin(refs);
 
-       return do_for_each_repo_ref_iterator(the_repository, iter,
-                                            do_for_each_reflog_helper, &hp);
-}
-
-int for_each_reflog(each_reflog_fn fn, void *cb_data)
-{
-       return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
+       return do_for_each_ref_iterator(iter, do_for_each_reflog_helper, &hp);
 }
 
 int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
@@ -2595,58 +2384,28 @@ int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
                                                     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(the_repository),
-                                               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)
-{
-       return refs_for_each_reflog_ent(get_main_ref_store(the_repository), 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)
-{
-       return refs_reflog_exists(get_main_ref_store(the_repository), refname);
-}
-
 int refs_create_reflog(struct ref_store *refs, const char *refname,
                       struct strbuf *err)
 {
        return refs->be->create_reflog(refs, refname, err);
 }
 
-int safe_create_reflog(const char *refname, struct strbuf *err)
-{
-       return refs_create_reflog(get_main_ref_store(the_repository), refname,
-                                 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)
-{
-       return refs_delete_reflog(get_main_ref_store(the_repository), refname);
-}
-
 int refs_reflog_expire(struct ref_store *refs,
                       const char *refname,
                       unsigned int flags,
@@ -2660,19 +2419,6 @@ int refs_reflog_expire(struct ref_store *refs,
                                       cleanup_fn, policy_cb_data);
 }
 
-int reflog_expire(const char *refname,
-                 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_reflog_expire(get_main_ref_store(the_repository),
-                                 refname, flags,
-                                 prepare_fn, should_prune_fn,
-                                 cleanup_fn, policy_cb_data);
-}
-
 int initial_ref_transaction_commit(struct ref_transaction *transaction,
                                   struct strbuf *err)
 {
@@ -2751,12 +2497,6 @@ out:
        return ret;
 }
 
-int delete_refs(const char *msg, struct string_list *refnames,
-               unsigned int flags)
-{
-       return refs_delete_refs(get_main_ref_store(the_repository), msg, refnames, flags);
-}
-
 int refs_rename_ref(struct ref_store *refs, const char *oldref,
                    const char *newref, const char *logmsg)
 {
@@ -2769,11 +2509,6 @@ int refs_rename_ref(struct ref_store *refs, const char *oldref,
        return retval;
 }
 
-int rename_ref(const char *oldref, const char *newref, const char *logmsg)
-{
-       return refs_rename_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
-}
-
 int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
                    const char *newref, const char *logmsg)
 {
@@ -2786,7 +2521,37 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
        return retval;
 }
 
-int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
+const char *ref_update_original_update_refname(struct ref_update *update)
+{
+       while (update->parent_update)
+               update = update->parent_update;
+
+       return update->refname;
+}
+
+int ref_update_has_null_new_value(struct ref_update *update)
+{
+       return !update->new_target && is_null_oid(&update->new_oid);
+}
+
+int ref_update_check_old_target(const char *referent, struct ref_update *update,
+                               struct strbuf *err)
 {
-       return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
+       if (!update->old_target)
+               BUG("called without old_target set");
+
+       if (!strcmp(referent, update->old_target))
+               return 0;
+
+       if (!strcmp(referent, ""))
+               strbuf_addf(err, "verifying symref target: '%s': "
+                           "reference is missing but expected %s",
+                           ref_update_original_update_refname(update),
+                           update->old_target);
+       else
+               strbuf_addf(err, "verifying symref target: '%s': "
+                           "is at %s but expected %s",
+                           ref_update_original_update_refname(update),
+                           referent, update->old_target);
+       return -1;
 }