Merge branch 'ma/split-symref-update-fix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 Sep 2017 01:47:53 +0000 (10:47 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Sep 2017 01:47:53 +0000 (10:47 +0900)
A leakfix.

* ma/split-symref-update-fix:
refs/files-backend: add `refname`, not "HEAD", to list
refs/files-backend: correct return value in lock_ref_for_update
refs/files-backend: fix memory leak in lock_ref_for_update
refs/files-backend: add longer-scoped copy of string to list

1  2 
refs/files-backend.c
diff --combined refs/files-backend.c
index fccbc24ac4ad3631b96a4796f4b1f89913fa0ea3,926f87b9455d9efa317e466113fbe4214afc515f..3a7bf6a146b04456238e330f314c30e224d4c6e6
@@@ -3,7 -3,6 +3,7 @@@
  #include "../refs.h"
  #include "refs-internal.h"
  #include "ref-cache.h"
 +#include "packed-backend.h"
  #include "../iterator.h"
  #include "../dir-iterator.h"
  #include "../lockfile.h"
@@@ -16,6 -15,39 +16,6 @@@ struct ref_lock 
        struct object_id old_oid;
  };
  
 -/*
 - * 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.
 - */
 -static 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;
 -}
 -
 -struct packed_ref_cache {
 -      struct ref_cache *cache;
 -
 -      /*
 -       * Count of references to the data structure in this instance,
 -       * including the pointer from files_ref_store::packed if any.
 -       * The data will not be freed as long as the reference count
 -       * is nonzero.
 -       */
 -      unsigned int referrers;
 -
 -      /* The metadata from when this packed-refs cache was read */
 -      struct stat_validity validity;
 -};
 -
  /*
   * Future: need to be in "struct repository"
   * when doing a full libification.
@@@ -26,12 -58,54 +26,12 @@@ struct files_ref_store 
  
        char *gitdir;
        char *gitcommondir;
 -      char *packed_refs_path;
  
        struct ref_cache *loose;
 -      struct packed_ref_cache *packed;
  
 -      /*
 -       * Lock used for the "packed-refs" file. Note that this (and
 -       * thus the enclosing `files_ref_store`) must not be freed.
 -       */
 -      struct lock_file packed_refs_lock;
 +      struct ref_store *packed_ref_store;
  };
  
 -/*
 - * Increment the reference count of *packed_refs.
 - */
 -static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
 -{
 -      packed_refs->referrers++;
 -}
 -
 -/*
 - * Decrease the reference count of *packed_refs.  If it goes to zero,
 - * free *packed_refs and return true; otherwise return false.
 - */
 -static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
 -{
 -      if (!--packed_refs->referrers) {
 -              free_ref_cache(packed_refs->cache);
 -              stat_validity_clear(&packed_refs->validity);
 -              free(packed_refs);
 -              return 1;
 -      } else {
 -              return 0;
 -      }
 -}
 -
 -static void clear_packed_ref_cache(struct files_ref_store *refs)
 -{
 -      if (refs->packed) {
 -              struct packed_ref_cache *packed_refs = refs->packed;
 -
 -              if (is_lock_file_locked(&refs->packed_refs_lock))
 -                      die("BUG: packed-ref cache cleared while locked");
 -              refs->packed = NULL;
 -              release_packed_ref_cache(packed_refs);
 -      }
 -}
 -
  static void clear_loose_ref_cache(struct files_ref_store *refs)
  {
        if (refs->loose) {
@@@ -58,8 -132,7 +58,8 @@@ static struct ref_store *files_ref_stor
        get_common_dir_noenv(&sb, gitdir);
        refs->gitcommondir = strbuf_detach(&sb, NULL);
        strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
 -      refs->packed_refs_path = strbuf_detach(&sb, NULL);
 +      refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
 +      strbuf_release(&sb);
  
        return ref_store;
  }
@@@ -102,6 -175,156 +102,6 @@@ static struct files_ref_store *files_do
        return refs;
  }
  
 -/* The length of a peeled reference line in packed-refs, including EOL: */
 -#define PEELED_LINE_LENGTH 42
 -
 -/*
 - * The packed-refs header line that we write out.  Perhaps other
 - * traits will be added later.  The trailing space is required.
 - */
 -static const char PACKED_REFS_HEADER[] =
 -      "# pack-refs with: peeled fully-peeled \n";
 -
 -/*
 - * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
 - * Return a pointer to the refname within the line (null-terminated),
 - * or NULL if there was a problem.
 - */
 -static const char *parse_ref_line(struct strbuf *line, struct object_id *oid)
 -{
 -      const char *ref;
 -
 -      if (parse_oid_hex(line->buf, oid, &ref) < 0)
 -              return NULL;
 -      if (!isspace(*ref++))
 -              return NULL;
 -
 -      if (isspace(*ref))
 -              return NULL;
 -
 -      if (line->buf[line->len - 1] != '\n')
 -              return NULL;
 -      line->buf[--line->len] = 0;
 -
 -      return ref;
 -}
 -
 -/*
 - * Read from `packed_refs_file` into a newly-allocated
 - * `packed_ref_cache` and return it. The return value will already
 - * have its reference count incremented.
 - *
 - * A comment line of the form "# pack-refs with: " may contain zero or
 - * more traits. We interpret the traits as follows:
 - *
 - *   No traits:
 - *
 - *      Probably no references are peeled. But if the file contains a
 - *      peeled value for a reference, we will use it.
 - *
 - *   peeled:
 - *
 - *      References under "refs/tags/", if they *can* be peeled, *are*
 - *      peeled in this file. References outside of "refs/tags/" are
 - *      probably not peeled even if they could have been, but if we find
 - *      a peeled value for such a reference we will use it.
 - *
 - *   fully-peeled:
 - *
 - *      All references in the file that can be peeled are peeled.
 - *      Inversely (and this is more important), any references in the
 - *      file for which no peeled value is recorded is not peelable. This
 - *      trait should typically be written alongside "peeled" for
 - *      compatibility with older clients, but we do not require it
 - *      (i.e., "peeled" is a no-op if "fully-peeled" is set).
 - */
 -static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file)
 -{
 -      FILE *f;
 -      struct packed_ref_cache *packed_refs = xcalloc(1, sizeof(*packed_refs));
 -      struct ref_entry *last = NULL;
 -      struct strbuf line = STRBUF_INIT;
 -      enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
 -      struct ref_dir *dir;
 -
 -      acquire_packed_ref_cache(packed_refs);
 -      packed_refs->cache = create_ref_cache(NULL, NULL);
 -      packed_refs->cache->root->flag &= ~REF_INCOMPLETE;
 -
 -      f = fopen(packed_refs_file, "r");
 -      if (!f) {
 -              if (errno == ENOENT) {
 -                      /*
 -                       * This is OK; it just means that no
 -                       * "packed-refs" file has been written yet,
 -                       * which is equivalent to it being empty.
 -                       */
 -                      return packed_refs;
 -              } else {
 -                      die_errno("couldn't read %s", packed_refs_file);
 -              }
 -      }
 -
 -      stat_validity_update(&packed_refs->validity, fileno(f));
 -
 -      dir = get_ref_dir(packed_refs->cache->root);
 -      while (strbuf_getwholeline(&line, f, '\n') != EOF) {
 -              struct object_id oid;
 -              const char *refname;
 -              const char *traits;
 -
 -              if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
 -                      if (strstr(traits, " fully-peeled "))
 -                              peeled = PEELED_FULLY;
 -                      else if (strstr(traits, " peeled "))
 -                              peeled = PEELED_TAGS;
 -                      /* perhaps other traits later as well */
 -                      continue;
 -              }
 -
 -              refname = parse_ref_line(&line, &oid);
 -              if (refname) {
 -                      int flag = REF_ISPACKED;
 -
 -                      if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
 -                              if (!refname_is_safe(refname))
 -                                      die("packed refname is dangerous: %s", refname);
 -                              oidclr(&oid);
 -                              flag |= REF_BAD_NAME | REF_ISBROKEN;
 -                      }
 -                      last = create_ref_entry(refname, &oid, flag);
 -                      if (peeled == PEELED_FULLY ||
 -                          (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
 -                              last->flag |= REF_KNOWS_PEELED;
 -                      add_ref_entry(dir, last);
 -                      continue;
 -              }
 -              if (last &&
 -                  line.buf[0] == '^' &&
 -                  line.len == PEELED_LINE_LENGTH &&
 -                  line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
 -                  !get_oid_hex(line.buf + 1, &oid)) {
 -                      oidcpy(&last->u.value.peeled, &oid);
 -                      /*
 -                       * Regardless of what the file header said,
 -                       * we definitely know the value of *this*
 -                       * reference:
 -                       */
 -                      last->flag |= REF_KNOWS_PEELED;
 -              }
 -      }
 -
 -      fclose(f);
 -      strbuf_release(&line);
 -
 -      return packed_refs;
 -}
 -
 -static const char *files_packed_refs_path(struct files_ref_store *refs)
 -{
 -      return refs->packed_refs_path;
 -}
 -
  static void files_reflog_path(struct files_ref_store *refs,
                              struct strbuf *sb,
                              const char *refname)
@@@ -147,6 -370,70 +147,6 @@@ static void files_ref_path(struct files
        }
  }
  
 -/*
 - * Check that the packed refs cache (if any) still reflects the
 - * contents of the file. If not, clear the cache.
 - */
 -static void validate_packed_ref_cache(struct files_ref_store *refs)
 -{
 -      if (refs->packed &&
 -          !stat_validity_check(&refs->packed->validity,
 -                               files_packed_refs_path(refs)))
 -              clear_packed_ref_cache(refs);
 -}
 -
 -/*
 - * Get the packed_ref_cache for the specified files_ref_store,
 - * creating and populating it if it hasn't been read before or if the
 - * file has been changed (according to its `validity` field) since it
 - * was last read. On the other hand, if we hold the lock, then assume
 - * that the file hasn't been changed out from under us, so skip the
 - * extra `stat()` call in `stat_validity_check()`.
 - */
 -static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)
 -{
 -      const char *packed_refs_file = files_packed_refs_path(refs);
 -
 -      if (!is_lock_file_locked(&refs->packed_refs_lock))
 -              validate_packed_ref_cache(refs);
 -
 -      if (!refs->packed)
 -              refs->packed = read_packed_refs(packed_refs_file);
 -
 -      return refs->packed;
 -}
 -
 -static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
 -{
 -      return get_ref_dir(packed_ref_cache->cache->root);
 -}
 -
 -static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
 -{
 -      return get_packed_ref_dir(get_packed_ref_cache(refs));
 -}
 -
 -/*
 - * Add a reference to the in-memory packed reference cache.  This may
 - * only be called while the packed-refs file is locked (see
 - * lock_packed_refs()).  To actually write the packed-refs file, call
 - * commit_packed_refs().
 - */
 -static void add_packed_ref(struct files_ref_store *refs,
 -                         const char *refname, const struct object_id *oid)
 -{
 -      struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs);
 -
 -      if (!is_lock_file_locked(&refs->packed_refs_lock))
 -              die("BUG: packed refs not locked");
 -
 -      if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
 -              die("Reference has invalid format: '%s'", refname);
 -
 -      add_ref_entry(get_packed_ref_dir(packed_ref_cache),
 -                    create_ref_entry(refname, oid, REF_ISPACKED));
 -}
 -
  /*
   * Read the loose references from the namespace dirname into dir
   * (without recursing).  dirname must end with '/'.  dir must be the
@@@ -269,6 -556,39 +269,6 @@@ static struct ref_cache *get_loose_ref_
        return refs->loose;
  }
  
 -/*
 - * Return the ref_entry for the given refname from the packed
 - * references.  If it does not exist, return NULL.
 - */
 -static struct ref_entry *get_packed_ref(struct files_ref_store *refs,
 -                                      const char *refname)
 -{
 -      return find_ref_entry(get_packed_refs(refs), refname);
 -}
 -
 -/*
 - * A loose ref file doesn't exist; check for a packed ref.
 - */
 -static int resolve_packed_ref(struct files_ref_store *refs,
 -                            const char *refname,
 -                            unsigned char *sha1, unsigned int *flags)
 -{
 -      struct ref_entry *entry;
 -
 -      /*
 -       * The loose reference file does not exist; check for a packed
 -       * reference.
 -       */
 -      entry = get_packed_ref(refs, refname);
 -      if (entry) {
 -              hashcpy(sha1, entry->u.value.oid.hash);
 -              *flags |= REF_ISPACKED;
 -              return 0;
 -      }
 -      /* refname is not a packed reference. */
 -      return -1;
 -}
 -
  static int files_read_raw_ref(struct ref_store *ref_store,
                              const char *refname, unsigned char *sha1,
                              struct strbuf *referent, unsigned int *type)
@@@ -312,8 -632,7 +312,8 @@@ stat_ref
        if (lstat(path, &st) < 0) {
                if (errno != ENOENT)
                        goto out;
 -              if (resolve_packed_ref(refs, refname, sha1, type)) {
 +              if (refs_read_raw_ref(refs->packed_ref_store, refname,
 +                                    sha1, referent, type)) {
                        errno = ENOENT;
                        goto out;
                }
                 * ref is supposed to be, there could still be a
                 * packed ref:
                 */
 -              if (resolve_packed_ref(refs, refname, sha1, type)) {
 +              if (refs_read_raw_ref(refs->packed_ref_store, refname,
 +                                    sha1, referent, type)) {
                        errno = EISDIR;
                        goto out;
                }
@@@ -537,9 -855,7 +537,9 @@@ retry
        if (!lock->lk)
                lock->lk = xcalloc(1, sizeof(struct lock_file));
  
 -      if (hold_lock_file_for_update(lock->lk, ref_file.buf, LOCK_NO_DEREF) < 0) {
 +      if (hold_lock_file_for_update_timeout(
 +                          lock->lk, ref_file.buf, LOCK_NO_DEREF,
 +                          get_files_ref_lock_timeout_ms()) < 0) {
                if (errno == ENOENT && --attempts_remaining > 0) {
                        /*
                         * Maybe somebody just deleted one of the
  
                /*
                 * If the ref did not exist and we are creating it,
 -               * make sure there is no existing ref that conflicts
 -               * with refname:
 +               * make sure there is no existing packed ref that
 +               * conflicts with refname:
                 */
                if (refs_verify_refname_available(
 -                                  &refs->base, refname,
 +                                  refs->packed_ref_store, refname,
                                    extras, skip, err))
                        goto error_return;
        }
@@@ -685,9 -1001,15 +685,9 @@@ static int files_peel_ref(struct ref_st
         * be expensive and (b) loose references anyway usually do not
         * have REF_KNOWS_PEELED.
         */
 -      if (flag & REF_ISPACKED) {
 -              struct ref_entry *r = get_packed_ref(refs, refname);
 -              if (r) {
 -                      if (peel_entry(r, 0))
 -                              return -1;
 -                      hashcpy(sha1, r->u.value.peeled.hash);
 -                      return 0;
 -              }
 -      }
 +      if (flag & REF_ISPACKED &&
 +          !refs_peel_ref(refs->packed_ref_store, refname, sha1))
 +              return 0;
  
        return peel_object(base, sha1);
  }
  struct files_ref_iterator {
        struct ref_iterator base;
  
 -      struct packed_ref_cache *packed_ref_cache;
        struct ref_iterator *iter0;
        unsigned int flags;
  };
@@@ -747,6 -1070,7 +747,6 @@@ static int files_ref_iterator_abort(str
        if (iter->iter0)
                ok = ref_iterator_abort(iter->iter0);
  
 -      release_packed_ref_cache(iter->packed_ref_cache);
        base_ref_iterator_free(ref_iterator);
        return ok;
  }
@@@ -788,28 -1112,18 +788,28 @@@ static struct ref_iterator *files_ref_i
         * (If they've already been read, that's OK; we only need to
         * guarantee that they're read before the packed refs, not
         * *how much* before.) After that, we call
 -       * get_packed_ref_cache(), which internally checks whether the
 -       * packed-ref cache is up to date with what is on disk, and
 -       * re-reads it if not.
 +       * packed_ref_iterator_begin(), which internally checks
 +       * whether the packed-ref cache is up to date with what is on
 +       * disk, and re-reads it if not.
         */
  
        loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
                                              prefix, 1);
  
 -      iter->packed_ref_cache = get_packed_ref_cache(refs);
 -      acquire_packed_ref_cache(iter->packed_ref_cache);
 -      packed_iter = cache_ref_iterator_begin(iter->packed_ref_cache->cache,
 -                                             prefix, 0);
 +      /*
 +       * The packed-refs file might contain broken references, for
 +       * example an old version of a reference that points at an
 +       * object that has since been garbage-collected. This is OK as
 +       * long as there is a corresponding loose reference that
 +       * overrides it, and we don't want to emit an error message in
 +       * this case. So ask the packed_ref_store for all of its
 +       * references, and (if needed) do our own check for broken
 +       * ones in files_ref_iterator_advance(), after we have merged
 +       * the packed and loose references.
 +       */
 +      packed_iter = refs_ref_iterator_begin(
 +                      refs->packed_ref_store, prefix, 0,
 +                      DO_FOR_EACH_INCLUDE_BROKEN);
  
        iter->iter0 = overlay_ref_iterator_begin(loose_iter, packed_iter);
        iter->flags = flags;
@@@ -867,9 -1181,7 +867,9 @@@ static int create_reflock(const char *p
  {
        struct lock_file *lk = cb;
  
 -      return hold_lock_file_for_update(lk, path, LOCK_NO_DEREF) < 0 ? -1 : 0;
 +      return hold_lock_file_for_update_timeout(
 +                      lk, path, LOCK_NO_DEREF,
 +                      get_files_ref_lock_timeout_ms()) < 0 ? -1 : 0;
  }
  
  /*
@@@ -943,7 -1255,7 +943,7 @@@ static struct ref_lock *lock_ref_sha1_b
         * our refname.
         */
        if (is_null_oid(&lock->old_oid) &&
 -          refs_verify_refname_available(&refs->base, refname,
 +          refs_verify_refname_available(refs->packed_ref_store, refname,
                                          extras, skip, err)) {
                last_errno = ENOTDIR;
                goto error_return;
        return lock;
  }
  
 -/*
 - * Write an entry to the packed-refs file for the specified refname.
 - * If peeled is non-NULL, write it as the entry's peeled value.
 - */
 -static void write_packed_entry(FILE *fh, const char *refname,
 -                             const unsigned char *sha1,
 -                             const unsigned char *peeled)
 -{
 -      fprintf_or_die(fh, "%s %s\n", sha1_to_hex(sha1), refname);
 -      if (peeled)
 -              fprintf_or_die(fh, "^%s\n", sha1_to_hex(peeled));
 -}
 -
 -/*
 - * Lock the packed-refs file for writing. Flags is passed to
 - * hold_lock_file_for_update(). Return 0 on success. On errors, set
 - * errno appropriately and return a nonzero value.
 - */
 -static int lock_packed_refs(struct files_ref_store *refs, int flags)
 -{
 -      static int timeout_configured = 0;
 -      static int timeout_value = 1000;
 -      struct packed_ref_cache *packed_ref_cache;
 -
 -      files_assert_main_repository(refs, "lock_packed_refs");
 -
 -      if (!timeout_configured) {
 -              git_config_get_int("core.packedrefstimeout", &timeout_value);
 -              timeout_configured = 1;
 -      }
 -
 -      if (hold_lock_file_for_update_timeout(
 -                          &refs->packed_refs_lock, files_packed_refs_path(refs),
 -                          flags, timeout_value) < 0)
 -              return -1;
 -
 -      /*
 -       * Now that we hold the `packed-refs` lock, make sure that our
 -       * cache matches the current version of the file. Normally
 -       * `get_packed_ref_cache()` does that for us, but that
 -       * function assumes that when the file is locked, any existing
 -       * cache is still valid. We've just locked the file, but it
 -       * might have changed the moment *before* we locked it.
 -       */
 -      validate_packed_ref_cache(refs);
 -
 -      packed_ref_cache = get_packed_ref_cache(refs);
 -      /* Increment the reference count to prevent it from being freed: */
 -      acquire_packed_ref_cache(packed_ref_cache);
 -      return 0;
 -}
 -
 -/*
 - * Write the current version of the packed refs cache from memory to
 - * disk. The packed-refs file must already be locked for writing (see
 - * lock_packed_refs()). Return zero on success. On errors, set errno
 - * and return a nonzero value
 - */
 -static int commit_packed_refs(struct files_ref_store *refs)
 -{
 -      struct packed_ref_cache *packed_ref_cache =
 -              get_packed_ref_cache(refs);
 -      int ok, error = 0;
 -      int save_errno = 0;
 -      FILE *out;
 -      struct ref_iterator *iter;
 -
 -      files_assert_main_repository(refs, "commit_packed_refs");
 -
 -      if (!is_lock_file_locked(&refs->packed_refs_lock))
 -              die("BUG: packed-refs not locked");
 -
 -      out = fdopen_lock_file(&refs->packed_refs_lock, "w");
 -      if (!out)
 -              die_errno("unable to fdopen packed-refs descriptor");
 -
 -      fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
 -
 -      iter = cache_ref_iterator_begin(packed_ref_cache->cache, NULL, 0);
 -      while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
 -              struct object_id peeled;
 -              int peel_error = ref_iterator_peel(iter, &peeled);
 -
 -              write_packed_entry(out, iter->refname, iter->oid->hash,
 -                                 peel_error ? NULL : peeled.hash);
 -      }
 -
 -      if (ok != ITER_DONE)
 -              die("error while iterating over references");
 -
 -      if (commit_lock_file(&refs->packed_refs_lock)) {
 -              save_errno = errno;
 -              error = -1;
 -      }
 -      release_packed_ref_cache(packed_ref_cache);
 -      errno = save_errno;
 -      return error;
 -}
 -
 -/*
 - * Rollback the lockfile for the packed-refs file, and discard the
 - * in-memory packed reference cache.  (The packed-refs file will be
 - * read anew if it is needed again after this function is called.)
 - */
 -static void rollback_packed_refs(struct files_ref_store *refs)
 -{
 -      struct packed_ref_cache *packed_ref_cache =
 -              get_packed_ref_cache(refs);
 -
 -      files_assert_main_repository(refs, "rollback_packed_refs");
 -
 -      if (!is_lock_file_locked(&refs->packed_refs_lock))
 -              die("BUG: packed-refs not locked");
 -      rollback_lock_file(&refs->packed_refs_lock);
 -      release_packed_ref_cache(packed_ref_cache);
 -      clear_packed_ref_cache(refs);
 -}
 -
  struct ref_to_prune {
        struct ref_to_prune *next;
        unsigned char sha1[20];
@@@ -1097,11 -1527,12 +1097,11 @@@ static int files_pack_refs(struct ref_s
                files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
                               "pack_refs");
        struct ref_iterator *iter;
 -      struct ref_dir *packed_refs;
        int ok;
        struct ref_to_prune *refs_to_prune = NULL;
 +      struct strbuf err = STRBUF_INIT;
  
 -      lock_packed_refs(refs, LOCK_DIE_ON_ERROR);
 -      packed_refs = get_packed_refs(refs);
 +      packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
  
        iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0);
        while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
                 * in the packed ref cache. If the reference should be
                 * pruned, also add it to refs_to_prune.
                 */
 -              struct ref_entry *packed_entry;
 -
                if (!should_pack_ref(iter->refname, iter->oid, iter->flags,
                                     flags))
                        continue;
                 * we don't copy the peeled status, because we want it
                 * to be re-peeled.
                 */
 -              packed_entry = find_ref_entry(packed_refs, iter->refname);
 -              if (packed_entry) {
 -                      /* Overwrite existing packed entry with info from loose entry */
 -                      packed_entry->flag = REF_ISPACKED;
 -                      oidcpy(&packed_entry->u.value.oid, iter->oid);
 -              } else {
 -                      packed_entry = create_ref_entry(iter->refname, iter->oid,
 -                                                      REF_ISPACKED);
 -                      add_ref_entry(packed_refs, packed_entry);
 -              }
 -              oidclr(&packed_entry->u.value.peeled);
 +              add_packed_ref(refs->packed_ref_store, iter->refname, iter->oid);
  
                /* Schedule the loose reference for pruning if requested. */
                if ((flags & PACK_REFS_PRUNE)) {
        if (ok != ITER_DONE)
                die("error while iterating over references");
  
 -      if (commit_packed_refs(refs))
 -              die_errno("unable to overwrite old ref-pack file");
 +      if (commit_packed_refs(refs->packed_ref_store, &err))
 +              die("unable to overwrite old ref-pack file: %s", err.buf);
 +      packed_refs_unlock(refs->packed_ref_store);
  
        prune_refs(refs, refs_to_prune);
 +      strbuf_release(&err);
        return 0;
  }
  
 -/*
 - * Rewrite the packed-refs file, omitting any refs listed in
 - * 'refnames'. On error, leave packed-refs unchanged, write an error
 - * message to 'err', and return a nonzero value.
 - *
 - * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
 - */
 -static int repack_without_refs(struct files_ref_store *refs,
 -                             struct string_list *refnames, struct strbuf *err)
 -{
 -      struct ref_dir *packed;
 -      struct string_list_item *refname;
 -      int ret, needs_repacking = 0, removed = 0;
 -
 -      files_assert_main_repository(refs, "repack_without_refs");
 -      assert(err);
 -
 -      /* Look for a packed ref */
 -      for_each_string_list_item(refname, refnames) {
 -              if (get_packed_ref(refs, refname->string)) {
 -                      needs_repacking = 1;
 -                      break;
 -              }
 -      }
 -
 -      /* Avoid locking if we have nothing to do */
 -      if (!needs_repacking)
 -              return 0; /* no refname exists in packed refs */
 -
 -      if (lock_packed_refs(refs, 0)) {
 -              unable_to_lock_message(files_packed_refs_path(refs), errno, err);
 -              return -1;
 -      }
 -      packed = get_packed_refs(refs);
 -
 -      /* Remove refnames from the cache */
 -      for_each_string_list_item(refname, refnames)
 -              if (remove_entry_from_dir(packed, refname->string) != -1)
 -                      removed = 1;
 -      if (!removed) {
 -              /*
 -               * All packed entries disappeared while we were
 -               * acquiring the lock.
 -               */
 -              rollback_packed_refs(refs);
 -              return 0;
 -      }
 -
 -      /* Write what remains */
 -      ret = commit_packed_refs(refs);
 -      if (ret)
 -              strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
 -                          strerror(errno));
 -      return ret;
 -}
 -
  static int files_delete_refs(struct ref_store *ref_store, const char *msg,
                             struct string_list *refnames, unsigned int flags)
  {
        if (!refnames->nr)
                return 0;
  
 -      result = repack_without_refs(refs, refnames, &err);
 -      if (result) {
 -              /*
 -               * If we failed to rewrite the packed-refs file, then
 -               * it is unsafe to try to remove loose refs, because
 -               * doing so might expose an obsolete packed value for
 -               * a reference that might even point at an object that
 -               * has been garbage collected.
 -               */
 -              if (refnames->nr == 1)
 -                      error(_("could not delete reference %s: %s"),
 -                            refnames->items[0].string, err.buf);
 -              else
 -                      error(_("could not delete references: %s"), err.buf);
 +      if (packed_refs_lock(refs->packed_ref_store, 0, &err))
 +              goto error;
  
 -              goto out;
 +      if (repack_without_refs(refs->packed_ref_store, refnames, &err)) {
 +              packed_refs_unlock(refs->packed_ref_store);
 +              goto error;
        }
  
 +      packed_refs_unlock(refs->packed_ref_store);
 +
        for (i = 0; i < refnames->nr; i++) {
                const char *refname = refnames->items[i].string;
  
                        result |= error(_("could not remove reference %s"), refname);
        }
  
 -out:
        strbuf_release(&err);
        return result;
 +
 +error:
 +      /*
 +       * If we failed to rewrite the packed-refs file, then it is
 +       * unsafe to try to remove loose refs, because doing so might
 +       * expose an obsolete packed value for a reference that might
 +       * even point at an object that has been garbage collected.
 +       */
 +      if (refnames->nr == 1)
 +              error(_("could not delete reference %s: %s"),
 +                    refnames->items[0].string, err.buf);
 +      else
 +              error(_("could not delete references: %s"), err.buf);
 +
 +      strbuf_release(&err);
 +      return -1;
  }
  
  /*
@@@ -2099,11 -2589,10 +2099,10 @@@ static int split_head_update(struct ref
  
        /*
         * First make sure that HEAD is not already in the
-        * transaction. This insertion is O(N) in the transaction
+        * transaction. This check is O(lg N) in the transaction
         * size, but it happens at most once per transaction.
         */
-       item = string_list_insert(affected_refnames, "HEAD");
-       if (item->util) {
+       if (string_list_has_string(affected_refnames, "HEAD")) {
                /* An entry already existed */
                strbuf_addf(err,
                            "multiple updates for 'HEAD' (including one "
                        update->new_oid.hash, update->old_oid.hash,
                        update->msg);
  
+       /*
+        * Add "HEAD". This insertion is O(N) in the transaction
+        * size, but it happens at most once per transaction.
+        * Add new_update->refname instead of a literal "HEAD".
+        */
+       if (strcmp(new_update->refname, "HEAD"))
+               BUG("%s unexpectedly not 'HEAD'", new_update->refname);
+       item = string_list_insert(affected_refnames, new_update->refname);
        item->util = new_update;
  
        return 0;
@@@ -2144,13 -2641,12 +2151,12 @@@ static int split_symref_update(struct f
  
        /*
         * First make sure that referent is not already in the
-        * transaction. This insertion is O(N) in the transaction
+        * transaction. This check is O(lg N) in the transaction
         * size, but it happens at most once per symref in a
         * transaction.
         */
-       item = string_list_insert(affected_refnames, referent);
-       if (item->util) {
-               /* An entry already existed */
+       if (string_list_has_string(affected_refnames, referent)) {
+               /* An entry already exists */
                strbuf_addf(err,
                            "multiple updates for '%s' (including one "
                            "via symref '%s') are not allowed",
        update->flags |= REF_LOG_ONLY | REF_NODEREF;
        update->flags &= ~REF_HAVE_OLD;
  
+       /*
+        * Add the referent. This insertion is O(N) in the transaction
+        * size, but it happens at most once per symref in a
+        * transaction. Make sure to add new_update->refname, which will
+        * be valid as long as affected_refnames is in use, and NOT
+        * referent, which might soon be freed by our caller.
+        */
+       item = string_list_insert(affected_refnames, new_update->refname);
+       if (item->util)
+               BUG("%s unexpectedly found in affected_refnames",
+                   new_update->refname);
        item->util = new_update;
  
        return 0;
@@@ -2256,7 -2763,7 +2273,7 @@@ static int lock_ref_for_update(struct f
        struct strbuf referent = STRBUF_INIT;
        int mustexist = (update->flags & REF_HAVE_OLD) &&
                !is_null_oid(&update->old_oid);
-       int ret;
+       int ret = 0;
        struct ref_lock *lock;
  
        files_assert_main_repository(refs, "lock_ref_for_update");
                ret = split_head_update(update, transaction, head_ref,
                                        affected_refnames, err);
                if (ret)
-                       return ret;
+                       goto out;
        }
  
        ret = lock_raw_ref(refs, update->refname, mustexist,
                strbuf_addf(err, "cannot lock ref '%s': %s",
                            original_update_refname(update), reason);
                free(reason);
-               return ret;
+               goto out;
        }
  
        update->backend_data = lock;
                                        strbuf_addf(err, "cannot lock ref '%s': "
                                                    "error reading reference",
                                                    original_update_refname(update));
-                                       return -1;
+                                       ret = TRANSACTION_GENERIC_ERROR;
+                                       goto out;
                                }
                        } else if (check_old_oid(update, &lock->old_oid, err)) {
-                               return TRANSACTION_GENERIC_ERROR;
+                               ret = TRANSACTION_GENERIC_ERROR;
+                               goto out;
                        }
                } else {
                        /*
                                                  referent.buf, transaction,
                                                  affected_refnames, err);
                        if (ret)
-                               return ret;
+                               goto out;
                }
        } else {
                struct ref_update *parent_update;
  
-               if (check_old_oid(update, &lock->old_oid, err))
-                       return TRANSACTION_GENERIC_ERROR;
+               if (check_old_oid(update, &lock->old_oid, err)) {
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
+               }
  
                /*
                 * If this update is happening indirectly because of a
                                    "cannot update ref '%s': %s",
                                    update->refname, write_err);
                        free(write_err);
-                       return TRANSACTION_GENERIC_ERROR;
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
                } else {
                        update->flags |= REF_NEEDS_COMMIT;
                }
                if (close_ref(lock)) {
                        strbuf_addf(err, "couldn't close '%s.lock'",
                                    update->refname);
-                       return TRANSACTION_GENERIC_ERROR;
+                       ret = TRANSACTION_GENERIC_ERROR;
+                       goto out;
                }
        }
-       return 0;
+ out:
+       strbuf_release(&referent);
+       return ret;
  }
  
  /*
@@@ -2580,19 -3096,11 +2606,19 @@@ static int files_transaction_finish(str
                }
        }
  
 -      if (repack_without_refs(refs, &refs_to_delete, err)) {
 +      if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
  
 +      if (repack_without_refs(refs->packed_ref_store, &refs_to_delete, err)) {
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              packed_refs_unlock(refs->packed_ref_store);
 +              goto cleanup;
 +      }
 +
 +      packed_refs_unlock(refs->packed_ref_store);
 +
        /* Delete the reflogs of any references that were deleted: */
        for_each_string_list_item(ref_to_delete, &refs_to_delete) {
                strbuf_reset(&sb);
@@@ -2699,7 -3207,9 +2725,7 @@@ static int files_initial_transaction_co
                }
        }
  
 -      if (lock_packed_refs(refs, 0)) {
 -              strbuf_addf(err, "unable to lock packed-refs file: %s",
 -                          strerror(errno));
 +      if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
  
                if ((update->flags & REF_HAVE_NEW) &&
                    !is_null_oid(&update->new_oid))
 -                      add_packed_ref(refs, update->refname,
 +                      add_packed_ref(refs->packed_ref_store, update->refname,
                                       &update->new_oid);
        }
  
 -      if (commit_packed_refs(refs)) {
 -              strbuf_addf(err, "unable to commit packed-refs file: %s",
 -                          strerror(errno));
 +      if (commit_packed_refs(refs->packed_ref_store, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
  
  cleanup:
 +      packed_refs_unlock(refs->packed_ref_store);
        transaction->state = REF_TRANSACTION_CLOSED;
        string_list_clear(&affected_refnames, 0);
        return ret;