Merge branch 'mh/ref-lock-entry'
authorJunio C Hamano <gitster@pobox.com>
Sun, 27 Aug 2017 05:55:09 +0000 (22:55 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 27 Aug 2017 05:55:09 +0000 (22:55 -0700)
The code to acquire a lock on a reference (e.g. while accepting a
push from a client) used to immediately fail when the reference is
already locked---now it waits for a very short while and retries,
which can make it succeed if the lock holder was holding it during
a read-only operation.

* mh/ref-lock-entry:
refs: retry acquiring reference locks for 100ms

1  2 
Documentation/config.txt
refs.c
refs/files-backend.c
refs/refs-internal.h
diff --combined Documentation/config.txt
index 9b42e0ca73429d01f987b2b93c413ab49a98fef4,2c04b9dfb4d43e6ade316d05d93a284b02bfa65f..dc4e3f58a2d2e8a5a6753bd290d65fec4f7f7b21
@@@ -216,15 -216,15 +216,15 @@@ boolean:
         synonyms are accepted for 'true' and 'false'; these are all
         case-insensitive.
  
 -       true;; Boolean true can be spelled as `yes`, `on`, `true`,
 -              or `1`.  Also, a variable defined without `= <value>`
 +      true;; Boolean true literals are `yes`, `on`, `true`,
 +              and `1`.  Also, a variable defined without `= <value>`
                is taken as true.
  
 -       false;; Boolean false can be spelled as `no`, `off`,
 -              `false`, or `0`.
 +      false;; Boolean false literals are `no`, `off`, `false`,
 +              `0` and the empty string.
  +
  When converting value to the canonical form using `--bool` type
 -specifier; 'git config' will ensure that the output is "true" or
 +specifier, 'git config' will ensure that the output is "true" or
  "false" (spelled in lowercase).
  
  integer::
@@@ -776,6 -776,12 +776,12 @@@ core.commentChar:
  If set to "auto", `git-commit` would select a character that is not
  the beginning character of any line in existing commit messages.
  
+ core.filesRefLockTimeout::
+       The length of time, in milliseconds, to retry when trying to
+       lock an individual reference. Value 0 means not to retry at
+       all; -1 means to try indefinitely. Default is 100 (i.e.,
+       retry for 100ms).
  core.packedRefsTimeout::
        The length of time, in milliseconds, to retry when trying to
        lock the `packed-refs` file. Value 0 means not to retry at
@@@ -1077,25 -1083,14 +1083,25 @@@ This does not affect linkgit:git-format
  'git-diff-{asterisk}' plumbing commands.  Can be overridden on the
  command line with the `--color[=<when>]` option.
  
 +diff.colorMoved::
 +      If set to either a valid `<mode>` or a true value, moved lines
 +      in a diff are colored differently, for details of valid modes
 +      see '--color-moved' in linkgit:git-diff[1]. If simply set to
 +      true the default color mode will be used. When set to false,
 +      moved lines are not colored.
 +
  color.diff.<slot>::
        Use customized color for diff colorization.  `<slot>` specifies
        which part of the patch to use the specified color, and is one
        of `context` (context text - `plain` is a historical synonym),
        `meta` (metainformation), `frag`
        (hunk header), 'func' (function in hunk header), `old` (removed lines),
 -      `new` (added lines), `commit` (commit headers), or `whitespace`
 -      (highlighting whitespace errors).
 +      `new` (added lines), `commit` (commit headers), `whitespace`
 +      (highlighting whitespace errors), `oldMoved` (deleted lines),
 +      `newMoved` (added lines), `oldMovedDimmed`, `oldMovedAlternative`,
 +      `oldMovedAlternativeDimmed`, `newMovedDimmed`, `newMovedAlternative`
 +      and `newMovedAlternativeDimmed` (See the '<mode>'
 +      setting of '--color-moved' in linkgit:git-diff[1] for details).
  
  color.decorate.<slot>::
        Use customized color for 'git log --decorate' output.  `<slot>` is one
@@@ -1564,13 -1559,11 +1570,13 @@@ gc.<pattern>.reflogExpireUnreachable:
  gc.rerereResolved::
        Records of conflicted merge you resolved earlier are
        kept for this many days when 'git rerere gc' is run.
 +      You can also use more human-readable "1.month.ago", etc.
        The default is 60 days.  See linkgit:git-rerere[1].
  
  gc.rerereUnresolved::
        Records of conflicted merge you have not resolved are
        kept for this many days when 'git rerere gc' is run.
 +      You can also use more human-readable "1.month.ago", etc.
        The default is 15 days.  See linkgit:git-rerere[1].
  
  gitcvs.commitMsgAnnotation::
@@@ -2925,8 -2918,8 +2931,8 @@@ sendemail.smtpsslcertpath:
  
  sendemail.<identity>.*::
        Identity-specific versions of the 'sendemail.*' parameters
 -      found below, taking precedence over those when the this
 -      identity is selected, through command-line or
 +      found below, taking precedence over those when this
 +      identity is selected, through either the command-line or
        `sendemail.identity`.
  
  sendemail.aliasesFile::
diff --combined refs.c
index 3d549a89701ab77c973835cd7c556b5a8c6ddd6f,748e34d7c2d07cc420a50292d145e08823c21ed2..b0106b8162c7b8e3770e04c212abb05f1afbd0c3
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -174,24 -174,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)
@@@ -579,6 -561,21 +579,21 @@@ enum ref_type ref_type(const char *refn
         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)
  {
        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));
@@@ -634,8 -633,9 +651,9 @@@ static int delete_pseudoref(const char 
                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))
@@@ -836,7 -836,7 +854,7 @@@ int read_ref_at(const char *refname, un
        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);
@@@ -1178,7 -1178,7 +1196,7 @@@ int ref_is_hidden(const char *refname, 
                const char *match = hide_refs->items[i].string;
                const char *subject;
                int neg = 0;
 -              int len;
 +              const char *p;
  
                if (*match == '!') {
                        neg = 1;
                }
  
                /* 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;
diff --combined refs/files-backend.c
index 5cca55510b6a0683f16937215f6056d896c622e0,d611e0f7d7760b4e85fc6d3c9fd26cb2646eeea4..fccbc24ac4ad3631b96a4796f4b1f89913fa0ea3
@@@ -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,7 -855,9 +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;
        }
@@@ -683,9 -1003,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;
  };
@@@ -745,6 -1072,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;
  }
@@@ -786,28 -1114,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;
@@@ -865,7 -1183,9 +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;
  }
  
  /*
@@@ -939,7 -1259,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];
@@@ -1093,11 -1531,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;
  }
  
  /*
@@@ -2576,19 -3074,11 +2580,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);
@@@ -2695,7 -3185,9 +2699,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;
diff --combined refs/refs-internal.h
index 4789106fc093391f1c3e67d0fe46c8ece47fde34,9977fea98b666bae75caf019ad43545ab953e262..b02dc5a7e3500cae1784199a82b37be020004df1
   */
  #define REF_DELETED_LOOSE 0x200
  
+ /*
+  * Return the length of time to retry acquiring a loose reference lock
+  * before giving up, in milliseconds:
+  */
+ long get_files_ref_lock_timeout_ms(void);
  /*
   * Return true iff refname is minimally safe. "Safe" here means that
   * deleting a loose reference by this name will not do any damage, for
   */
  int refname_is_safe(const char *refname);
  
 +/*
 + * Helper function: 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);
 +
  enum peel_status {
        /* object was peeled successfully: */
        PEEL_PEELED = 0,
@@@ -664,7 -661,6 +670,7 @@@ struct ref_storage_be 
  };
  
  extern struct ref_storage_be refs_be_files;
 +extern struct ref_storage_be refs_be_packed;
  
  /*
   * A representation of the reference store for the main repository or