mailinfo: release strbuf on error return in handle_boundary()
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 8af9641aa17e68bfcf9d5e042cb1116995a99042..b0106b8162c7b8e3770e04c212abb05f1afbd0c3 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -3,6 +3,7 @@
  */
 
 #include "cache.h"
+#include "config.h"
 #include "hashmap.h"
 #include "lockfile.h"
 #include "iterator.h"
@@ -173,6 +174,24 @@ int refname_is_safe(const char *refname)
        return 1;
 }
 
+/*
+ * Return true if refname, which has the specified oid and flags, can
+ * be resolved to an object in the database. If the referred-to object
+ * does not exist, emit a warning and return false.
+ */
+int ref_resolves_to_object(const char *refname,
+                          const struct object_id *oid,
+                          unsigned int flags)
+{
+       if (flags & REF_ISBROKEN)
+               return 0;
+       if (!has_sha1_file(oid->hash)) {
+               error("%s does not point to a valid object!", refname);
+               return 0;
+       }
+       return 1;
+}
+
 char *refs_resolve_refdup(struct ref_store *refs,
                          const char *refname, int resolve_flags,
                          unsigned char *sha1, int *flags)
@@ -229,7 +248,7 @@ static int filter_refs(const char *refname, const struct object_id *oid,
 {
        struct ref_filter *filter = (struct ref_filter *)data;
 
-       if (wildmatch(filter->pattern, refname, 0, NULL))
+       if (wildmatch(filter->pattern, refname, 0))
                return 0;
        return filter->fn(refname, oid, flags, filter->cb_data);
 }
@@ -560,6 +579,21 @@ enum ref_type ref_type(const char *refname)
        return REF_TYPE_NORMAL;
 }
 
+long get_files_ref_lock_timeout_ms(void)
+{
+       static int configured = 0;
+
+       /* The default timeout is 100 ms: */
+       static int timeout_ms = 100;
+
+       if (!configured) {
+               git_config_get_int("core.filesreflocktimeout", &timeout_ms);
+               configured = 1;
+       }
+
+       return timeout_ms;
+}
+
 static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
                           const unsigned char *old_sha1, struct strbuf *err)
 {
@@ -572,7 +606,9 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
        strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
 
        filename = git_path("%s", pseudoref);
-       fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+       fd = hold_lock_file_for_update_timeout(&lock, filename,
+                                              LOCK_DIE_ON_ERROR,
+                                              get_files_ref_lock_timeout_ms());
        if (fd < 0) {
                strbuf_addf(err, "could not open '%s' for writing: %s",
                            filename, strerror(errno));
@@ -615,8 +651,9 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1
                int fd;
                unsigned char actual_old_sha1[20];
 
-               fd = hold_lock_file_for_update(&lock, filename,
-                                              LOCK_DIE_ON_ERROR);
+               fd = hold_lock_file_for_update_timeout(
+                               &lock, filename, LOCK_DIE_ON_ERROR,
+                               get_files_ref_lock_timeout_ms());
                if (fd < 0)
                        die_errno(_("Could not open '%s' for writing"), filename);
                if (read_ref(pseudoref, actual_old_sha1))
@@ -817,7 +854,7 @@ int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, in
        for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
 
        if (!cb.reccnt) {
-               if (flags & GET_SHA1_QUIETLY)
+               if (flags & GET_OID_QUIETLY)
                        exit(128);
                else
                        die("Log for %s is empty.", refname);
@@ -848,11 +885,24 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err)
 
 void ref_transaction_free(struct ref_transaction *transaction)
 {
-       int i;
+       size_t i;
 
        if (!transaction)
                return;
 
+       switch (transaction->state) {
+       case REF_TRANSACTION_OPEN:
+       case REF_TRANSACTION_CLOSED:
+               /* OK */
+               break;
+       case REF_TRANSACTION_PREPARED:
+               die("BUG: free called on a prepared reference transaction");
+               break;
+       default:
+               die("BUG: unexpected reference transaction state");
+               break;
+       }
+
        for (i = 0; i < transaction->nr; i++) {
                free(transaction->updates[i]->msg);
                free(transaction->updates[i]);
@@ -1146,7 +1196,7 @@ int ref_is_hidden(const char *refname, const char *refname_full)
                const char *match = hide_refs->items[i].string;
                const char *subject;
                int neg = 0;
-               int len;
+               const char *p;
 
                if (*match == '!') {
                        neg = 1;
@@ -1161,10 +1211,9 @@ int ref_is_hidden(const char *refname, const char *refname_full)
                }
 
                /* refname can be NULL when namespaces are used. */
-               if (!subject || !starts_with(subject, match))
-                       continue;
-               len = strlen(match);
-               if (!subject[len] || subject[len] == '/')
+               if (subject &&
+                   skip_prefix(subject, match, &p) &&
+                   (!*p || *p == '/'))
                        return !neg;
        }
        return 0;
@@ -1246,8 +1295,19 @@ struct ref_iterator *refs_ref_iterator_begin(
 {
        struct ref_iterator *iter;
 
+       if (ref_paranoia < 0)
+               ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
+       if (ref_paranoia)
+               flags |= DO_FOR_EACH_INCLUDE_BROKEN;
+
        iter = refs->be->iterator_begin(refs, prefix, flags);
-       iter = prefix_ref_iterator_begin(iter, prefix, trim);
+
+       /*
+        * `iterator_begin()` already takes care of prefix, but we
+        * might need to do some trimming:
+        */
+       if (trim)
+               iter = prefix_ref_iterator_begin(iter, "", trim);
 
        return iter;
 }
@@ -1317,6 +1377,18 @@ int for_each_ref_in_submodule(const char *submodule, const char *prefix,
                                    prefix, fn, cb_data);
 }
 
+int for_each_fullref_in_submodule(const char *submodule, const char *prefix,
+                                 each_ref_fn fn, void *cb_data,
+                                 unsigned int broken)
+{
+       unsigned int flag = 0;
+
+       if (broken)
+               flag = DO_FOR_EACH_INCLUDE_BROKEN;
+       return do_for_each_ref(get_submodule_ref_store(submodule),
+                              prefix, fn, 0, flag, cb_data);
+}
+
 int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 {
        return do_for_each_ref(get_main_ref_store(),
@@ -1488,7 +1560,8 @@ struct ref_store_hash_entry
        char name[FLEX_ARRAY];
 };
 
-static int ref_store_hash_cmp(const void *entry, const void *entry_or_key,
+static int ref_store_hash_cmp(const void *unused_cmp_data,
+                             const void *entry, const void *entry_or_key,
                              const void *keydata)
 {
        const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;
@@ -1571,7 +1644,7 @@ static void register_ref_store_map(struct hashmap *map,
                                   const char *name)
 {
        if (!map->tablesize)
-               hashmap_init(map, ref_store_hash_cmp, 0);
+               hashmap_init(map, ref_store_hash_cmp, NULL, 0);
 
        if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))
                die("BUG: %s ref_store '%s' initialized twice", type, name);
@@ -1683,18 +1756,108 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
                                  refs_heads_master, logmsg);
 }
 
-int ref_transaction_commit(struct ref_transaction *transaction,
-                          struct strbuf *err)
+int ref_update_reject_duplicates(struct string_list *refnames,
+                                struct strbuf *err)
+{
+       size_t i, n = refnames->nr;
+
+       assert(err);
+
+       for (i = 1; i < n; i++) {
+               int cmp = strcmp(refnames->items[i - 1].string,
+                                refnames->items[i].string);
+
+               if (!cmp) {
+                       strbuf_addf(err,
+                                   "multiple updates for ref '%s' not allowed.",
+                                   refnames->items[i].string);
+                       return 1;
+               } else if (cmp > 0) {
+                       die("BUG: ref_update_reject_duplicates() received unsorted list");
+               }
+       }
+       return 0;
+}
+
+int ref_transaction_prepare(struct ref_transaction *transaction,
+                           struct strbuf *err)
 {
        struct ref_store *refs = transaction->ref_store;
 
+       switch (transaction->state) {
+       case REF_TRANSACTION_OPEN:
+               /* Good. */
+               break;
+       case REF_TRANSACTION_PREPARED:
+               die("BUG: prepare called twice on reference transaction");
+               break;
+       case REF_TRANSACTION_CLOSED:
+               die("BUG: prepare called on a closed reference transaction");
+               break;
+       default:
+               die("BUG: unexpected reference transaction state");
+               break;
+       }
+
        if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
                strbuf_addstr(err,
                              _("ref updates forbidden inside quarantine environment"));
                return -1;
        }
 
-       return refs->be->transaction_commit(refs, transaction, err);
+       return refs->be->transaction_prepare(refs, transaction, err);
+}
+
+int ref_transaction_abort(struct ref_transaction *transaction,
+                         struct strbuf *err)
+{
+       struct ref_store *refs = transaction->ref_store;
+       int ret = 0;
+
+       switch (transaction->state) {
+       case REF_TRANSACTION_OPEN:
+               /* No need to abort explicitly. */
+               break;
+       case REF_TRANSACTION_PREPARED:
+               ret = refs->be->transaction_abort(refs, transaction, err);
+               break;
+       case REF_TRANSACTION_CLOSED:
+               die("BUG: abort called on a closed reference transaction");
+               break;
+       default:
+               die("BUG: unexpected reference transaction state");
+               break;
+       }
+
+       ref_transaction_free(transaction);
+       return ret;
+}
+
+int ref_transaction_commit(struct ref_transaction *transaction,
+                          struct strbuf *err)
+{
+       struct ref_store *refs = transaction->ref_store;
+       int ret;
+
+       switch (transaction->state) {
+       case REF_TRANSACTION_OPEN:
+               /* Need to prepare first. */
+               ret = ref_transaction_prepare(transaction, err);
+               if (ret)
+                       return ret;
+               break;
+       case REF_TRANSACTION_PREPARED:
+               /* Fall through to finish. */
+               break;
+       case REF_TRANSACTION_CLOSED:
+               die("BUG: commit called on a closed reference transaction");
+               break;
+       default:
+               die("BUG: unexpected reference transaction state");
+               break;
+       }
+
+       return refs->be->transaction_finish(refs, transaction, err);
 }
 
 int refs_verify_refname_available(struct ref_store *refs,
@@ -1896,15 +2059,16 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
        return refs->be->initial_transaction_commit(refs, transaction, err);
 }
 
-int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,
-                    unsigned int flags)
+int refs_delete_refs(struct ref_store *refs, const char *msg,
+                    struct string_list *refnames, unsigned int flags)
 {
-       return refs->be->delete_refs(refs, refnames, flags);
+       return refs->be->delete_refs(refs, msg, refnames, flags);
 }
 
-int delete_refs(struct string_list *refnames, unsigned int flags)
+int delete_refs(const char *msg, struct string_list *refnames,
+               unsigned int flags)
 {
-       return refs_delete_refs(get_main_ref_store(), refnames, flags);
+       return refs_delete_refs(get_main_ref_store(), msg, refnames, flags);
 }
 
 int refs_rename_ref(struct ref_store *refs, const char *oldref,