Merge branch 'mh/ref-races-optim-invalidate-cached'
authorJunio C Hamano <gitster@pobox.com>
Thu, 25 Jul 2013 02:21:02 +0000 (19:21 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 25 Jul 2013 02:21:02 +0000 (19:21 -0700)
* mh/ref-races-optim-invalidate-cached:
  refs: do not invalidate the packed-refs cache unnecessarily

1  2 
refs.c

diff --combined refs.c
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -1295,37 -1295,6 +1295,37 @@@ static struct ref_entry *get_packed_ref
        return find_ref(get_packed_refs(&ref_cache), refname);
  }
  
 +/*
 + * A loose ref file doesn't exist; check for a packed ref.  The
 + * options are forwarded from resolve_safe_unsafe().
 + */
 +static const char *handle_missing_loose_ref(const char *refname,
 +                                          unsigned char *sha1,
 +                                          int reading,
 +                                          int *flag)
 +{
 +      struct ref_entry *entry;
 +
 +      /*
 +       * The loose reference file does not exist; check for a packed
 +       * reference.
 +       */
 +      entry = get_packed_ref(refname);
 +      if (entry) {
 +              hashcpy(sha1, entry->u.value.sha1);
 +              if (flag)
 +                      *flag |= REF_ISPACKED;
 +              return refname;
 +      }
 +      /* The reference is not a packed reference, either. */
 +      if (reading) {
 +              return NULL;
 +      } else {
 +              hashclr(sha1);
 +              return refname;
 +      }
 +}
 +
  const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
  {
        int depth = MAXDEPTH;
  
                git_snpath(path, sizeof(path), "%s", refname);
  
 +              /*
 +               * We might have to loop back here to avoid a race
 +               * condition: first we lstat() the file, then we try
 +               * to read it as a link or as a file.  But if somebody
 +               * changes the type of the file (file <-> directory
 +               * <-> symlink) between the lstat() and reading, then
 +               * we don't want to report that as an error but rather
 +               * try again starting with the lstat().
 +               */
 +      stat_ref:
                if (lstat(path, &st) < 0) {
 -                      struct ref_entry *entry;
 -
 -                      if (errno != ENOENT)
 -                              return NULL;
 -                      /*
 -                       * The loose reference file does not exist;
 -                       * check for a packed reference.
 -                       */
 -                      entry = get_packed_ref(refname);
 -                      if (entry) {
 -                              hashcpy(sha1, entry->u.value.sha1);
 -                              if (flag)
 -                                      *flag |= REF_ISPACKED;
 -                              return refname;
 -                      }
 -                      /* The reference is not a packed reference, either. */
 -                      if (reading) {
 +                      if (errno == ENOENT)
 +                              return handle_missing_loose_ref(refname, sha1,
 +                                                              reading, flag);
 +                      else
                                return NULL;
 -                      } else {
 -                              hashclr(sha1);
 -                              return refname;
 -                      }
                }
  
                /* Follow "normalized" - ie "refs/.." symlinks by hand */
                if (S_ISLNK(st.st_mode)) {
                        len = readlink(path, buffer, sizeof(buffer)-1);
 -                      if (len < 0)
 -                              return NULL;
 +                      if (len < 0) {
 +                              if (errno == ENOENT || errno == EINVAL)
 +                                      /* inconsistent with lstat; retry */
 +                                      goto stat_ref;
 +                              else
 +                                      return NULL;
 +                      }
                        buffer[len] = 0;
                        if (!prefixcmp(buffer, "refs/") &&
                                        !check_refname_format(buffer, 0)) {
                 * a ref
                 */
                fd = open(path, O_RDONLY);
 -              if (fd < 0)
 -                      return NULL;
 +              if (fd < 0) {
 +                      if (errno == ENOENT)
 +                              /* inconsistent with lstat; retry */
 +                              goto stat_ref;
 +                      else
 +                              return NULL;
 +              }
                len = read_in_full(fd, buffer, sizeof(buffer)-1);
                close(fd);
                if (len < 0)
                /*
                 * Is it a symbolic ref?
                 */
 -              if (prefixcmp(buffer, "ref:"))
 -                      break;
 +              if (prefixcmp(buffer, "ref:")) {
 +                      /*
 +                       * Please note that FETCH_HEAD has a second
 +                       * line containing other data.
 +                       */
 +                      if (get_sha1_hex(buffer, sha1) ||
 +                          (buffer[40] != '\0' && !isspace(buffer[40]))) {
 +                              if (flag)
 +                                      *flag |= REF_ISBROKEN;
 +                              return NULL;
 +                      }
 +                      return refname;
 +              }
                if (flag)
                        *flag |= REF_ISSYMREF;
                buf = buffer + 4;
                }
                refname = strcpy(refname_buffer, buf);
        }
 -      /* Please note that FETCH_HEAD has a second line containing other data. */
 -      if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) {
 -              if (flag)
 -                      *flag |= REF_ISBROKEN;
 -              return NULL;
 -      }
 -      return refname;
  }
  
  char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
@@@ -2174,11 -2136,14 +2174,14 @@@ int lock_packed_refs(int flags
  {
        struct packed_ref_cache *packed_ref_cache;
  
-       /* Discard the old cache because it might be invalid: */
-       clear_packed_ref_cache(&ref_cache);
        if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0)
                return -1;
-       /* Read the current packed-refs while holding the lock: */
+       /*
+        * Get the current packed-refs while holding the lock.  If the
+        * packed-refs file has been modified since we last read it,
+        * this will automatically invalidate the cache and re-read
+        * the packed-refs file.
+        */
        packed_ref_cache = get_packed_ref_cache(&ref_cache);
        packed_ref_cache->lock = &packlock;
        /* Increment the reference count to prevent it from being freed: */