t1007: add hash-object --literally tests
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 84b9070b8dca5eb94fbe9b7d7c463a0ea2af92eb..2ce5d690907d33bda557f66150ef1317f39b4f93 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -7,29 +7,28 @@
 
 /*
  * How to handle various characters in refnames:
- * This table is used by both the SIMD and non-SIMD code.  It has
- * some cases that are only useful for the SIMD; these are handled
- * equivalently to the listed disposition in the non-SIMD code.
  * 0: An acceptable character for refs
- * 1: @, look for a following { to reject @{ in refs (SIMD or = 0)
- * 2: \0: End-of-component and string
- * 3: /: End-of-component (SIMD or = 2)
- * 4: ., look for a preceding . to reject .. in refs
- * 5: {, look for a preceding @ to reject @{ in refs
- * 6: *, usually a bad character except, once as a wildcard (SIMD or = 7)
- * 7: A bad character except * (see check_refname_component below)
+ * 1: End-of-component
+ * 2: ., look for a preceding . to reject .. in refs
+ * 3: {, look for a preceding @ to reject @{ in refs
+ * 4: A bad character: ASCII control characters, "~", "^", ":" or SP
  */
 static unsigned char refname_disposition[256] = {
-       2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-       7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 4, 3,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 7,
-       1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 7, 0,
+       1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+       4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 7, 7
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4
 };
 
+/*
+ * Used as a flag to ref_transaction_delete when a loose ref is being
+ * pruned.
+ */
+#define REF_ISPRUNING  0x0100
 /*
  * Try to read one refname component from the front of refname.
  * Return the length of the component found, or -1 if the component is
@@ -39,9 +38,8 @@ static unsigned char refname_disposition[256] = {
  * - any path component of it begins with ".", or
  * - it has double dots "..", or
  * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
- * - it has pattern-matching notation "*", "?", "[", anywhere, or
- * - it ends with a "/", or
- * - it ends with ".lock", or
+ * - it ends with a "/".
+ * - it ends with ".lock"
  * - it contains a "\" (backslash)
  */
 static int check_refname_component(const char *refname, int flags)
@@ -53,19 +51,17 @@ static int check_refname_component(const char *refname, int flags)
                int ch = *cp & 255;
                unsigned char disp = refname_disposition[ch];
                switch (disp) {
-               case 2: /* fall-through */
-               case 3:
+               case 1:
                        goto out;
-               case 4:
+               case 2:
                        if (last == '.')
                                return -1; /* Refname contains "..". */
                        break;
-               case 5:
+               case 3:
                        if (last == '@')
                                return -1; /* Refname contains "@{". */
                        break;
-               case 6: /* fall-through */
-               case 7:
+               case 4:
                        return -1;
                }
                last = ch;
@@ -88,7 +84,7 @@ static int check_refname_component(const char *refname, int flags)
        return cp - refname;
 }
 
-static int check_refname_format_bytewise(const char *refname, int flags)
+int check_refname_format(const char *refname, int flags)
 {
        int component_len, component_count = 0;
 
@@ -124,196 +120,6 @@ static int check_refname_format_bytewise(const char *refname, int flags)
        return 0;
 }
 
-#if defined(__GNUC__) && defined(__x86_64__)
-#define SSE_VECTOR_BYTES 16
-
-/* Vectorized version of check_refname_format. */
-int check_refname_format(const char *refname, int flags)
-{
-       const char *cp = refname;
-
-       const __m128i dot = _mm_set1_epi8('.');
-       const __m128i at = _mm_set1_epi8('@');
-       const __m128i curly = _mm_set1_epi8('{');
-       const __m128i slash = _mm_set1_epi8('/');
-       const __m128i zero = _mm_set1_epi8('\000');
-       const __m128i el = _mm_set1_epi8('l');
-
-       /* below '*', all characters are forbidden or rare */
-       const __m128i star_ub = _mm_set1_epi8('*' + 1);
-
-       const __m128i colon = _mm_set1_epi8(':');
-       const __m128i question = _mm_set1_epi8('?');
-
-       /* '['..'^' contains 4 characters: 3 forbidden and 1 rare */
-       const __m128i bracket_lb = _mm_set1_epi8('[' - 1);
-       const __m128i caret_ub = _mm_set1_epi8('^' + 1);
-
-       /* '~' and above are forbidden */
-       const __m128i tilde_lb = _mm_set1_epi8('~' - 1);
-
-       int component_count = 0;
-       int orig_flags = flags;
-
-       if (refname[0] == 0 || refname[0] == '/') {
-               /* entirely empty ref or initial ref component */
-               return -1;
-       }
-
-       /*
-        * Initial ref component of '.'; below we look for /. so we'll
-        * miss this.
-        */
-       if (refname[0] == '.') {
-               if (refname[1] == '/' || refname[1] == '\0')
-                       return -1;
-               if (!(flags & REFNAME_DOT_COMPONENT))
-                       return -1;
-       }
-       while(1) {
-               __m128i tmp, tmp1, result;
-               uint64_t mask;
-
-               if ((uintptr_t) cp % PAGE_SIZE > PAGE_SIZE - SSE_VECTOR_BYTES  - 1)
-                       /*
-                        * End-of-page; fall back to slow method for
-                        * this entire ref.
-                        */
-                       return check_refname_format_bytewise(refname, orig_flags);
-
-               tmp = _mm_loadu_si128((__m128i *)cp);
-               tmp1 = _mm_loadu_si128((__m128i *)(cp + 1));
-
-               /*
-                * This range (note the lt) contains some
-                * permissible-but-rare characters (including all
-                * characters >= 128), which we handle later.  It also
-                * includes \000.
-                */
-               result = _mm_cmplt_epi8(tmp, star_ub);
-
-               result = _mm_or_si128(result, _mm_cmpeq_epi8(tmp, question));
-               result = _mm_or_si128(result, _mm_cmpeq_epi8(tmp, colon));
-
-               /* This range contains the permissible ] as bycatch */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpgt_epi8(tmp, bracket_lb),
-                                             _mm_cmplt_epi8(tmp, caret_ub)));
-
-               result = _mm_or_si128(result, _mm_cmpgt_epi8(tmp, tilde_lb));
-
-               /* .. */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, dot),
-                                             _mm_cmpeq_epi8(tmp1, dot)));
-               /* @{ */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, at),
-                                             _mm_cmpeq_epi8(tmp1, curly)));
-               /* // */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, slash),
-                                             _mm_cmpeq_epi8(tmp1, slash)));
-               /* trailing / */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, slash),
-                                             _mm_cmpeq_epi8(tmp1, zero)));
-               /* .l, beginning of .lock */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, dot),
-                                             _mm_cmpeq_epi8(tmp1, el)));
-               /*
-                * Even though /. is not necessarily an error, we flag
-                * it anyway. If we find it, we'll check if it's valid
-                * and if so we'll advance just past it.
-                */
-               result = _mm_or_si128(result, _mm_and_si128(
-                                             _mm_cmpeq_epi8(tmp, slash),
-                                             _mm_cmpeq_epi8(tmp1, dot)));
-
-               mask = _mm_movemask_epi8(result);
-               if (mask) {
-                       /*
-                        * We've found either end-of-string, or some
-                        * probably-bad character or substring.
-                        */
-                       int i = __builtin_ctz(mask);
-                       switch (refname_disposition[cp[i] & 255]) {
-                       case 0: /* fall-through */
-                       case 5:
-                               /*
-                                * bycatch: a good character that's in
-                                * one of the ranges of mostly-forbidden
-                                * characters
-                                */
-                               cp += i + 1;
-                               break;
-                       case 1:
-                               if (cp[i + 1] == '{')
-                                       return -1;
-                               cp += i + 1;
-                               break;
-                       case 2:
-                               if (!(flags & REFNAME_ALLOW_ONELEVEL)
-                                   && !component_count && !strchr(refname, '/'))
-                                       /* Refname has only one component. */
-                                       return -1;
-                               return 0;
-                       case 3:
-                               component_count ++;
-                               /*
-                                * Even if leading dots are allowed, don't
-                                * allow "." as a component (".." is
-                                * prevented by case 4 below).
-                                */
-                               if (cp[i + 1] == '.') {
-                                       if (cp[i + 2] == '\0')
-                                               return -1;
-                                       if (flags & REFNAME_DOT_COMPONENT) {
-                                               /* skip to just after the /. */
-                                               cp += i + 2;
-                                               break;
-                                       }
-                                       return -1;
-                               } else if (cp[i + 1] == '/' || cp[i + 1] == '\0')
-                                       return -1;
-                               break;
-                       case 4:
-                               if (cp[i + 1] == '.' || cp[i + 1] == '\0')
-                                       return -1;
-                               /* .lock as end-of-component or end-of-string */
-                               if ((!strncmp(cp + i, ".lock", 5))
-                                   && (cp[i + 5] == '/' || cp[i + 5] == 0))
-                                       return -1;
-                               cp += 1;
-                               break;
-                       case 6:
-                               if (((cp == refname + i) || cp[i - 1] == '/')
-                                   && (cp[i + 1] == '/' || cp[i + 1] == 0))
-                                       if (flags & REFNAME_REFSPEC_PATTERN) {
-                                               flags &= ~REFNAME_REFSPEC_PATTERN;
-                                               /* restart after the * */
-                                               cp += i + 1;
-                                               continue;
-                                       }
-                               /* fall-through */
-                       case 7:
-                               return -1;
-                       }
-               } else
-                       cp += SSE_VECTOR_BYTES;
-       }
-}
-
-#else
-
-int check_refname_format (const char *refname, int flags)
-{
-       return check_refname_format_bytewise(refname, flags);
-}
-
-#endif
-
 struct ref_entry;
 
 /*
@@ -2267,7 +2073,10 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
        return logs_found;
 }
 
-/* This function should make sure errno is meaningful on error */
+/*
+ * Locks a "refs/" ref returning the lock on success and NULL on failure.
+ * On failure errno is set to something meaningful.
+ */
 static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                                            const unsigned char *old_sha1,
                                            int flags, int *type_p)
@@ -2368,15 +2177,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        return NULL;
 }
 
-struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1)
-{
-       char refpath[PATH_MAX];
-       if (check_refname_format(refname, 0))
-               return NULL;
-       strcpy(refpath, mkpath("refs/%s", refname));
-       return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);
-}
-
 struct ref_lock *lock_any_ref_for_update(const char *refname,
                                         const unsigned char *old_sha1,
                                         int flags, int *type_p)
@@ -2586,13 +2386,25 @@ static void try_remove_empty_parents(char *name)
 /* make sure nobody touched the ref, and unlink */
 static void prune_ref(struct ref_to_prune *r)
 {
-       struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
+       struct ref_transaction *transaction;
+       struct strbuf err = STRBUF_INIT;
 
-       if (lock) {
-               unlink_or_warn(git_path("%s", r->name));
-               unlock_ref(lock);
-               try_remove_empty_parents(r->name);
+       if (check_refname_format(r->name, 0))
+               return;
+
+       transaction = ref_transaction_begin(&err);
+       if (!transaction ||
+           ref_transaction_delete(transaction, r->name, r->sha1,
+                                  REF_ISPRUNING, 1, &err) ||
+           ref_transaction_commit(transaction, NULL, &err)) {
+               ref_transaction_free(transaction);
+               error("%s", err.buf);
+               strbuf_release(&err);
+               return;
        }
+       ref_transaction_free(transaction);
+       strbuf_release(&err);
+       try_remove_empty_parents(r->name);
 }
 
 static void prune_refs(struct ref_to_prune *r)
@@ -2735,11 +2547,6 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
        return ret;
 }
 
-static int repack_without_ref(const char *refname)
-{
-       return repack_without_refs(&refname, 1, NULL);
-}
-
 static int delete_ref_loose(struct ref_lock *lock, int flag)
 {
        if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
@@ -2757,24 +2564,22 @@ static int delete_ref_loose(struct ref_lock *lock, int flag)
 
 int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
 {
-       struct ref_lock *lock;
-       int ret = 0, flag = 0;
-
-       lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag);
-       if (!lock)
+       struct ref_transaction *transaction;
+       struct strbuf err = STRBUF_INIT;
+
+       transaction = ref_transaction_begin(&err);
+       if (!transaction ||
+           ref_transaction_delete(transaction, refname, sha1, delopt,
+                                  sha1 && !is_null_sha1(sha1), &err) ||
+           ref_transaction_commit(transaction, NULL, &err)) {
+               error("%s", err.buf);
+               ref_transaction_free(transaction);
+               strbuf_release(&err);
                return 1;
-       ret |= delete_ref_loose(lock, flag);
-
-       /* removing the loose one could have resurrected an earlier
-        * packed one.  Also, if it was not loose we need to repack
-        * without it.
-        */
-       ret |= repack_without_ref(lock->ref_name);
-
-       unlink_or_warn(git_path("logs/%s", lock->ref_name));
-       clear_loose_ref_cache(&ref_cache);
-       unlock_ref(lock);
-       return ret;
+       }
+       ref_transaction_free(transaction);
+       strbuf_release(&err);
+       return 0;
 }
 
 /*
@@ -3531,43 +3336,6 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
        return retval;
 }
 
-static struct ref_lock *update_ref_lock(const char *refname,
-                                       const unsigned char *oldval,
-                                       int flags, int *type_p,
-                                       enum action_on_err onerr)
-{
-       struct ref_lock *lock;
-       lock = lock_any_ref_for_update(refname, oldval, flags, type_p);
-       if (!lock) {
-               const char *str = "Cannot lock the ref '%s'.";
-               switch (onerr) {
-               case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
-               case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
-               case UPDATE_REFS_QUIET_ON_ERR: break;
-               }
-       }
-       return lock;
-}
-
-static int update_ref_write(const char *action, const char *refname,
-                           const unsigned char *sha1, struct ref_lock *lock,
-                           struct strbuf *err, enum action_on_err onerr)
-{
-       if (write_ref_sha1(lock, sha1, action) < 0) {
-               const char *str = "Cannot update the ref '%s'.";
-               if (err)
-                       strbuf_addf(err, str, refname);
-
-               switch (onerr) {
-               case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
-               case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
-               case UPDATE_REFS_QUIET_ON_ERR: break;
-               }
-               return 1;
-       }
-       return 0;
-}
-
 /**
  * Information needed for a single ref update.  Set new_sha1 to the
  * new value or to zero to delete the ref.  To check the old value
@@ -3584,6 +3352,21 @@ struct ref_update {
        const char refname[FLEX_ARRAY];
 };
 
+/*
+ * Transaction states.
+ * OPEN:   The transaction is in a valid state and can accept new updates.
+ *         An OPEN transaction can be committed.
+ * CLOSED: A closed transaction is no longer active and no other operations
+ *         than free can be used on it in this state.
+ *         A transaction can either become closed by successfully committing
+ *         an active transaction or if there is a failure while building
+ *         the transaction thus rendering it failed/inactive.
+ */
+enum ref_transaction_state {
+       REF_TRANSACTION_OPEN   = 0,
+       REF_TRANSACTION_CLOSED = 1
+};
+
 /*
  * Data structure for holding a reference transaction, which can
  * consist of checks and updates to multiple references, carried out
@@ -3593,9 +3376,10 @@ struct ref_transaction {
        struct ref_update **updates;
        size_t alloc;
        size_t nr;
+       enum ref_transaction_state state;
 };
 
-struct ref_transaction *ref_transaction_begin(void)
+struct ref_transaction *ref_transaction_begin(struct strbuf *err)
 {
        return xcalloc(1, sizeof(struct ref_transaction));
 }
@@ -3635,6 +3419,9 @@ int ref_transaction_update(struct ref_transaction *transaction,
 {
        struct ref_update *update;
 
+       if (transaction->state != REF_TRANSACTION_OPEN)
+               die("BUG: update called for transaction that is not open");
+
        if (have_old && !old_sha1)
                die("BUG: have_old is true but old_sha1 is NULL");
 
@@ -3647,44 +3434,84 @@ int ref_transaction_update(struct ref_transaction *transaction,
        return 0;
 }
 
-void ref_transaction_create(struct ref_transaction *transaction,
-                           const char *refname,
-                           const unsigned char *new_sha1,
-                           int flags)
+int ref_transaction_create(struct ref_transaction *transaction,
+                          const char *refname,
+                          const unsigned char *new_sha1,
+                          int flags,
+                          struct strbuf *err)
 {
-       struct ref_update *update = add_update(transaction, refname);
+       struct ref_update *update;
+
+       if (transaction->state != REF_TRANSACTION_OPEN)
+               die("BUG: create called for transaction that is not open");
+
+       if (!new_sha1 || is_null_sha1(new_sha1))
+               die("BUG: create ref with null new_sha1");
+
+       update = add_update(transaction, refname);
 
-       assert(!is_null_sha1(new_sha1));
        hashcpy(update->new_sha1, new_sha1);
        hashclr(update->old_sha1);
        update->flags = flags;
        update->have_old = 1;
+       return 0;
 }
 
-void ref_transaction_delete(struct ref_transaction *transaction,
-                           const char *refname,
-                           const unsigned char *old_sha1,
-                           int flags, int have_old)
+int ref_transaction_delete(struct ref_transaction *transaction,
+                          const char *refname,
+                          const unsigned char *old_sha1,
+                          int flags, int have_old,
+                          struct strbuf *err)
 {
-       struct ref_update *update = add_update(transaction, refname);
+       struct ref_update *update;
+
+       if (transaction->state != REF_TRANSACTION_OPEN)
+               die("BUG: delete called for transaction that is not open");
+
+       if (have_old && !old_sha1)
+               die("BUG: have_old is true but old_sha1 is NULL");
 
+       update = add_update(transaction, refname);
        update->flags = flags;
        update->have_old = have_old;
        if (have_old) {
                assert(!is_null_sha1(old_sha1));
                hashcpy(update->old_sha1, old_sha1);
        }
+       return 0;
 }
 
 int update_ref(const char *action, const char *refname,
               const unsigned char *sha1, const unsigned char *oldval,
               int flags, enum action_on_err onerr)
 {
-       struct ref_lock *lock;
-       lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
-       if (!lock)
+       struct ref_transaction *t;
+       struct strbuf err = STRBUF_INIT;
+
+       t = ref_transaction_begin(&err);
+       if (!t ||
+           ref_transaction_update(t, refname, sha1, oldval, flags,
+                                  !!oldval, &err) ||
+           ref_transaction_commit(t, action, &err)) {
+               const char *str = "update_ref failed for ref '%s': %s";
+
+               ref_transaction_free(t);
+               switch (onerr) {
+               case UPDATE_REFS_MSG_ON_ERR:
+                       error(str, refname, err.buf);
+                       break;
+               case UPDATE_REFS_DIE_ON_ERR:
+                       die(str, refname, err.buf);
+                       break;
+               case UPDATE_REFS_QUIET_ON_ERR:
+                       break;
+               }
+               strbuf_release(&err);
                return 1;
-       return update_ref_write(action, refname, sha1, lock, NULL, onerr);
+       }
+       strbuf_release(&err);
+       ref_transaction_free(t);
+       return 0;
 }
 
 static int ref_update_compare(const void *r1, const void *r2)
@@ -3718,8 +3545,13 @@ int ref_transaction_commit(struct ref_transaction *transaction,
        int n = transaction->nr;
        struct ref_update **updates = transaction->updates;
 
-       if (!n)
+       if (transaction->state != REF_TRANSACTION_OPEN)
+               die("BUG: commit called for transaction that is not open");
+
+       if (!n) {
+               transaction->state = REF_TRANSACTION_CLOSED;
                return 0;
+       }
 
        /* Allocate work space */
        delnames = xmalloc(sizeof(*delnames) * n);
@@ -3734,12 +3566,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
        for (i = 0; i < n; i++) {
                struct ref_update *update = updates[i];
 
-               update->lock = update_ref_lock(update->refname,
-                                              (update->have_old ?
-                                               update->old_sha1 : NULL),
-                                              update->flags,
-                                              &update->type,
-                                              UPDATE_REFS_QUIET_ON_ERR);
+               update->lock = lock_any_ref_for_update(update->refname,
+                                                      (update->have_old ?
+                                                       update->old_sha1 :
+                                                       NULL),
+                                                      update->flags,
+                                                      &update->type);
                if (!update->lock) {
                        if (err)
                                strbuf_addf(err, "Cannot lock the ref '%s'.",
@@ -3754,14 +3586,15 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                struct ref_update *update = updates[i];
 
                if (!is_null_sha1(update->new_sha1)) {
-                       ret = update_ref_write(msg,
-                                              update->refname,
-                                              update->new_sha1,
-                                              update->lock, err,
-                                              UPDATE_REFS_QUIET_ON_ERR);
-                       update->lock = NULL; /* freed by update_ref_write */
-                       if (ret)
+                       ret = write_ref_sha1(update->lock, update->new_sha1,
+                                            msg);
+                       update->lock = NULL; /* freed by write_ref_sha1 */
+                       if (ret) {
+                               if (err)
+                                       strbuf_addf(err, "Cannot update the ref '%s'.",
+                                                   update->refname);
                                goto cleanup;
+                       }
                }
        }
 
@@ -3770,8 +3603,9 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                struct ref_update *update = updates[i];
 
                if (update->lock) {
-                       delnames[delnum++] = update->lock->ref_name;
                        ret |= delete_ref_loose(update->lock, update->type);
+                       if (!(update->flags & REF_ISPRUNING))
+                               delnames[delnum++] = update->lock->ref_name;
                }
        }
 
@@ -3781,6 +3615,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
        clear_loose_ref_cache(&ref_cache);
 
 cleanup:
+       transaction->state = REF_TRANSACTION_CLOSED;
+
        for (i = 0; i < n; i++)
                if (updates[i]->lock)
                        unlock_ref(updates[i]->lock);