Merge branch 'mh/ref-api'
authorJunio C Hamano <gitster@pobox.com>
Tue, 20 Dec 2011 21:25:53 +0000 (13:25 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 20 Dec 2011 21:25:53 +0000 (13:25 -0800)
* mh/ref-api:
add_ref(): take a (struct ref_entry *) parameter
create_ref_entry(): extract function from add_ref()
repack_without_ref(): remove temporary
resolve_gitlink_ref_recursive(): change to work with struct ref_cache
Pass a (ref_cache *) to the resolve_gitlink_*() helper functions
resolve_gitlink_ref(): improve docstring
get_ref_dir(): change signature
refs: change signatures of get_packed_refs() and get_loose_refs()
is_dup_ref(): extract function from sort_ref_array()
add_ref(): add docstring
parse_ref_line(): add docstring
is_refname_available(): remove the "quiet" argument
clear_ref_array(): rename from free_ref_array()
refs: rename parameters result -> sha1
refs: rename "refname" variables
struct ref_entry: document name member

Conflicts:
cache.h
refs.c

1  2 
cache.h
refs.c
diff --combined cache.h
index 7d93df61c31b58826258f36c07214f58abcea1a2,e1644b103d5399826b4d34e90c622cbca1fb821a..270dfdb8aebcade3071156de8b170461d3273b5c
+++ b/cache.h
@@@ -35,7 -35,6 +35,7 @@@ int git_inflate(git_zstream *, int flus
  void git_deflate_init(git_zstream *, int level);
  void git_deflate_init_gzip(git_zstream *, int level);
  void git_deflate_end(git_zstream *);
 +int git_deflate_abort(git_zstream *);
  int git_deflate_end_gently(git_zstream *);
  int git_deflate(git_zstream *, int flush);
  unsigned long git_deflate_bound(git_zstream *, unsigned long);
@@@ -598,7 -597,6 +598,7 @@@ extern size_t packed_git_window_size
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
  extern unsigned long big_file_threshold;
 +extern unsigned long pack_size_limit_cfg;
  extern int read_replace_refs;
  extern int fsync_object_files;
  extern int core_preload_index;
@@@ -833,9 -831,9 +833,9 @@@ static inline int get_sha1_with_context
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
- extern int read_ref_full(const char *filename, unsigned char *sha1,
+ extern int read_ref_full(const char *refname, unsigned char *sha1,
                         int reading, int *flags);
- extern int read_ref(const char *filename, unsigned char *sha1);
+ extern int read_ref(const char *refname, unsigned char *sha1);
  
  /*
   * Resolve a reference, recursively following symbolic refererences.
   *
   * errno is sometimes set on errors, but not always.
   */
 -extern const char *resolve_ref(const char *refname, unsigned char *sha1, int reading, int *flag);
 +extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
 +extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
  
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
@@@ -1032,11 -1029,12 +1032,11 @@@ extern char *git_getpass(const char *pr
  extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
  extern int finish_connect(struct child_process *conn);
  extern int git_connection_is_socket(struct child_process *conn);
 -extern int path_match(const char *path, int nr, char **match);
  struct extra_have_objects {
        int nr, alloc;
        unsigned char (*array)[20];
  };
 -extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
 +extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
  extern int server_supports(const char *feature);
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
diff --combined refs.c
index 4bf45b1979f7f975bfb6969c3cd1009c79eaeff2,579e4c3a18ddc093aac522477b41af032514d2dc..6f436f1cb05e62c6afda086f5e410a268cf8bb52
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -11,6 -11,7 +11,7 @@@ struct ref_entry 
        unsigned char flag; /* ISSYMREF? ISPACKED? */
        unsigned char sha1[20];
        unsigned char peeled[20];
+       /* The full name of the reference (e.g., "refs/heads/master"): */
        char name[FLEX_ARRAY];
  };
  
@@@ -19,6 -20,11 +20,11 @@@ struct ref_array 
        struct ref_entry **refs;
  };
  
+ /*
+  * 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(char *line, unsigned char *sha1)
  {
        /*
        return line;
  }
  
- static void add_ref(const char *name, const unsigned char *sha1,
-                   int flag, int check_name, struct ref_array *refs,
-                   struct ref_entry **new_entry)
+ static struct ref_entry *create_ref_entry(const char *refname,
+                                         const unsigned char *sha1, int flag,
+                                         int check_name)
  {
        int len;
-       struct ref_entry *entry;
+       struct ref_entry *ref;
  
-       /* Allocate it and add it in.. */
-       len = strlen(name) + 1;
-       entry = xmalloc(sizeof(struct ref_entry) + len);
-       hashcpy(entry->sha1, sha1);
-       hashclr(entry->peeled);
        if (check_name &&
-           check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
-               die("Reference has invalid format: '%s'", name);
-       memcpy(entry->name, name, len);
-       entry->flag = flag;
-       if (new_entry)
-               *new_entry = entry;
+           check_refname_format(refname, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
+               die("Reference has invalid format: '%s'", refname);
+       len = strlen(refname) + 1;
+       ref = xmalloc(sizeof(struct ref_entry) + len);
+       hashcpy(ref->sha1, sha1);
+       hashclr(ref->peeled);
+       memcpy(ref->name, refname, len);
+       ref->flag = flag;
+       return ref;
+ }
+ /* Add a ref_entry to the end of the ref_array (unsorted). */
+ static void add_ref(struct ref_array *refs, struct ref_entry *ref)
+ {
        ALLOC_GROW(refs->refs, refs->nr + 1, refs->alloc);
-       refs->refs[refs->nr++] = entry;
+       refs->refs[refs->nr++] = ref;
  }
  
  static int ref_entry_cmp(const void *a, const void *b)
        return strcmp(one->name, two->name);
  }
  
+ /*
+  * Emit a warning and return true iff ref1 and ref2 have the same name
+  * and the same sha1.  Die if they have the same name but different
+  * sha1s.
+  */
+ static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2)
+ {
+       if (!strcmp(ref1->name, ref2->name)) {
+               /* Duplicate name; make sure that the SHA1s match: */
+               if (hashcmp(ref1->sha1, ref2->sha1))
+                       die("Duplicated ref, and SHA1s don't match: %s",
+                           ref1->name);
+               warning("Duplicated ref: %s", ref1->name);
+               return 1;
+       } else {
+               return 0;
+       }
+ }
  static void sort_ref_array(struct ref_array *array)
  {
-       int i = 0, j = 1;
+       int i, j;
  
        /* Nothing to sort unless there are at least two entries */
        if (array->nr < 2)
        qsort(array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);
  
        /* Remove any duplicates from the ref_array */
-       for (; j < array->nr; j++) {
-               struct ref_entry *a = array->refs[i];
-               struct ref_entry *b = array->refs[j];
-               if (!strcmp(a->name, b->name)) {
-                       if (hashcmp(a->sha1, b->sha1))
-                               die("Duplicated ref, and SHA1s don't match: %s",
-                                   a->name);
-                       warning("Duplicated ref: %s", a->name);
-                       free(b);
+       i = 0;
+       for (j = 1; j < array->nr; j++) {
+               if (is_dup_ref(array->refs[i], array->refs[j])) {
+                       free(array->refs[j]);
                        continue;
                }
-               i++;
-               array->refs[i] = array->refs[j];
+               array->refs[++i] = array->refs[j];
        }
        array->nr = i + 1;
  }
  
- static struct ref_entry *search_ref_array(struct ref_array *array, const char *name)
+ static struct ref_entry *search_ref_array(struct ref_array *array, const char *refname)
  {
        struct ref_entry *e, **r;
        int len;
  
-       if (name == NULL)
+       if (refname == NULL)
                return NULL;
  
        if (!array->nr)
                return NULL;
  
-       len = strlen(name) + 1;
+       len = strlen(refname) + 1;
        e = xmalloc(sizeof(struct ref_entry) + len);
-       memcpy(e->name, name, len);
+       memcpy(e->name, refname, len);
  
        r = bsearch(&e, array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);
  
@@@ -148,7 -170,7 +170,7 @@@ static struct ref_entry *current_ref
  
  static struct ref_array extra_refs;
  
- static void free_ref_array(struct ref_array *array)
+ static void clear_ref_array(struct ref_array *array)
  {
        int i;
        for (i = 0; i < array->nr; i++)
  static void clear_packed_ref_cache(struct ref_cache *refs)
  {
        if (refs->did_packed)
-               free_ref_array(&refs->packed);
+               clear_ref_array(&refs->packed);
        refs->did_packed = 0;
  }
  
  static void clear_loose_ref_cache(struct ref_cache *refs)
  {
        if (refs->did_loose)
-               free_ref_array(&refs->loose);
+               clear_ref_array(&refs->loose);
        refs->did_loose = 0;
  }
  
@@@ -222,7 -244,7 +244,7 @@@ static void read_packed_refs(FILE *f, s
  
        while (fgets(refline, sizeof(refline), f)) {
                unsigned char sha1[20];
-               const char *name;
+               const char *refname;
                static const char header[] = "# pack-refs with:";
  
                if (!strncmp(refline, header, sizeof(header)-1)) {
                        continue;
                }
  
-               name = parse_ref_line(refline, sha1);
-               if (name) {
-                       add_ref(name, sha1, flag, 1, array, &last);
+               refname = parse_ref_line(refline, sha1);
+               if (refname) {
+                       last = create_ref_entry(refname, sha1, flag, 1);
+                       add_ref(array, last);
                        continue;
                }
                if (last &&
        sort_ref_array(array);
  }
  
- void add_extra_ref(const char *name, const unsigned char *sha1, int flag)
+ void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)
  {
-       add_ref(name, sha1, flag, 0, &extra_refs, NULL);
+       add_ref(&extra_refs, create_ref_entry(refname, sha1, flag, 0));
  }
  
  void clear_extra_refs(void)
  {
-       free_ref_array(&extra_refs);
+       clear_ref_array(&extra_refs);
  }
  
- static struct ref_array *get_packed_refs(const char *submodule)
+ static struct ref_array *get_packed_refs(struct ref_cache *refs)
  {
-       struct ref_cache *refs = get_ref_cache(submodule);
        if (!refs->did_packed) {
                const char *packed_refs_file;
                FILE *f;
  
-               if (submodule)
-                       packed_refs_file = git_path_submodule(submodule, "packed-refs");
+               if (*refs->name)
+                       packed_refs_file = git_path_submodule(refs->name, "packed-refs");
                else
                        packed_refs_file = git_path("packed-refs");
                f = fopen(packed_refs_file, "r");
        return &refs->packed;
  }
  
- static void get_ref_dir(const char *submodule, const char *base,
+ static void get_ref_dir(struct ref_cache *refs, const char *base,
                        struct ref_array *array)
  {
        DIR *dir;
        const char *path;
  
-       if (submodule)
-               path = git_path_submodule(submodule, "%s", base);
+       if (*refs->name)
+               path = git_path_submodule(refs->name, "%s", base);
        else
                path = git_path("%s", base);
  
        if (dir) {
                struct dirent *de;
                int baselen = strlen(base);
-               char *ref = xmalloc(baselen + 257);
+               char *refname = xmalloc(baselen + 257);
  
-               memcpy(ref, base, baselen);
+               memcpy(refname, base, baselen);
                if (baselen && base[baselen-1] != '/')
-                       ref[baselen++] = '/';
+                       refname[baselen++] = '/';
  
                while ((de = readdir(dir)) != NULL) {
                        unsigned char sha1[20];
                                continue;
                        if (has_extension(de->d_name, ".lock"))
                                continue;
-                       memcpy(ref + baselen, de->d_name, namelen+1);
-                       refdir = submodule
-                               ? git_path_submodule(submodule, "%s", ref)
-                               : git_path("%s", ref);
+                       memcpy(refname + baselen, de->d_name, namelen+1);
+                       refdir = *refs->name
+                               ? git_path_submodule(refs->name, "%s", refname)
+                               : git_path("%s", refname);
                        if (stat(refdir, &st) < 0)
                                continue;
                        if (S_ISDIR(st.st_mode)) {
-                               get_ref_dir(submodule, ref, array);
+                               get_ref_dir(refs, refname, array);
                                continue;
                        }
-                       if (submodule) {
+                       if (*refs->name) {
                                hashclr(sha1);
                                flag = 0;
-                               if (resolve_gitlink_ref(submodule, ref, sha1) < 0) {
+                               if (resolve_gitlink_ref(refs->name, refname, sha1) < 0) {
                                        hashclr(sha1);
                                        flag |= REF_ISBROKEN;
                                }
-                       } else if (read_ref_full(ref, sha1, 1, &flag)) {
+                       } else if (read_ref_full(refname, sha1, 1, &flag)) {
                                hashclr(sha1);
                                flag |= REF_ISBROKEN;
                        }
-                       add_ref(ref, sha1, flag, 1, array, NULL);
+                       add_ref(array, create_ref_entry(refname, sha1, flag, 1));
                }
-               free(ref);
+               free(refname);
                closedir(dir);
        }
  }
@@@ -361,7 -382,7 +382,7 @@@ static int warn_if_dangling_symref(cons
        if (!(flags & REF_ISSYMREF))
                return 0;
  
 -      resolves_to = resolve_ref(refname, junk, 0, NULL);
 +      resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
        if (!resolves_to || strcmp(resolves_to, d->refname))
                return 0;
  
@@@ -379,12 -400,10 +400,10 @@@ void warn_dangling_symref(FILE *fp, con
        for_each_rawref(warn_if_dangling_symref, &data);
  }
  
- static struct ref_array *get_loose_refs(const char *submodule)
+ static struct ref_array *get_loose_refs(struct ref_cache *refs)
  {
-       struct ref_cache *refs = get_ref_cache(submodule);
        if (!refs->did_loose) {
-               get_ref_dir(submodule, "refs", &refs->loose);
+               get_ref_dir(refs, "refs", &refs->loose);
                sort_ref_array(&refs->loose);
                refs->did_loose = 1;
        }
  
  /*
   * Called by resolve_gitlink_ref_recursive() after it failed to read
-  * from "name", which is "module/.git/<refname>". Find <refname> in
-  * the packed-refs file for the submodule.
+  * from the loose refs in ref_cache refs. Find <refname> in the
+  * packed-refs file for the submodule.
   */
- static int resolve_gitlink_packed_ref(char *name, int pathlen, const char *refname, unsigned char *result)
+ static int resolve_gitlink_packed_ref(struct ref_cache *refs,
+                                     const char *refname, unsigned char *sha1)
  {
-       int retval = -1;
        struct ref_entry *ref;
-       struct ref_array *array;
+       struct ref_array *array = get_packed_refs(refs);
  
-       /* being defensive: resolve_gitlink_ref() did this for us */
-       if (pathlen < 6 || memcmp(name + pathlen - 6, "/.git/", 6))
-               die("Oops");
-       name[pathlen - 6] = '\0'; /* make it path to the submodule */
-       array = get_packed_refs(name);
        ref = search_ref_array(array, refname);
-       if (ref != NULL) {
-               memcpy(result, ref->sha1, 20);
-               retval = 0;
-       }
-       return retval;
+       if (ref == NULL)
+               return -1;
+       memcpy(sha1, ref->sha1, 20);
+       return 0;
  }
  
- static int resolve_gitlink_ref_recursive(char *name, int pathlen, const char *refname, unsigned char *result, int recursion)
+ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
+                                        const char *refname, unsigned char *sha1,
+                                        int recursion)
  {
-       int fd, len = strlen(refname);
+       int fd, len;
        char buffer[128], *p;
+       char *path;
  
-       if (recursion > MAXDEPTH || len > MAXREFLEN)
+       if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
                return -1;
-       memcpy(name + pathlen, refname, len+1);
-       fd = open(name, O_RDONLY);
+       path = *refs->name
+               ? git_path_submodule(refs->name, "%s", refname)
+               : git_path("%s", refname);
+       fd = open(path, O_RDONLY);
        if (fd < 0)
-               return resolve_gitlink_packed_ref(name, pathlen, refname, result);
+               return resolve_gitlink_packed_ref(refs, refname, sha1);
  
        len = read(fd, buffer, sizeof(buffer)-1);
        close(fd);
        buffer[len] = 0;
  
        /* Was it a detached head or an old-fashioned symlink? */
-       if (!get_sha1_hex(buffer, result))
+       if (!get_sha1_hex(buffer, sha1))
                return 0;
  
        /* Symref? */
        while (isspace(*p))
                p++;
  
-       return resolve_gitlink_ref_recursive(name, pathlen, p, result, recursion+1);
+       return resolve_gitlink_ref_recursive(refs, p, sha1, recursion+1);
  }
  
- int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *result)
+ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
  {
        int len = strlen(path), retval;
-       char *gitdir;
-       const char *tmp;
+       char *submodule;
+       struct ref_cache *refs;
  
        while (len && path[len-1] == '/')
                len--;
        if (!len)
                return -1;
-       gitdir = xmalloc(len + MAXREFLEN + 8);
-       memcpy(gitdir, path, len);
-       memcpy(gitdir + len, "/.git", 6);
-       len += 5;
-       tmp = read_gitfile(gitdir);
-       if (tmp) {
-               free(gitdir);
-               len = strlen(tmp);
-               gitdir = xmalloc(len + MAXREFLEN + 3);
-               memcpy(gitdir, tmp, len);
-       }
-       gitdir[len] = '/';
-       gitdir[++len] = '\0';
-       retval = resolve_gitlink_ref_recursive(gitdir, len, refname, result, 0);
-       free(gitdir);
+       submodule = xstrndup(path, len);
+       refs = get_ref_cache(submodule);
+       free(submodule);
+       retval = resolve_gitlink_ref_recursive(refs, refname, sha1, 0);
        return retval;
  }
  
   * Try to read ref from the packed references.  On success, set sha1
   * and return 0; otherwise, return -1.
   */
- static int get_packed_ref(const char *ref, unsigned char *sha1)
+ static int get_packed_ref(const char *refname, unsigned char *sha1)
  {
-       struct ref_array *packed = get_packed_refs(NULL);
-       struct ref_entry *entry = search_ref_array(packed, ref);
+       struct ref_array *packed = get_packed_refs(get_ref_cache(NULL));
+       struct ref_entry *entry = search_ref_array(packed, refname);
        if (entry) {
                hashcpy(sha1, entry->sha1);
                return 0;
        return -1;
  }
  
- const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag)
 -const char *resolve_ref(const char *refname, unsigned char *sha1, int reading, int *flag)
++const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
  {
        int depth = MAXDEPTH;
        ssize_t len;
        char buffer[256];
-       static char ref_buffer[256];
+       static char refname_buffer[256];
  
        if (flag)
                *flag = 0;
  
-       if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))
+       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
                return NULL;
  
        for (;;) {
                if (--depth < 0)
                        return NULL;
  
-               git_snpath(path, sizeof(path), "%s", ref);
+               git_snpath(path, sizeof(path), "%s", refname);
  
                if (lstat(path, &st) < 0) {
                        if (errno != ENOENT)
                         * The loose reference file does not exist;
                         * check for a packed reference.
                         */
-                       if (!get_packed_ref(ref, sha1)) {
+                       if (!get_packed_ref(refname, sha1)) {
                                if (flag)
                                        *flag |= REF_ISPACKED;
-                               return ref;
+                               return refname;
                        }
                        /* The reference is not a packed reference, either. */
                        if (reading) {
                                return NULL;
                        } else {
                                hashclr(sha1);
-                               return ref;
+                               return refname;
                        }
                }
  
                        buffer[len] = 0;
                        if (!prefixcmp(buffer, "refs/") &&
                                        !check_refname_format(buffer, 0)) {
-                               strcpy(ref_buffer, buffer);
-                               ref = ref_buffer;
+                               strcpy(refname_buffer, buffer);
+                               refname = refname_buffer;
                                if (flag)
                                        *flag |= REF_ISSYMREF;
                                continue;
                                *flag |= REF_ISBROKEN;
                        return NULL;
                }
-               ref = strcpy(ref_buffer, buf);
+               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]))) {
                        *flag |= REF_ISBROKEN;
                return NULL;
        }
-       return ref;
+       return refname;
  }
  
 +char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
 +{
 +      const char *ret = resolve_ref_unsafe(ref, sha1, reading, flag);
 +      return ret ? xstrdup(ret) : NULL;
 +}
 +
  /* The argument to filter_refs */
  struct ref_filter {
        const char *pattern;
        void *cb_data;
  };
  
- int read_ref_full(const char *ref, unsigned char *sha1, int reading, int *flags)
+ int read_ref_full(const char *refname, unsigned char *sha1, int reading, int *flags)
  {
-       if (resolve_ref_unsafe(ref, sha1, reading, flags))
 -      if (resolve_ref(refname, sha1, reading, flags))
++      if (resolve_ref_unsafe(refname, sha1, reading, flags))
                return 0;
        return -1;
  }
  
- int read_ref(const char *ref, unsigned char *sha1)
+ int read_ref(const char *refname, unsigned char *sha1)
  {
-       return read_ref_full(ref, sha1, 1, NULL);
+       return read_ref_full(refname, sha1, 1, NULL);
  }
  
  #define DO_FOR_EACH_INCLUDE_BROKEN 01
@@@ -649,23 -651,23 +657,23 @@@ static int do_one_ref(const char *base
        return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
  }
  
- static int filter_refs(const char *ref, const unsigned char *sha, int flags,
-       void *data)
+ static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
+                      void *data)
  {
        struct ref_filter *filter = (struct ref_filter *)data;
-       if (fnmatch(filter->pattern, ref, 0))
+       if (fnmatch(filter->pattern, refname, 0))
                return 0;
-       return filter->fn(ref, sha, flags, filter->cb_data);
+       return filter->fn(refname, sha1, flags, filter->cb_data);
  }
  
- int peel_ref(const char *ref, unsigned char *sha1)
+ int peel_ref(const char *refname, unsigned char *sha1)
  {
        int flag;
        unsigned char base[20];
        struct object *o;
  
-       if (current_ref && (current_ref->name == ref
-               || !strcmp(current_ref->name, ref))) {
+       if (current_ref && (current_ref->name == refname
+               || !strcmp(current_ref->name, refname))) {
                if (current_ref->flag & REF_KNOWS_PEELED) {
                        hashcpy(sha1, current_ref->peeled);
                        return 0;
                goto fallback;
        }
  
-       if (read_ref_full(ref, base, 1, &flag))
+       if (read_ref_full(refname, base, 1, &flag))
                return -1;
  
        if ((flag & REF_ISPACKED)) {
-               struct ref_array *array = get_packed_refs(NULL);
-               struct ref_entry *r = search_ref_array(array, ref);
+               struct ref_array *array = get_packed_refs(get_ref_cache(NULL));
+               struct ref_entry *r = search_ref_array(array, refname);
  
                if (r != NULL && r->flag & REF_KNOWS_PEELED) {
                        hashcpy(sha1, r->peeled);
  fallback:
        o = parse_object(base);
        if (o && o->type == OBJ_TAG) {
-               o = deref_tag(o, ref, 0);
+               o = deref_tag(o, refname, 0);
                if (o) {
                        hashcpy(sha1, o->sha1);
                        return 0;
@@@ -703,8 -705,9 +711,9 @@@ static int do_for_each_ref(const char *
                           int trim, int flags, void *cb_data)
  {
        int retval = 0, i, p = 0, l = 0;
-       struct ref_array *packed = get_packed_refs(submodule);
-       struct ref_array *loose = get_loose_refs(submodule);
+       struct ref_cache *refs = get_ref_cache(submodule);
+       struct ref_array *packed = get_packed_refs(refs);
+       struct ref_array *loose = get_loose_refs(refs);
  
        struct ref_array *extra = &extra_refs;
  
@@@ -920,16 -923,16 +929,16 @@@ static inline int bad_ref_char(int ch
  }
  
  /*
-  * Try to read one refname component from the front of ref.  Return
+  * Try to read one refname component from the front of refname.  Return
   * the length of the component found, or -1 if the component is not
   * legal.
   */
- static int check_refname_component(const char *ref, int flags)
+ static int check_refname_component(const char *refname, int flags)
  {
        const char *cp;
        char last = '\0';
  
-       for (cp = ref; ; cp++) {
+       for (cp = refname; ; cp++) {
                char ch = *cp;
                if (ch == '\0' || ch == '/')
                        break;
                        return -1; /* Refname contains "@{". */
                last = ch;
        }
-       if (cp == ref)
+       if (cp == refname)
                return -1; /* Component has zero length. */
-       if (ref[0] == '.') {
+       if (refname[0] == '.') {
                if (!(flags & REFNAME_DOT_COMPONENT))
                        return -1; /* Component starts with '.'. */
                /*
                 * Even if leading dots are allowed, don't allow "."
                 * as a component (".." is prevented by a rule above).
                 */
-               if (ref[1] == '\0')
+               if (refname[1] == '\0')
                        return -1; /* Component equals ".". */
        }
-       if (cp - ref >= 5 && !memcmp(cp - 5, ".lock", 5))
+       if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
                return -1; /* Refname ends with ".lock". */
-       return cp - ref;
+       return cp - refname;
  }
  
- int check_refname_format(const char *ref, int flags)
+ int check_refname_format(const char *refname, int flags)
  {
        int component_len, component_count = 0;
  
        while (1) {
                /* We are at the start of a path component. */
-               component_len = check_refname_component(ref, flags);
+               component_len = check_refname_component(refname, flags);
                if (component_len < 0) {
                        if ((flags & REFNAME_REFSPEC_PATTERN) &&
-                                       ref[0] == '*' &&
-                                       (ref[1] == '\0' || ref[1] == '/')) {
+                                       refname[0] == '*' &&
+                                       (refname[1] == '\0' || refname[1] == '/')) {
                                /* Accept one wildcard as a full refname component. */
                                flags &= ~REFNAME_REFSPEC_PATTERN;
                                component_len = 1;
                        }
                }
                component_count++;
-               if (ref[component_len] == '\0')
+               if (refname[component_len] == '\0')
                        break;
                /* Skip to next component. */
-               ref += component_len + 1;
+               refname += component_len + 1;
        }
  
-       if (ref[component_len - 1] == '.')
+       if (refname[component_len - 1] == '.')
                return -1; /* Refname ends with '.'. */
        if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)
                return -1; /* Refname has only one component. */
@@@ -1059,22 -1062,28 +1068,28 @@@ static int remove_empty_directories(con
        return result;
  }
  
- static int is_refname_available(const char *ref, const char *oldref,
-                               struct ref_array *array, int quiet)
+ /*
+  * Return true iff a reference named refname could be created without
+  * conflicting with the name of an existing reference.  If oldrefname
+  * is non-NULL, ignore potential conflicts with oldrefname (e.g.,
+  * because oldrefname is scheduled for deletion in the same
+  * operation).
+  */
+ static int is_refname_available(const char *refname, const char *oldrefname,
+                               struct ref_array *array)
  {
-       int i, namlen = strlen(ref); /* e.g. 'foo/bar' */
+       int i, namlen = strlen(refname); /* e.g. 'foo/bar' */
        for (i = 0; i < array->nr; i++ ) {
                struct ref_entry *entry = array->refs[i];
                /* entry->name could be 'foo' or 'foo/bar/baz' */
-               if (!oldref || strcmp(oldref, entry->name)) {
+               if (!oldrefname || strcmp(oldrefname, entry->name)) {
                        int len = strlen(entry->name);
                        int cmplen = (namlen < len) ? namlen : len;
-                       const char *lead = (namlen < len) ? entry->name : ref;
-                       if (!strncmp(ref, entry->name, cmplen) &&
+                       const char *lead = (namlen < len) ? entry->name : refname;
+                       if (!strncmp(refname, entry->name, cmplen) &&
                            lead[cmplen] == '/') {
-                               if (!quiet)
-                                       error("'%s' exists; cannot create '%s'",
-                                             entry->name, ref);
+                               error("'%s' exists; cannot create '%s'",
+                                     entry->name, refname);
                                return 0;
                        }
                }
@@@ -1117,7 -1126,7 +1132,7 @@@ int dwim_ref(const char *str, int len, 
  
                this_result = refs_found ? sha1_from_ref : sha1;
                mksnpath(fullref, sizeof(fullref), *p, len, str);
 -              r = resolve_ref(fullref, this_result, 1, &flag);
 +              r = resolve_ref_unsafe(fullref, this_result, 1, &flag);
                if (r) {
                        if (!refs_found++)
                                *ref = xstrdup(r);
@@@ -1147,7 -1156,7 +1162,7 @@@ int dwim_log(const char *str, int len, 
                const char *ref, *it;
  
                mksnpath(path, sizeof(path), *p, len, str);
 -              ref = resolve_ref(path, hash, 1, NULL);
 +              ref = resolve_ref_unsafe(path, hash, 1, NULL);
                if (!ref)
                        continue;
                if (!stat(git_path("logs/%s", path), &st) &&
        return logs_found;
  }
  
- static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int flags, int *type_p)
+ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
+                                           const unsigned char *old_sha1,
+                                           int flags, int *type_p)
  {
        char *ref_file;
-       const char *orig_ref = ref;
+       const char *orig_refname = refname;
        struct ref_lock *lock;
        int last_errno = 0;
        int type, lflags;
        lock = xcalloc(1, sizeof(struct ref_lock));
        lock->lock_fd = -1;
  
-       ref = resolve_ref_unsafe(ref, lock->old_sha1, mustexist, &type);
-       if (!ref && errno == EISDIR) {
 -      refname = resolve_ref(refname, lock->old_sha1, mustexist, &type);
++      refname = resolve_ref_unsafe(refname, lock->old_sha1, mustexist, &type);
+       if (!refname && errno == EISDIR) {
                /* we are trying to lock foo but we used to
                 * have foo/bar which now does not exist;
                 * it is normal for the empty directory 'foo'
                 * to remain.
                 */
-               ref_file = git_path("%s", orig_ref);
+               ref_file = git_path("%s", orig_refname);
                if (remove_empty_directories(ref_file)) {
                        last_errno = errno;
-                       error("there are still refs under '%s'", orig_ref);
+                       error("there are still refs under '%s'", orig_refname);
                        goto error_return;
                }
-               ref = resolve_ref_unsafe(orig_ref, lock->old_sha1, mustexist, &type);
 -              refname = resolve_ref(orig_refname, lock->old_sha1, mustexist, &type);
++              refname = resolve_ref_unsafe(orig_refname, lock->old_sha1, mustexist, &type);
        }
        if (type_p)
            *type_p = type;
-       if (!ref) {
+       if (!refname) {
                last_errno = errno;
                error("unable to resolve reference %s: %s",
-                       orig_ref, strerror(errno));
+                       orig_refname, strerror(errno));
                goto error_return;
        }
        missing = is_null_sha1(lock->old_sha1);
         * name is a proper prefix of our refname.
         */
        if (missing &&
-            !is_refname_available(ref, NULL, get_packed_refs(NULL), 0)) {
+            !is_refname_available(refname, NULL, get_packed_refs(get_ref_cache(NULL)))) {
                last_errno = ENOTDIR;
                goto error_return;
        }
  
        lflags = LOCK_DIE_ON_ERROR;
        if (flags & REF_NODEREF) {
-               ref = orig_ref;
+               refname = orig_refname;
                lflags |= LOCK_NODEREF;
        }
-       lock->ref_name = xstrdup(ref);
-       lock->orig_ref_name = xstrdup(orig_ref);
-       ref_file = git_path("%s", ref);
+       lock->ref_name = xstrdup(refname);
+       lock->orig_ref_name = xstrdup(orig_refname);
+       ref_file = git_path("%s", refname);
        if (missing)
                lock->force_write = 1;
        if ((flags & REF_NODEREF) && (type & REF_ISSYMREF))
        return NULL;
  }
  
- struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
+ struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1)
  {
        char refpath[PATH_MAX];
-       if (check_refname_format(ref, 0))
+       if (check_refname_format(refname, 0))
                return NULL;
-       strcpy(refpath, mkpath("refs/%s", ref));
+       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 *ref, const unsigned char *old_sha1, int flags)
+ struct ref_lock *lock_any_ref_for_update(const char *refname,
+                                        const unsigned char *old_sha1, int flags)
  {
-       if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))
+       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
                return NULL;
-       return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
+       return lock_ref_sha1_basic(refname, old_sha1, flags, NULL);
  }
  
  static struct lock_file packlock;
  static int repack_without_ref(const char *refname)
  {
        struct ref_array *packed;
-       struct ref_entry *ref;
        int fd, i;
  
-       packed = get_packed_refs(NULL);
-       ref = search_ref_array(packed, refname);
-       if (ref == NULL)
+       packed = get_packed_refs(get_ref_cache(NULL));
+       if (search_ref_array(packed, refname) == NULL)
                return 0;
        fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
        if (fd < 0) {
        for (i = 0; i < packed->nr; i++) {
                char line[PATH_MAX + 100];
                int len;
-               ref = packed->refs[i];
+               struct ref_entry *ref = packed->refs[i];
  
                if (!strcmp(refname, ref->name))
                        continue;
@@@ -1347,97 -1356,98 +1362,98 @@@ int delete_ref(const char *refname, con
   */
  #define TMP_RENAMED_LOG  "logs/refs/.tmp-renamed-log"
  
- int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+ int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
  {
        unsigned char sha1[20], orig_sha1[20];
        int flag = 0, logmoved = 0;
        struct ref_lock *lock;
        struct stat loginfo;
-       int log = !lstat(git_path("logs/%s", oldref), &loginfo);
+       int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
        const char *symref = NULL;
+       struct ref_cache *refs = get_ref_cache(NULL);
  
        if (log && S_ISLNK(loginfo.st_mode))
-               return error("reflog for %s is a symlink", oldref);
+               return error("reflog for %s is a symlink", oldrefname);
  
-       symref = resolve_ref_unsafe(oldref, orig_sha1, 1, &flag);
 -      symref = resolve_ref(oldrefname, orig_sha1, 1, &flag);
++      symref = resolve_ref_unsafe(oldrefname, orig_sha1, 1, &flag);
        if (flag & REF_ISSYMREF)
                return error("refname %s is a symbolic ref, renaming it is not supported",
-                       oldref);
+                       oldrefname);
        if (!symref)
-               return error("refname %s not found", oldref);
+               return error("refname %s not found", oldrefname);
  
-       if (!is_refname_available(newref, oldref, get_packed_refs(NULL), 0))
+       if (!is_refname_available(newrefname, oldrefname, get_packed_refs(refs)))
                return 1;
  
-       if (!is_refname_available(newref, oldref, get_loose_refs(NULL), 0))
+       if (!is_refname_available(newrefname, oldrefname, get_loose_refs(refs)))
                return 1;
  
-       if (log && rename(git_path("logs/%s", oldref), git_path(TMP_RENAMED_LOG)))
+       if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
                return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
-                       oldref, strerror(errno));
+                       oldrefname, strerror(errno));
  
-       if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
-               error("unable to delete old %s", oldref);
+       if (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {
+               error("unable to delete old %s", oldrefname);
                goto rollback;
        }
  
-       if (!read_ref_full(newref, sha1, 1, &flag) &&
-           delete_ref(newref, sha1, REF_NODEREF)) {
+       if (!read_ref_full(newrefname, sha1, 1, &flag) &&
+           delete_ref(newrefname, sha1, REF_NODEREF)) {
                if (errno==EISDIR) {
-                       if (remove_empty_directories(git_path("%s", newref))) {
-                               error("Directory not empty: %s", newref);
+                       if (remove_empty_directories(git_path("%s", newrefname))) {
+                               error("Directory not empty: %s", newrefname);
                                goto rollback;
                        }
                } else {
-                       error("unable to delete existing %s", newref);
+                       error("unable to delete existing %s", newrefname);
                        goto rollback;
                }
        }
  
-       if (log && safe_create_leading_directories(git_path("logs/%s", newref))) {
-               error("unable to create directory for %s", newref);
+       if (log && safe_create_leading_directories(git_path("logs/%s", newrefname))) {
+               error("unable to create directory for %s", newrefname);
                goto rollback;
        }
  
   retry:
-       if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newref))) {
+       if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newrefname))) {
                if (errno==EISDIR || errno==ENOTDIR) {
                        /*
                         * rename(a, b) when b is an existing
                         * directory ought to result in ISDIR, but
                         * Solaris 5.8 gives ENOTDIR.  Sheesh.
                         */
-                       if (remove_empty_directories(git_path("logs/%s", newref))) {
-                               error("Directory not empty: logs/%s", newref);
+                       if (remove_empty_directories(git_path("logs/%s", newrefname))) {
+                               error("Directory not empty: logs/%s", newrefname);
                                goto rollback;
                        }
                        goto retry;
                } else {
                        error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
-                               newref, strerror(errno));
+                               newrefname, strerror(errno));
                        goto rollback;
                }
        }
        logmoved = log;
  
-       lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
+       lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
        if (!lock) {
-               error("unable to lock %s for update", newref);
+               error("unable to lock %s for update", newrefname);
                goto rollback;
        }
        lock->force_write = 1;
        hashcpy(lock->old_sha1, orig_sha1);
        if (write_ref_sha1(lock, orig_sha1, logmsg)) {
-               error("unable to write current sha1 into %s", newref);
+               error("unable to write current sha1 into %s", newrefname);
                goto rollback;
        }
  
        return 0;
  
   rollback:
-       lock = lock_ref_sha1_basic(oldref, NULL, 0, NULL);
+       lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
        if (!lock) {
-               error("unable to lock %s for rollback", oldref);
+               error("unable to lock %s for rollback", oldrefname);
                goto rollbacklog;
        }
  
        flag = log_all_ref_updates;
        log_all_ref_updates = 0;
        if (write_ref_sha1(lock, orig_sha1, NULL))
-               error("unable to write current sha1 into %s", oldref);
+               error("unable to write current sha1 into %s", oldrefname);
        log_all_ref_updates = flag;
  
   rollbacklog:
-       if (logmoved && rename(git_path("logs/%s", newref), git_path("logs/%s", oldref)))
+       if (logmoved && rename(git_path("logs/%s", newrefname), git_path("logs/%s", oldrefname)))
                error("unable to restore logfile %s from %s: %s",
-                       oldref, newref, strerror(errno));
+                       oldrefname, newrefname, strerror(errno));
        if (!logmoved && log &&
-           rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldref)))
+           rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldrefname)))
                error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s",
-                       oldref, strerror(errno));
+                       oldrefname, strerror(errno));
  
        return 1;
  }
@@@ -1512,16 -1522,16 +1528,16 @@@ static int copy_msg(char *buf, const ch
        return cp - buf;
  }
  
- int log_ref_setup(const char *ref_name, char *logfile, int bufsize)
+ int log_ref_setup(const char *refname, char *logfile, int bufsize)
  {
        int logfd, oflags = O_APPEND | O_WRONLY;
  
-       git_snpath(logfile, bufsize, "logs/%s", ref_name);
+       git_snpath(logfile, bufsize, "logs/%s", refname);
        if (log_all_ref_updates &&
-           (!prefixcmp(ref_name, "refs/heads/") ||
-            !prefixcmp(ref_name, "refs/remotes/") ||
-            !prefixcmp(ref_name, "refs/notes/") ||
-            !strcmp(ref_name, "HEAD"))) {
+           (!prefixcmp(refname, "refs/heads/") ||
+            !prefixcmp(refname, "refs/remotes/") ||
+            !prefixcmp(refname, "refs/notes/") ||
+            !strcmp(refname, "HEAD"))) {
                if (safe_create_leading_directories(logfile) < 0)
                        return error("unable to create directory for %s",
                                     logfile);
        return 0;
  }
  
- static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
+ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
                         const unsigned char *new_sha1, const char *msg)
  {
        int logfd, result, written, oflags = O_APPEND | O_WRONLY;
        if (log_all_ref_updates < 0)
                log_all_ref_updates = !is_bare_repository();
  
-       result = log_ref_setup(ref_name, log_file, sizeof(log_file));
+       result = log_ref_setup(refname, log_file, sizeof(log_file));
        if (result)
                return result;
  
@@@ -1648,7 -1658,7 +1664,7 @@@ int write_ref_sha1(struct ref_lock *loc
                unsigned char head_sha1[20];
                int head_flag;
                const char *head_ref;
 -              head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag);
 +              head_ref = resolve_ref_unsafe("HEAD", head_sha1, 1, &head_flag);
                if (head_ref && (head_flag & REF_ISSYMREF) &&
                    !strcmp(head_ref, lock->ref_name))
                        log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
@@@ -1735,7 -1745,9 +1751,9 @@@ static char *ref_msg(const char *line, 
        return xmemdupz(line, ep - line);
  }
  
- int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+ int read_ref_at(const char *refname, unsigned long at_time, int cnt,
+               unsigned char *sha1, char **msg,
+               unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
  {
        const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
        char *tz_c;
        void *log_mapped;
        size_t mapsz;
  
-       logfile = git_path("logs/%s", ref);
+       logfile = git_path("logs/%s", refname);
        logfd = open(logfile, O_RDONLY, 0);
        if (logfd < 0)
                die_errno("Unable to read log '%s'", logfile);
        return 1;
  }
  
- int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs, void *cb_data)
+ int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long ofs, void *cb_data)
  {
        const char *logfile;
        FILE *logfp;
        struct strbuf sb = STRBUF_INIT;
        int ret = 0;
  
-       logfile = git_path("logs/%s", ref);
+       logfile = git_path("logs/%s", refname);
        logfp = fopen(logfile, "r");
        if (!logfp)
                return -1;
        return ret;
  }
  
- int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
+ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data)
  {
-       return for_each_recent_reflog_ent(ref, fn, 0, cb_data);
+       return for_each_recent_reflog_ent(refname, fn, 0, cb_data);
  }
  
  static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
@@@ -1985,7 -1997,7 +2003,7 @@@ int update_ref(const char *action, cons
  int ref_exists(const char *refname)
  {
        unsigned char sha1[20];
 -      return !!resolve_ref(refname, sha1, 1, NULL);
 +      return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
  }
  
  struct ref *find_ref_by_name(const struct ref *list, const char *name)
@@@ -2019,7 -2031,7 +2037,7 @@@ static void gen_scanf_fmt(char *scanf_f
        return;
  }
  
- char *shorten_unambiguous_ref(const char *ref, int strict)
+ char *shorten_unambiguous_ref(const char *refname, int strict)
  {
        int i;
        static char **scanf_fmts;
  
        /* bail out if there are no rules */
        if (!nr_rules)
-               return xstrdup(ref);
+               return xstrdup(refname);
  
-       /* buffer for scanf result, at most ref must fit */
-       short_name = xstrdup(ref);
+       /* buffer for scanf result, at most refname must fit */
+       short_name = xstrdup(refname);
  
        /* skip first rule, it will always match */
        for (i = nr_rules - 1; i > 0 ; --i) {
                int rules_to_fail = i;
                int short_name_len;
  
-               if (1 != sscanf(ref, scanf_fmts[i], short_name))
+               if (1 != sscanf(refname, scanf_fmts[i], short_name))
                        continue;
  
                short_name_len = strlen(short_name);
        }
  
        free(short_name);
-       return xstrdup(ref);
+       return xstrdup(refname);
  }