sub-process: move sub-process functions into separate files
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 49a7e703d53b3a3ffe7d9f6a006fe1819c9ed5e9..0272e332cc9951497dddcd8b5c57a30e80552b4f 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -3,12 +3,32 @@
  */
 
 #include "cache.h"
+#include "hashmap.h"
 #include "lockfile.h"
 #include "refs.h"
 #include "refs/refs-internal.h"
 #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
@@ -385,7 +405,7 @@ int refname_match(const char *abbrev_name, const char *full_name)
 static char *substitute_branch_name(const char **string, int *len)
 {
        struct strbuf buf = STRBUF_INIT;
-       int ret = interpret_branch_name(*string, *len, &buf);
+       int ret = interpret_branch_name(*string, *len, &buf, 0);
 
        if (ret == *len) {
                size_t size;
@@ -400,6 +420,13 @@ static char *substitute_branch_name(const char **string, int *len)
 int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
 {
        char *last_branch = substitute_branch_name(&str, &len);
+       int   refs_found  = expand_ref(str, len, sha1, ref);
+       free(last_branch);
+       return refs_found;
+}
+
+int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)
+{
        const char **p, *r;
        int refs_found = 0;
 
@@ -425,7 +452,6 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
                        warning("ignoring broken ref %s.", fullref);
                }
        }
-       free(last_branch);
        return refs_found;
 }
 
@@ -566,8 +592,8 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1
        return 0;
 }
 
-int delete_ref(const char *refname, const unsigned char *old_sha1,
-              unsigned int flags)
+int delete_ref(const char *msg, const char *refname,
+              const unsigned char *old_sha1, unsigned int flags)
 {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
@@ -578,7 +604,7 @@ int delete_ref(const char *refname, const unsigned char *old_sha1,
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_delete(transaction, refname, old_sha1,
-                                  flags, NULL, &err) ||
+                                  flags, msg, &err) ||
            ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
                ref_transaction_free(transaction);
@@ -613,12 +639,17 @@ int copy_reflog_msg(char *buf, const char *msg)
 
 int should_autocreate_reflog(const char *refname)
 {
-       if (!log_all_ref_updates)
+       switch (log_all_ref_updates) {
+       case LOG_REFS_ALWAYS:
+               return 1;
+       case LOG_REFS_NORMAL:
+               return starts_with(refname, "refs/heads/") ||
+                       starts_with(refname, "refs/remotes/") ||
+                       starts_with(refname, "refs/notes/") ||
+                       !strcmp(refname, "HEAD");
+       default:
                return 0;
-       return starts_with(refname, "refs/heads/") ||
-               starts_with(refname, "refs/remotes/") ||
-               starts_with(refname, "refs/notes/") ||
-               !strcmp(refname, "HEAD");
+       }
 }
 
 int is_branch(const char *refname)
@@ -644,7 +675,7 @@ struct read_ref_at_cb {
        int *cutoff_cnt;
 };
 
-static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
+static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
@@ -668,30 +699,30 @@ static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
                 * hold the values for the previous record.
                 */
                if (!is_null_sha1(cb->osha1)) {
-                       hashcpy(cb->sha1, nsha1);
-                       if (hashcmp(cb->osha1, nsha1))
+                       hashcpy(cb->sha1, noid->hash);
+                       if (hashcmp(cb->osha1, noid->hash))
                                warning("Log for ref %s has gap after %s.",
                                        cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
                }
                else if (cb->date == cb->at_time)
-                       hashcpy(cb->sha1, nsha1);
-               else if (hashcmp(nsha1, cb->sha1))
+                       hashcpy(cb->sha1, noid->hash);
+               else if (hashcmp(noid->hash, cb->sha1))
                        warning("Log for ref %s unexpectedly ended on %s.",
                                cb->refname, show_date(cb->date, cb->tz,
                                                       DATE_MODE(RFC2822)));
-               hashcpy(cb->osha1, osha1);
-               hashcpy(cb->nsha1, nsha1);
+               hashcpy(cb->osha1, ooid->hash);
+               hashcpy(cb->nsha1, noid->hash);
                cb->found_it = 1;
                return 1;
        }
-       hashcpy(cb->osha1, osha1);
-       hashcpy(cb->nsha1, nsha1);
+       hashcpy(cb->osha1, ooid->hash);
+       hashcpy(cb->nsha1, noid->hash);
        if (cb->cnt > 0)
                cb->cnt--;
        return 0;
 }
 
-static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
+static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
                                  const char *email, unsigned long timestamp,
                                  int tz, const char *message, void *cb_data)
 {
@@ -705,9 +736,9 @@ static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
                *cb->cutoff_tz = tz;
        if (cb->cutoff_cnt)
                *cb->cutoff_cnt = cb->reccnt;
-       hashcpy(cb->sha1, osha1);
+       hashcpy(cb->sha1, ooid->hash);
        if (is_null_sha1(cb->sha1))
-               hashcpy(cb->sha1, nsha1);
+               hashcpy(cb->sha1, noid->hash);
        /* We just want the first entry */
        return 1;
 }
@@ -857,6 +888,14 @@ int ref_transaction_verify(struct ref_transaction *transaction,
                                      flags, NULL, err);
 }
 
+int update_ref_oid(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 update_ref(msg, refname, new_oid ? new_oid->hash : NULL,
+               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)
@@ -996,10 +1035,10 @@ static struct string_list *hide_refs;
 
 int parse_hide_refs_config(const char *var, const char *value, const char *section)
 {
+       const char *key;
        if (!strcmp("transfer.hiderefs", var) ||
-           /* NEEDSWORK: use parse_config_key() once both are merged */
-           (starts_with(var, section) && var[strlen(section)] == '.' &&
-            !strcmp(var + strlen(section), ".hiderefs"))) {
+           (!parse_config_key(var, section, NULL, NULL, &key) &&
+            !strcmp(key, "hiderefs"))) {
                char *ref;
                int len;
 
@@ -1080,20 +1119,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)
@@ -1131,9 +1170,13 @@ int head_ref(each_ref_fn fn, void *cb_data)
 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;
 
-       iter = files_ref_iterator_begin(submodule, prefix, flags);
+       if (!refs)
+               return 0;
+
+       iter = refs->be->iterator_begin(refs, prefix, flags);
        iter = prefix_ref_iterator_begin(iter, prefix, trim);
 
        return do_for_each_ref_iterator(iter, fn, cb_data);
@@ -1192,8 +1235,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)
+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;
@@ -1225,7 +1270,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;
@@ -1264,3 +1310,306 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
        errno = ELOOP;
        return NULL;
 }
+
+/* backend functions */
+int refs_init_db(struct strbuf *err)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->init_db(refs, err);
+}
+
+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;
+}
+
+struct submodule_hash_entry
+{
+       struct hashmap_entry ent; /* must be the first member! */
+
+       struct ref_store *refs;
+
+       /* NUL-terminated name of submodule: */
+       char submodule[FLEX_ARRAY];
+};
+
+static int submodule_hash_cmp(const void *entry, const void *entry_or_key,
+                             const void *keydata)
+{
+       const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key;
+       const char *submodule = keydata ? keydata : e2->submodule;
+
+       return strcmp(e1->submodule, submodule);
+}
+
+static struct submodule_hash_entry *alloc_submodule_hash_entry(
+               const char *submodule, struct ref_store *refs)
+{
+       struct submodule_hash_entry *entry;
+
+       FLEX_ALLOC_STR(entry, submodule, submodule);
+       hashmap_entry_init(entry, strhash(submodule));
+       entry->refs = refs;
+       return entry;
+}
+
+/* A pointer to the ref_store for the main repository: */
+static struct ref_store *main_ref_store;
+
+/* A hashmap of ref_stores, stored by submodule name: */
+static struct hashmap submodule_ref_stores;
+
+/*
+ * Return the ref_store instance for the specified submodule (or the
+ * main repository if submodule is NULL). If that ref_store hasn't
+ * been initialized yet, return NULL.
+ */
+static struct ref_store *lookup_ref_store(const char *submodule)
+{
+       struct submodule_hash_entry *entry;
+
+       if (!submodule)
+               return main_ref_store;
+
+       if (!submodule_ref_stores.tablesize)
+               /* It's initialized on demand in register_ref_store(). */
+               return NULL;
+
+       entry = hashmap_get_from_hash(&submodule_ref_stores,
+                                     strhash(submodule), submodule);
+       return entry ? entry->refs : NULL;
+}
+
+/*
+ * Register the specified ref_store to be the one that should be used
+ * for submodule (or the main repository if submodule is NULL). It is
+ * a fatal error to call this function twice for the same submodule.
+ */
+static void register_ref_store(struct ref_store *refs, const char *submodule)
+{
+       if (!submodule) {
+               if (main_ref_store)
+                       die("BUG: main_ref_store initialized twice");
+
+               main_ref_store = refs;
+       } else {
+               if (!submodule_ref_stores.tablesize)
+                       hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);
+
+               if (hashmap_put(&submodule_ref_stores,
+                               alloc_submodule_hash_entry(submodule, refs)))
+                       die("BUG: ref_store for submodule '%s' initialized twice",
+                           submodule);
+       }
+}
+
+/*
+ * Create, record, and return a ref_store instance for the specified
+ * submodule (or the main repository if submodule is NULL).
+ */
+static 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);
+       struct ref_store *refs;
+
+       if (!be)
+               die("BUG: reference backend %s is unknown", be_name);
+
+       refs = be->init(submodule);
+       register_ref_store(refs, submodule);
+       return refs;
+}
+
+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 base_ref_store_init(struct ref_store *refs,
+                        const struct ref_storage_be *be)
+{
+       refs->be = be;
+}
+
+/* backend functions */
+int pack_refs(unsigned int flags)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->pack_refs(refs, flags);
+}
+
+int peel_ref(const char *refname, unsigned char *sha1)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->peel_ref(refs, refname, sha1);
+}
+
+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);
+}
+
+int for_each_reflog(each_ref_fn fn, void *cb_data)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+       struct ref_iterator *iter;
+
+       iter = refs->be->reflog_iterator_begin(refs);
+
+       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)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->for_each_reflog_ent_reverse(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_ref_store(NULL);
+
+       return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
+}
+
+int reflog_exists(const char *refname)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->reflog_exists(refs, refname);
+}
+
+int safe_create_reflog(const char *refname, int force_create,
+                      struct strbuf *err)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->create_reflog(refs, refname, force_create, err);
+}
+
+int delete_reflog(const char *refname)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->delete_reflog(refs, refname);
+}
+
+int reflog_expire(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)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->reflog_expire(refs, 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_ref_store(NULL);
+
+       return refs->be->initial_transaction_commit(refs, transaction, err);
+}
+
+int delete_refs(struct string_list *refnames, unsigned int flags)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->delete_refs(refs, refnames, flags);
+}
+
+int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+       struct ref_store *refs = get_ref_store(NULL);
+
+       return refs->be->rename_ref(refs, oldref, newref, logmsg);
+}