Merge branch 'jk/refspec-parse-wildcard'
authorJunio C Hamano <gitster@pobox.com>
Mon, 3 Aug 2015 18:01:31 +0000 (11:01 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 3 Aug 2015 18:01:31 +0000 (11:01 -0700)
Allow an asterisk as a substring (as opposed to the entirety) of
a path component for both side of a refspec, e.g.
"refs/heads/o*:refs/remotes/heads/i*".

* jk/refspec-parse-wildcard:
refs: loosen restriction on wildcard "*" refspecs
refs: cleanup comments regarding check_refname_component()

1  2 
refs.c
refs.h
diff --combined refs.c
index 0b96ece4ec7e3caefd5776cac4e79c573d77b3bf,3127518e28353d4113fa46406b38cfe6016eb893..2db2975e08b7cccd644eab6b4b0ad752e4ec02b3
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -10,7 -10,8 +10,7 @@@ struct ref_lock 
        char *ref_name;
        char *orig_ref_name;
        struct lock_file *lk;
 -      unsigned char old_sha1[20];
 -      int lock_fd;
 +      struct object_id old_oid;
  };
  
  /*
   * 1: End-of-component
   * 2: ., look for a preceding . to reject .. in refs
   * 3: {, look for a preceding @ to reject @{ in refs
-  * 4: A bad character: ASCII control characters, "~", "^", ":" or SP
+  * 4: A bad character: ASCII control characters, and
+  *    ":", "?", "[", "\", "^", "~", SP, or TAB
+  * 5: *, reject unless REFNAME_REFSPEC_PATTERN is set
   */
  static unsigned char refname_disposition[256] = {
        1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-       4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
+       4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
   */
  #define REF_NEEDS_COMMIT 0x20
  
 +/*
 + * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a
 + * value to ref_update::flags
 + */
 +
  /*
   * Try to read one refname component from the front of refname.
   * Return the length of the component found, or -1 if the component is
   *
   * - any path component of it begins with ".", or
   * - it has double dots "..", or
-  * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
-  * - it ends with a "/".
-  * - it ends with ".lock"
-  * - it contains a "\" (backslash)
+  * - it has ASCII control characters, or
+  * - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
+  * - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
+  * - it ends with a "/", or
+  * - it ends with ".lock", or
+  * - it contains a "@{" portion
   */
- static int check_refname_component(const char *refname, int flags)
+ static int check_refname_component(const char *refname, int *flags)
  {
        const char *cp;
        char last = '\0';
                        break;
                case 4:
                        return -1;
+               case 5:
+                       if (!(*flags & REFNAME_REFSPEC_PATTERN))
+                               return -1; /* refspec can't be a pattern */
+                       /*
+                        * Unset the pattern flag so that we only accept
+                        * a single asterisk for one side of refspec.
+                        */
+                       *flags &= ~ REFNAME_REFSPEC_PATTERN;
+                       break;
                }
                last = ch;
        }
@@@ -125,18 -135,10 +139,10 @@@ int check_refname_format(const char *re
  
        while (1) {
                /* We are at the start of a path component. */
-               component_len = check_refname_component(refname, flags);
-               if (component_len <= 0) {
-                       if ((flags & REFNAME_REFSPEC_PATTERN) &&
-                                       refname[0] == '*' &&
-                                       (refname[1] == '\0' || refname[1] == '/')) {
-                               /* Accept one wildcard as a full refname component. */
-                               flags &= ~REFNAME_REFSPEC_PATTERN;
-                               component_len = 1;
-                       } else {
-                               return -1;
-                       }
-               }
+               component_len = check_refname_component(refname, &flags);
+               if (component_len <= 0)
+                       return -1;
                component_count++;
                if (refname[component_len] == '\0')
                        break;
@@@ -166,7 -168,7 +172,7 @@@ struct ref_value 
         * null.  If REF_ISSYMREF, then this is the name of the object
         * referred to by the last reference in the symlink chain.
         */
 -      unsigned char sha1[20];
 +      struct object_id oid;
  
        /*
         * If REF_KNOWS_PEELED, then this field holds the peeled value
         * be peelable.  See the documentation for peel_ref() for an
         * exact definition of "peelable".
         */
 -      unsigned char peeled[20];
 +      struct object_id peeled;
  };
  
  struct ref_cache;
@@@ -354,10 -356,12 +360,10 @@@ static struct ref_entry *create_ref_ent
        if (check_name &&
            check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
                die("Reference has invalid format: '%s'", refname);
 -      if (!check_name && !refname_is_safe(refname))
 -              die("Reference has invalid name: '%s'", refname);
        len = strlen(refname) + 1;
        ref = xmalloc(sizeof(struct ref_entry) + len);
 -      hashcpy(ref->u.value.sha1, sha1);
 -      hashclr(ref->u.value.peeled);
 +      hashcpy(ref->u.value.oid.hash, sha1);
 +      oidclr(&ref->u.value.peeled);
        memcpy(ref->name, refname, len);
        ref->flag = flag;
        return ref;
@@@ -631,7 -635,7 +637,7 @@@ static int is_dup_ref(const struct ref_
                /* This is impossible by construction */
                die("Reference directory conflict: %s", ref1->name);
  
 -      if (hashcmp(ref1->u.value.sha1, ref2->u.value.sha1))
 +      if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid))
                die("Duplicated ref, and SHA1s don't match: %s", ref1->name);
  
        warning("Duplicated ref: %s", ref1->name);
@@@ -679,7 -683,7 +685,7 @@@ static int ref_resolves_to_object(struc
  {
        if (entry->flag & REF_ISBROKEN)
                return 0;
 -      if (!has_sha1_file(entry->u.value.sha1)) {
 +      if (!has_sha1_file(entry->u.value.oid.hash)) {
                error("%s does not point to a valid object!", entry->name);
                return 0;
        }
@@@ -727,7 -731,7 +733,7 @@@ static int do_one_ref(struct ref_entry 
        /* Store the old value, in case this is a recursive call: */
        old_current_ref = current_ref;
        current_ref = entry;
 -      retval = data->fn(entry->name + data->trim, entry->u.value.sha1,
 +      retval = data->fn(entry->name + data->trim, &entry->u.value.oid,
                          entry->flag, data->cb_data);
        current_ref = old_current_ref;
        return retval;
@@@ -1246,8 -1250,6 +1252,8 @@@ static void read_packed_refs(FILE *f, s
                        int flag = REF_ISPACKED;
  
                        if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
 +                              if (!refname_is_safe(refname))
 +                                      die("packed refname is dangerous: %s", refname);
                                hashclr(sha1);
                                flag |= REF_BAD_NAME | REF_ISBROKEN;
                        }
                    line.len == PEELED_LINE_LENGTH &&
                    line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
                    !get_sha1_hex(line.buf + 1, sha1)) {
 -                      hashcpy(last->u.value.peeled, sha1);
 +                      hashcpy(last->u.value.peeled.hash, sha1);
                        /*
                         * Regardless of what the file header said,
                         * we definitely know the value of *this*
@@@ -1319,13 -1321,7 +1325,13 @@@ static struct ref_dir *get_packed_refs(
        return get_packed_ref_dir(get_packed_ref_cache(refs));
  }
  
 -void add_packed_ref(const char *refname, const unsigned char *sha1)
 +/*
 + * 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(const char *refname, const unsigned char *sha1)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
@@@ -1384,38 -1380,21 +1390,38 @@@ static void read_loose_refs(const char 
                                         create_dir_entry(refs, refname.buf,
                                                          refname.len, 1));
                } else {
 +                      int read_ok;
 +
                        if (*refs->name) {
                                hashclr(sha1);
                                flag = 0;
 -                              if (resolve_gitlink_ref(refs->name, refname.buf, sha1) < 0) {
 -                                      hashclr(sha1);
 -                                      flag |= REF_ISBROKEN;
 -                              }
 -                      } else if (read_ref_full(refname.buf,
 -                                               RESOLVE_REF_READING,
 -                                               sha1, &flag)) {
 +                              read_ok = !resolve_gitlink_ref(refs->name,
 +                                                             refname.buf, sha1);
 +                      } else {
 +                              read_ok = !read_ref_full(refname.buf,
 +                                                       RESOLVE_REF_READING,
 +                                                       sha1, &flag);
 +                      }
 +
 +                      if (!read_ok) {
                                hashclr(sha1);
                                flag |= REF_ISBROKEN;
 +                      } else if (is_null_sha1(sha1)) {
 +                              /*
 +                               * It is so astronomically unlikely
 +                               * that NULL_SHA1 is the SHA-1 of an
 +                               * actual object that we consider its
 +                               * appearance in a loose reference
 +                               * file to be repo corruption
 +                               * (probably due to a software bug).
 +                               */
 +                              flag |= REF_ISBROKEN;
                        }
 +
                        if (check_refname_format(refname.buf,
                                                 REFNAME_ALLOW_ONELEVEL)) {
 +                              if (!refname_is_safe(refname.buf))
 +                                      die("loose refname is dangerous: %s", refname.buf);
                                hashclr(sha1);
                                flag |= REF_BAD_NAME | REF_ISBROKEN;
                        }
@@@ -1465,7 -1444,7 +1471,7 @@@ static int resolve_gitlink_packed_ref(s
        if (ref == NULL)
                return -1;
  
 -      hashcpy(sha1, ref->u.value.sha1);
 +      hashcpy(sha1, ref->u.value.oid.hash);
        return 0;
  }
  
@@@ -1475,7 -1454,7 +1481,7 @@@ static int resolve_gitlink_ref_recursiv
  {
        int fd, len;
        char buffer[128], *p;
 -      char *path;
 +      const char *path;
  
        if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
                return -1;
@@@ -1552,7 -1531,7 +1558,7 @@@ static int resolve_missing_loose_ref(co
         */
        entry = get_packed_ref(refname);
        if (entry) {
 -              hashcpy(sha1, entry->u.value.sha1);
 +              hashcpy(sha1, entry->u.value.oid.hash);
                if (flags)
                        *flags |= REF_ISPACKED;
                return 0;
  }
  
  /* This function needs to return a meaningful errno on failure */
 -const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
 +static const char *resolve_ref_unsafe_1(const char *refname,
 +                                      int resolve_flags,
 +                                      unsigned char *sha1,
 +                                      int *flags,
 +                                      struct strbuf *sb_path)
  {
        int depth = MAXDEPTH;
        ssize_t len;
                bad_name = 1;
        }
        for (;;) {
 -              char path[PATH_MAX];
 +              const char *path;
                struct stat st;
                char *buf;
                int fd;
                        return NULL;
                }
  
 -              git_snpath(path, sizeof(path), "%s", refname);
 +              strbuf_reset(sb_path);
 +              strbuf_git_path(sb_path, "%s", refname);
 +              path = sb_path->buf;
  
                /*
                 * We might have to loop back here to avoid a race
        }
  }
  
 -char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
 +const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 +                             unsigned char *sha1, int *flags)
 +{
 +      struct strbuf sb_path = STRBUF_INIT;
 +      const char *ret = resolve_ref_unsafe_1(refname, resolve_flags,
 +                                             sha1, flags, &sb_path);
 +      strbuf_release(&sb_path);
 +      return ret;
 +}
 +
 +char *resolve_refdup(const char *refname, int resolve_flags,
 +                   unsigned char *sha1, int *flags)
  {
 -      return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags));
 +      return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
 +                                                sha1, flags));
  }
  
  /* The argument to filter_refs */
@@@ -1784,14 -1745,13 +1790,14 @@@ int ref_exists(const char *refname
        return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
  }
  
 -static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
 -                     void *data)
 +static int filter_refs(const char *refname, const struct object_id *oid,
 +                         int flags, void *data)
  {
        struct ref_filter *filter = (struct ref_filter *)data;
 +
        if (wildmatch(filter->pattern, refname, 0, NULL))
                return 0;
 -      return filter->fn(refname, sha1, flags, filter->cb_data);
 +      return filter->fn(refname, oid, flags, filter->cb_data);
  }
  
  enum peel_status {
@@@ -1865,9 -1825,9 +1871,9 @@@ static enum peel_status peel_entry(stru
        if (entry->flag & REF_KNOWS_PEELED) {
                if (repeel) {
                        entry->flag &= ~REF_KNOWS_PEELED;
 -                      hashclr(entry->u.value.peeled);
 +                      oidclr(&entry->u.value.peeled);
                } else {
 -                      return is_null_sha1(entry->u.value.peeled) ?
 +                      return is_null_oid(&entry->u.value.peeled) ?
                                PEEL_NON_TAG : PEEL_PEELED;
                }
        }
        if (entry->flag & REF_ISSYMREF)
                return PEEL_IS_SYMREF;
  
 -      status = peel_object(entry->u.value.sha1, entry->u.value.peeled);
 +      status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash);
        if (status == PEEL_PEELED || status == PEEL_NON_TAG)
                entry->flag |= REF_KNOWS_PEELED;
        return status;
@@@ -1891,7 -1851,7 +1897,7 @@@ int peel_ref(const char *refname, unsig
                            || !strcmp(current_ref->name, refname))) {
                if (peel_entry(current_ref, 0))
                        return -1;
 -              hashcpy(sha1, current_ref->u.value.peeled);
 +              hashcpy(sha1, current_ref->u.value.peeled.hash);
                return 0;
        }
  
                if (r) {
                        if (peel_entry(r, 0))
                                return -1;
 -                      hashcpy(sha1, r->u.value.peeled);
 +                      hashcpy(sha1, r->u.value.peeled.hash);
                        return 0;
                }
        }
@@@ -1926,17 -1886,17 +1932,17 @@@ struct warn_if_dangling_data 
        const char *msg_fmt;
  };
  
 -static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
 +static int warn_if_dangling_symref(const char *refname, const struct object_id *oid,
                                   int flags, void *cb_data)
  {
        struct warn_if_dangling_data *d = cb_data;
        const char *resolves_to;
 -      unsigned char junk[20];
 +      struct object_id junk;
  
        if (!(flags & REF_ISSYMREF))
                return 0;
  
 -      resolves_to = resolve_ref_unsafe(refname, 0, junk, NULL);
 +      resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL);
        if (!resolves_to
            || (d->refname
                ? strcmp(resolves_to, d->refname)
@@@ -2056,18 -2016,18 +2062,18 @@@ static int do_for_each_ref(struct ref_c
  
  static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int flag;
  
        if (submodule) {
 -              if (resolve_gitlink_ref(submodule, "HEAD", sha1) == 0)
 -                      return fn("HEAD", sha1, 0, cb_data);
 +              if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
 +                      return fn("HEAD", &oid, 0, cb_data);
  
                return 0;
        }
  
 -      if (!read_ref_full("HEAD", RESOLVE_REF_READING, sha1, &flag))
 -              return fn("HEAD", sha1, flag, cb_data);
 +      if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
 +              return fn("HEAD", &oid, flag, cb_data);
  
        return 0;
  }
@@@ -2135,20 -2095,19 +2141,20 @@@ int for_each_remote_ref_submodule(cons
  
  int for_each_replace_ref(each_ref_fn fn, void *cb_data)
  {
 -      return do_for_each_ref(&ref_cache, "refs/replace/", fn, 13, 0, cb_data);
 +      return do_for_each_ref(&ref_cache, git_replace_ref_base, fn,
 +                             strlen(git_replace_ref_base), 0, cb_data);
  }
  
  int head_ref_namespaced(each_ref_fn fn, void *cb_data)
  {
        struct strbuf buf = STRBUF_INIT;
        int ret = 0;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int flag;
  
        strbuf_addf(&buf, "%sHEAD", get_git_namespace());
 -      if (!read_ref_full(buf.buf, RESOLVE_REF_READING, sha1, &flag))
 -              ret = fn(buf.buf, sha1, flag, cb_data);
 +      if (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag))
 +              ret = fn(buf.buf, &oid, flag, cb_data);
        strbuf_release(&buf);
  
        return ret;
@@@ -2248,35 -2207,27 +2254,35 @@@ static void unlock_ref(struct ref_lock 
        free(lock);
  }
  
 -/* This function should make sure errno is meaningful on error */
 -static struct ref_lock *verify_lock(struct ref_lock *lock,
 -      const unsigned char *old_sha1, int mustexist)
 +/*
 + * Verify that the reference locked by lock has the value old_sha1.
 + * Fail if the reference doesn't exist and mustexist is set. Return 0
 + * on success. On error, write an error message to err, set errno, and
 + * return a negative value.
 + */
 +static int verify_lock(struct ref_lock *lock,
 +                     const unsigned char *old_sha1, int mustexist,
 +                     struct strbuf *err)
  {
 +      assert(err);
 +
        if (read_ref_full(lock->ref_name,
                          mustexist ? RESOLVE_REF_READING : 0,
 -                        lock->old_sha1, NULL)) {
 +                        lock->old_oid.hash, NULL)) {
                int save_errno = errno;
 -              error("Can't verify ref %s", lock->ref_name);
 -              unlock_ref(lock);
 +              strbuf_addf(err, "can't verify ref %s", lock->ref_name);
                errno = save_errno;
 -              return NULL;
 +              return -1;
        }
 -      if (hashcmp(lock->old_sha1, old_sha1)) {
 -              error("Ref %s is at %s but expected %s", lock->ref_name,
 -                      sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
 -              unlock_ref(lock);
 +      if (hashcmp(lock->old_oid.hash, old_sha1)) {
 +              strbuf_addf(err, "ref %s is at %s but expected %s",
 +                          lock->ref_name,
 +                          sha1_to_hex(lock->old_oid.hash),
 +                          sha1_to_hex(old_sha1));
                errno = EBUSY;
 -              return NULL;
 +              return -1;
        }
 -      return lock;
 +      return 0;
  }
  
  static int remove_empty_directories(const char *file)
@@@ -2397,7 -2348,7 +2403,7 @@@ static struct ref_lock *lock_ref_sha1_b
                                            unsigned int flags, int *type_p,
                                            struct strbuf *err)
  {
 -      char *ref_file;
 +      const char *ref_file;
        const char *orig_refname = refname;
        struct ref_lock *lock;
        int last_errno = 0;
        assert(err);
  
        lock = xcalloc(1, sizeof(struct ref_lock));
 -      lock->lock_fd = -1;
  
        if (mustexist)
                resolve_flags |= RESOLVE_REF_READING;
        }
  
        refname = resolve_ref_unsafe(refname, resolve_flags,
 -                                   lock->old_sha1, &type);
 +                                   lock->old_oid.hash, &type);
        if (!refname && errno == EISDIR) {
                /* we are trying to lock foo but we used to
                 * have foo/bar which now does not exist;
                        goto error_return;
                }
                refname = resolve_ref_unsafe(orig_refname, resolve_flags,
 -                                           lock->old_sha1, &type);
 +                                           lock->old_oid.hash, &type);
        }
        if (type_p)
            *type_p = type;
         * refname, nor a packed ref whose name is a proper prefix of
         * our refname.
         */
 -      if (is_null_sha1(lock->old_sha1) &&
 +      if (is_null_oid(&lock->old_oid) &&
            verify_refname_available(refname, extras, skip,
                                     get_packed_refs(&ref_cache), err)) {
                last_errno = ENOTDIR;
        ref_file = git_path("%s", refname);
  
   retry:
 -      switch (safe_create_leading_directories(ref_file)) {
 +      switch (safe_create_leading_directories_const(ref_file)) {
        case SCLD_OK:
                break; /* success */
        case SCLD_VANISHED:
                goto error_return;
        }
  
 -      lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
 -      if (lock->lock_fd < 0) {
 +      if (hold_lock_file_for_update(lock->lk, ref_file, lflags) < 0) {
                last_errno = errno;
                if (errno == ENOENT && --attempts_remaining > 0)
                        /*
                        goto error_return;
                }
        }
 -      return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 +      if (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) {
 +              last_errno = errno;
 +              goto error_return;
 +      }
 +      return lock;
  
   error_return:
        unlock_ref(lock);
@@@ -2538,32 -2487,18 +2544,32 @@@ static int write_packed_entry_fn(struc
        if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
                error("internal error: %s is not a valid packed reference!",
                      entry->name);
 -      write_packed_entry(cb_data, entry->name, entry->u.value.sha1,
 +      write_packed_entry(cb_data, entry->name, entry->u.value.oid.hash,
                           peel_status == PEEL_PEELED ?
 -                         entry->u.value.peeled : NULL);
 +                         entry->u.value.peeled.hash : NULL);
        return 0;
  }
  
 -/* This should return a meaningful errno on failure */
 -int lock_packed_refs(int flags)
 +/*
 + * 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(int flags)
  {
 +      static int timeout_configured = 0;
 +      static int timeout_value = 1000;
 +
        struct packed_ref_cache *packed_ref_cache;
  
 -      if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0)
 +      if (!timeout_configured) {
 +              git_config_get_int("core.packedrefstimeout", &timeout_value);
 +              timeout_configured = 1;
 +      }
 +
 +      if (hold_lock_file_for_update_timeout(
 +                          &packlock, git_path("packed-refs"),
 +                          flags, timeout_value) < 0)
                return -1;
        /*
         * Get the current packed-refs while holding the lock.  If the
  }
  
  /*
 - * Commit the packed refs changes.
 - * On error we must make sure that errno contains a meaningful value.
 + * 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
   */
 -int commit_packed_refs(void)
 +static int commit_packed_refs(void)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
        return error;
  }
  
 -void rollback_packed_refs(void)
 +/*
 + * 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(void)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
@@@ -2668,24 -2596,24 +2674,24 @@@ static int pack_if_possible_fn(struct r
        peel_status = peel_entry(entry, 1);
        if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
                die("internal error peeling reference %s (%s)",
 -                  entry->name, sha1_to_hex(entry->u.value.sha1));
 +                  entry->name, oid_to_hex(&entry->u.value.oid));
        packed_entry = find_ref(cb->packed_refs, entry->name);
        if (packed_entry) {
                /* Overwrite existing packed entry with info from loose entry */
                packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
 -              hashcpy(packed_entry->u.value.sha1, entry->u.value.sha1);
 +              oidcpy(&packed_entry->u.value.oid, &entry->u.value.oid);
        } else {
 -              packed_entry = create_ref_entry(entry->name, entry->u.value.sha1,
 +              packed_entry = create_ref_entry(entry->name, entry->u.value.oid.hash,
                                                REF_ISPACKED | REF_KNOWS_PEELED, 0);
                add_ref(cb->packed_refs, packed_entry);
        }
 -      hashcpy(packed_entry->u.value.peeled, entry->u.value.peeled);
 +      oidcpy(&packed_entry->u.value.peeled, &entry->u.value.peeled);
  
        /* Schedule the loose reference for pruning if requested. */
        if ((cb->flags & PACK_REFS_PRUNE)) {
                int namelen = strlen(entry->name) + 1;
                struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
 -              hashcpy(n->sha1, entry->u.value.sha1);
 +              hashcpy(n->sha1, entry->u.value.oid.hash);
                strcpy(n->name, entry->name);
                n->next = cb->ref_to_prune;
                cb->ref_to_prune = n;
@@@ -2776,14 -2704,7 +2782,14 @@@ int pack_refs(unsigned int flags
        return 0;
  }
  
 -int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 +/*
 + * 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 string_list *refnames, struct strbuf *err)
  {
        struct ref_dir *packed;
        struct string_list_item *refname;
@@@ -2848,15 -2769,15 +2854,15 @@@ static int delete_ref_loose(struct ref_
        return 0;
  }
  
 -int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags)
 +int delete_ref(const char *refname, const unsigned char *old_sha1,
 +             unsigned int flags)
  {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
  
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
 -          ref_transaction_delete(transaction, refname,
 -                                 (sha1 && !is_null_sha1(sha1)) ? sha1 : NULL,
 +          ref_transaction_delete(transaction, refname, old_sha1,
                                   flags, NULL, &err) ||
            ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
        return 0;
  }
  
 +int delete_refs(struct string_list *refnames)
 +{
 +      struct strbuf err = STRBUF_INIT;
 +      int i, result = 0;
 +
 +      if (!refnames->nr)
 +              return 0;
 +
 +      result = repack_without_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);
 +
 +              goto out;
 +      }
 +
 +      for (i = 0; i < refnames->nr; i++) {
 +              const char *refname = refnames->items[i].string;
 +
 +              if (delete_ref(refname, NULL, 0))
 +                      result |= error(_("could not remove reference %s"), refname);
 +      }
 +
 +out:
 +      strbuf_release(&err);
 +      return result;
 +}
 +
  /*
   * People using contrib's git-new-workdir have .git/logs/refs ->
   * /some/other/path/.git/logs/refs, and that may live on another device.
@@@ -2921,7 -2804,7 +2927,7 @@@ static int rename_tmp_log(const char *n
        int attempts_remaining = 4;
  
   retry:
 -      switch (safe_create_leading_directories(git_path("logs/%s", newrefname))) {
 +      switch (safe_create_leading_directories_const(git_path("logs/%s", newrefname))) {
        case SCLD_OK:
                break; /* success */
        case SCLD_VANISHED:
@@@ -2980,11 -2863,9 +2986,11 @@@ static int rename_ref_available(const c
        return ret;
  }
  
 -static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char *sha1);
 +static int write_ref_to_lockfile(struct ref_lock *lock,
 +                               const unsigned char *sha1, struct strbuf *err);
  static int commit_ref_update(struct ref_lock *lock,
 -                           const unsigned char *sha1, const char *logmsg);
 +                           const unsigned char *sha1, const char *logmsg,
 +                           int flags, struct strbuf *err);
  
  int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
  {
                strbuf_release(&err);
                goto rollback;
        }
 -      hashcpy(lock->old_sha1, orig_sha1);
 +      hashcpy(lock->old_oid.hash, orig_sha1);
  
 -      if (write_ref_to_lockfile(lock, orig_sha1) ||
 -          commit_ref_update(lock, orig_sha1, logmsg)) {
 -              error("unable to write current sha1 into %s", newrefname);
 +      if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
 +          commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) {
 +              error("unable to write current sha1 into %s: %s", newrefname, err.buf);
 +              strbuf_release(&err);
                goto rollback;
        }
  
  
        flag = log_all_ref_updates;
        log_all_ref_updates = 0;
 -      if (write_ref_to_lockfile(lock, orig_sha1) ||
 -          commit_ref_update(lock, orig_sha1, NULL))
 -              error("unable to write current sha1 into %s", oldrefname);
 +      if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
 +          commit_ref_update(lock, orig_sha1, NULL, 0, &err)) {
 +              error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
 +              strbuf_release(&err);
 +      }
        log_all_ref_updates = flag;
  
   rollbacklog:
@@@ -3087,6 -2965,7 +3093,6 @@@ static int close_ref(struct ref_lock *l
  {
        if (close_lock_file(lock->lk))
                return -1;
 -      lock->lock_fd = -1;
        return 0;
  }
  
@@@ -3094,6 -2973,7 +3100,6 @@@ static int commit_ref(struct ref_lock *
  {
        if (commit_lock_file(lock->lk))
                return -1;
 -      lock->lock_fd = -1;
        return 0;
  }
  
@@@ -3123,35 -3003,21 +3129,35 @@@ static int copy_msg(char *buf, const ch
        return cp - buf;
  }
  
 -/* This function must set a meaningful errno on failure */
 -int log_ref_setup(const char *refname, char *logfile, int bufsize)
 +static int should_autocreate_reflog(const char *refname)
 +{
 +      if (!log_all_ref_updates)
 +              return 0;
 +      return starts_with(refname, "refs/heads/") ||
 +              starts_with(refname, "refs/remotes/") ||
 +              starts_with(refname, "refs/notes/") ||
 +              !strcmp(refname, "HEAD");
 +}
 +
 +/*
 + * Create a reflog for a ref.  If force_create = 0, the reflog will
 + * only be created for certain refs (those for which
 + * should_autocreate_reflog returns non-zero.  Otherwise, create it
 + * regardless of the ref name.  Fill in *err and return -1 on failure.
 + */
 +static int log_ref_setup(const char *refname, struct strbuf *sb_logfile, struct strbuf *err, int force_create)
  {
        int logfd, oflags = O_APPEND | O_WRONLY;
 +      char *logfile;
  
 -      git_snpath(logfile, bufsize, "logs/%s", refname);
 -      if (log_all_ref_updates &&
 -          (starts_with(refname, "refs/heads/") ||
 -           starts_with(refname, "refs/remotes/") ||
 -           starts_with(refname, "refs/notes/") ||
 -           !strcmp(refname, "HEAD"))) {
 +      strbuf_git_path(sb_logfile, "logs/%s", refname);
 +      logfile = sb_logfile->buf;
 +      /* make sure the rest of the function can't change "logfile" */
 +      sb_logfile = NULL;
 +      if (force_create || should_autocreate_reflog(refname)) {
                if (safe_create_leading_directories(logfile) < 0) {
 -                      int save_errno = errno;
 -                      error("unable to create directory for %s", logfile);
 -                      errno = save_errno;
 +                      strbuf_addf(err, "unable to create directory for %s: "
 +                                  "%s", logfile, strerror(errno));
                        return -1;
                }
                oflags |= O_CREAT;
  
                if (errno == EISDIR) {
                        if (remove_empty_directories(logfile)) {
 -                              int save_errno = errno;
 -                              error("There are still logs under '%s'",
 -                                    logfile);
 -                              errno = save_errno;
 +                              strbuf_addf(err, "There are still logs under "
 +                                          "'%s'", logfile);
                                return -1;
                        }
                        logfd = open(logfile, oflags, 0666);
                }
  
                if (logfd < 0) {
 -                      int save_errno = errno;
 -                      error("Unable to append to %s: %s", logfile,
 -                            strerror(errno));
 -                      errno = save_errno;
 +                      strbuf_addf(err, "unable to append to %s: %s",
 +                                  logfile, strerror(errno));
                        return -1;
                }
        }
        return 0;
  }
  
 +
 +int safe_create_reflog(const char *refname, int force_create, struct strbuf *err)
 +{
 +      int ret;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      ret = log_ref_setup(refname, &sb, err, force_create);
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
  static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
                            const unsigned char *new_sha1,
                            const char *committer, const char *msg)
        return 0;
  }
  
 -static int log_ref_write(const char *refname, const unsigned char *old_sha1,
 -                       const unsigned char *new_sha1, const char *msg)
 +static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
 +                         const unsigned char *new_sha1, const char *msg,
 +                         struct strbuf *sb_log_file, int flags,
 +                         struct strbuf *err)
  {
        int logfd, result, oflags = O_APPEND | O_WRONLY;
 -      char log_file[PATH_MAX];
 +      char *log_file;
  
        if (log_all_ref_updates < 0)
                log_all_ref_updates = !is_bare_repository();
  
 -      result = log_ref_setup(refname, log_file, sizeof(log_file));
 +      result = log_ref_setup(refname, sb_log_file, err, flags & REF_FORCE_CREATE_REFLOG);
 +
        if (result)
                return result;
 +      log_file = sb_log_file->buf;
 +      /* make sure the rest of the function can't change "log_file" */
 +      sb_log_file = NULL;
  
        logfd = open(log_file, oflags);
        if (logfd < 0)
        result = log_ref_write_fd(logfd, old_sha1, new_sha1,
                                  git_committer_info(0), msg);
        if (result) {
 -              int save_errno = errno;
 +              strbuf_addf(err, "unable to append to %s: %s", log_file,
 +                          strerror(errno));
                close(logfd);
 -              error("Unable to append to %s", log_file);
 -              errno = save_errno;
                return -1;
        }
        if (close(logfd)) {
 -              int save_errno = errno;
 -              error("Unable to append to %s", log_file);
 -              errno = save_errno;
 +              strbuf_addf(err, "unable to append to %s: %s", log_file,
 +                          strerror(errno));
                return -1;
        }
        return 0;
  }
  
 +static int log_ref_write(const char *refname, const unsigned char *old_sha1,
 +                       const unsigned char *new_sha1, const char *msg,
 +                       int flags, struct strbuf *err)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,
 +                                err);
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
  int is_branch(const char *refname)
  {
        return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
  
  /*
   * Write sha1 into the open lockfile, then close the lockfile. On
 - * errors, rollback the lockfile and set errno to reflect the problem.
 + * errors, rollback the lockfile, fill in *err and
 + * return -1.
   */
  static int write_ref_to_lockfile(struct ref_lock *lock,
 -                               const unsigned char *sha1)
 +                               const unsigned char *sha1, struct strbuf *err)
  {
        static char term = '\n';
        struct object *o;
  
        o = parse_object(sha1);
        if (!o) {
 -              error("Trying to write ref %s with nonexistent object %s",
 -                      lock->ref_name, sha1_to_hex(sha1));
 +              strbuf_addf(err,
 +                          "Trying to write ref %s with nonexistent object %s",
 +                          lock->ref_name, sha1_to_hex(sha1));
                unlock_ref(lock);
 -              errno = EINVAL;
                return -1;
        }
        if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
 -              error("Trying to write non-commit object %s to branch %s",
 -                      sha1_to_hex(sha1), lock->ref_name);
 +              strbuf_addf(err,
 +                          "Trying to write non-commit object %s to branch %s",
 +                          sha1_to_hex(sha1), lock->ref_name);
                unlock_ref(lock);
 -              errno = EINVAL;
                return -1;
        }
 -      if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
 -          write_in_full(lock->lock_fd, &term, 1) != 1 ||
 +      if (write_in_full(lock->lk->fd, sha1_to_hex(sha1), 40) != 40 ||
 +          write_in_full(lock->lk->fd, &term, 1) != 1 ||
            close_ref(lock) < 0) {
 -              int save_errno = errno;
 -              error("Couldn't write %s", lock->lk->filename.buf);
 +              strbuf_addf(err,
 +                          "Couldn't write %s", lock->lk->filename.buf);
                unlock_ref(lock);
 -              errno = save_errno;
                return -1;
        }
        return 0;
   * necessary, using the specified lockmsg (which can be NULL).
   */
  static int commit_ref_update(struct ref_lock *lock,
 -                           const unsigned char *sha1, const char *logmsg)
 +                           const unsigned char *sha1, const char *logmsg,
 +                           int flags, struct strbuf *err)
  {
        clear_loose_ref_cache(&ref_cache);
 -      if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||
 +      if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0 ||
            (strcmp(lock->ref_name, lock->orig_ref_name) &&
 -           log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) {
 +           log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) {
 +              char *old_msg = strbuf_detach(err, NULL);
 +              strbuf_addf(err, "Cannot update the ref '%s': %s",
 +                          lock->ref_name, old_msg);
 +              free(old_msg);
                unlock_ref(lock);
                return -1;
        }
                head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
                                              head_sha1, &head_flag);
                if (head_ref && (head_flag & REF_ISSYMREF) &&
 -                  !strcmp(head_ref, lock->ref_name))
 -                      log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
 +                  !strcmp(head_ref, lock->ref_name)) {
 +                      struct strbuf log_err = STRBUF_INIT;
 +                      if (log_ref_write("HEAD", lock->old_oid.hash, sha1,
 +                                        logmsg, 0, &log_err)) {
 +                              error("%s", log_err.buf);
 +                              strbuf_release(&log_err);
 +                      }
 +              }
        }
        if (commit_ref(lock)) {
                error("Couldn't set %s", lock->ref_name);
                unlock_ref(lock);
                return -1;
        }
 +
        unlock_ref(lock);
        return 0;
  }
@@@ -3377,7 -3209,6 +3383,7 @@@ int create_symref(const char *ref_targe
        int fd, len, written;
        char *git_HEAD = git_pathdup("%s", ref_target);
        unsigned char old_sha1[20], new_sha1[20];
 +      struct strbuf err = STRBUF_INIT;
  
        if (logmsg && read_ref(ref_target, old_sha1))
                hashclr(old_sha1);
  #ifndef NO_SYMLINK_HEAD
        done:
  #endif
 -      if (logmsg && !read_ref(refs_heads_master, new_sha1))
 -              log_ref_write(ref_target, old_sha1, new_sha1, logmsg);
 +      if (logmsg && !read_ref(refs_heads_master, new_sha1) &&
 +              log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) {
 +              error("%s", err.buf);
 +              strbuf_release(&err);
 +      }
  
        free(git_HEAD);
        return 0;
@@@ -3481,14 -3309,14 +3487,14 @@@ static int read_ref_at_ent(unsigned cha
                        hashcpy(cb->sha1, nsha1);
                        if (hashcmp(cb->osha1, nsha1))
                                warning("Log for ref %s has gap after %s.",
 -                                      cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822));
 +                                      cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
                }
                else if (cb->date == cb->at_time)
                        hashcpy(cb->sha1, nsha1);
                else if (hashcmp(nsha1, cb->sha1))
                        warning("Log for ref %s unexpectedly ended on %s.",
                                cb->refname, show_date(cb->date, cb->tz,
 -                                                 DATE_RFC2822));
 +                                                     DATE_MODE(RFC2822)));
                hashcpy(cb->osha1, osha1);
                hashcpy(cb->nsha1, nsha1);
                cb->found_it = 1;
@@@ -3754,12 -3582,11 +3760,12 @@@ static int do_for_each_reflog(struct st
                                strbuf_addch(name, '/');
                                retval = do_for_each_reflog(name, fn, cb_data);
                        } else {
 -                              unsigned char sha1[20];
 -                              if (read_ref_full(name->buf, 0, sha1, NULL))
 +                              struct object_id oid;
 +
 +                              if (read_ref_full(name->buf, 0, oid.hash, NULL))
                                        retval = error("bad ref for %s", name->buf);
                                else
 -                                      retval = fn(name->buf, sha1, 0, cb_data);
 +                                      retval = fn(name->buf, &oid, 0, cb_data);
                        }
                        if (retval)
                                break;
@@@ -4049,7 -3876,7 +4055,7 @@@ int ref_transaction_commit(struct ref_t
                                ? TRANSACTION_NAME_CONFLICT
                                : TRANSACTION_GENERIC_ERROR;
                        reason = strbuf_detach(err, NULL);
 -                      strbuf_addf(err, "Cannot lock ref '%s': %s",
 +                      strbuf_addf(err, "cannot lock ref '%s': %s",
                                    update->refname, reason);
                        free(reason);
                        goto cleanup;
                                                  (update->flags & REF_NODEREF));
  
                        if (!overwriting_symref &&
 -                          !hashcmp(update->lock->old_sha1, update->new_sha1)) {
 +                          !hashcmp(update->lock->old_oid.hash, update->new_sha1)) {
                                /*
                                 * The reference already has the desired
                                 * value, so we don't need to write it.
                                 */
                        } else if (write_ref_to_lockfile(update->lock,
 -                                                       update->new_sha1)) {
 +                                                       update->new_sha1,
 +                                                       err)) {
 +                              char *write_err = strbuf_detach(err, NULL);
 +
                                /*
                                 * The lock was freed upon failure of
                                 * write_ref_to_lockfile():
                                 */
                                update->lock = NULL;
 -                              strbuf_addf(err, "Cannot update the ref '%s'.",
 -                                          update->refname);
 +                              strbuf_addf(err,
 +                                          "cannot update the ref '%s': %s",
 +                                          update->refname, write_err);
 +                              free(write_err);
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        } else {
  
                if (update->flags & REF_NEEDS_COMMIT) {
                        if (commit_ref_update(update->lock,
 -                                            update->new_sha1, update->msg)) {
 +                                            update->new_sha1, update->msg,
 +                                            update->flags, err)) {
                                /* freed by commit_ref_update(): */
                                update->lock = NULL;
 -                              strbuf_addf(err, "Cannot update the ref '%s'.",
 -                                          update->refname);
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        } else {
@@@ -4152,98 -3975,6 +4158,98 @@@ cleanup
        return ret;
  }
  
 +static int ref_present(const char *refname,
 +                     const struct object_id *oid, int flags, void *cb_data)
 +{
 +      struct string_list *affected_refnames = cb_data;
 +
 +      return string_list_has_string(affected_refnames, refname);
 +}
 +
 +int initial_ref_transaction_commit(struct ref_transaction *transaction,
 +                                 struct strbuf *err)
 +{
 +      struct ref_dir *loose_refs = get_loose_refs(&ref_cache);
 +      struct ref_dir *packed_refs = get_packed_refs(&ref_cache);
 +      int ret = 0, i;
 +      int n = transaction->nr;
 +      struct ref_update **updates = transaction->updates;
 +      struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
 +
 +      assert(err);
 +
 +      if (transaction->state != REF_TRANSACTION_OPEN)
 +              die("BUG: commit called for transaction that is not open");
 +
 +      /* Fail if a refname appears more than once in the transaction: */
 +      for (i = 0; i < n; i++)
 +              string_list_append(&affected_refnames, updates[i]->refname);
 +      string_list_sort(&affected_refnames);
 +      if (ref_update_reject_duplicates(&affected_refnames, err)) {
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +      /*
 +       * It's really undefined to call this function in an active
 +       * repository or when there are existing references: we are
 +       * only locking and changing packed-refs, so (1) any
 +       * simultaneous processes might try to change a reference at
 +       * the same time we do, and (2) any existing loose versions of
 +       * the references that we are setting would have precedence
 +       * over our values. But some remote helpers create the remote
 +       * "HEAD" and "master" branches before calling this function,
 +       * so here we really only check that none of the references
 +       * that we are creating already exists.
 +       */
 +      if (for_each_rawref(ref_present, &affected_refnames))
 +              die("BUG: initial ref transaction called with existing refs");
 +
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if ((update->flags & REF_HAVE_OLD) &&
 +                  !is_null_sha1(update->old_sha1))
 +                      die("BUG: initial ref transaction with old_sha1 set");
 +              if (verify_refname_available(update->refname,
 +                                           &affected_refnames, NULL,
 +                                           loose_refs, err) ||
 +                  verify_refname_available(update->refname,
 +                                           &affected_refnames, NULL,
 +                                           packed_refs, err)) {
 +                      ret = TRANSACTION_NAME_CONFLICT;
 +                      goto cleanup;
 +              }
 +      }
 +
 +      if (lock_packed_refs(0)) {
 +              strbuf_addf(err, "unable to lock packed-refs file: %s",
 +                          strerror(errno));
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if ((update->flags & REF_HAVE_NEW) &&
 +                  !is_null_sha1(update->new_sha1))
 +                      add_packed_ref(update->refname, update->new_sha1);
 +      }
 +
 +      if (commit_packed_refs()) {
 +              strbuf_addf(err, "unable to commit packed-refs file: %s",
 +                          strerror(errno));
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +cleanup:
 +      transaction->state = REF_TRANSACTION_CLOSED;
 +      string_list_clear(&affected_refnames, 0);
 +      return ret;
 +}
 +
  char *shorten_unambiguous_ref(const char *refname, int strict)
  {
        int i;
@@@ -4497,9 -4228,9 +4503,9 @@@ int reflog_expire(const char *refname, 
                        status |= error("couldn't write %s: %s", log_file,
                                        strerror(errno));
                } else if (update &&
 -                      (write_in_full(lock->lock_fd,
 +                         (write_in_full(lock->lk->fd,
                                sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
 -                       write_str_in_full(lock->lock_fd, "\n") != 1 ||
 +                       write_str_in_full(lock->lk->fd, "\n") != 1 ||
                         close_ref(lock) < 0)) {
                        status |= error("couldn't write %s",
                                        lock->lk->filename.buf);
diff --combined refs.h
index e4e46c3dc450d53eb299824da630bdbc4c5805cc,417f2c8ff2c35dc468b64c82444a3ee95f51419e..6a3fa6d41d50a7a76fdb2dab4c18aaaba1d9b8eb
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -1,98 -1,6 +1,98 @@@
  #ifndef REFS_H
  #define REFS_H
  
 +/*
 + * Resolve a reference, recursively following symbolic refererences.
 + *
 + * Store the referred-to object's name in sha1 and return the name of
 + * the non-symbolic reference that ultimately pointed at it.  The
 + * return value, if not NULL, is a pointer into either a static buffer
 + * or the input ref.
 + *
 + * If the reference cannot be resolved to an object, the behavior
 + * depends on the RESOLVE_REF_READING flag:
 + *
 + * - If RESOLVE_REF_READING is set, return NULL.
 + *
 + * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of
 + *   the last reference name in the chain, which will either be a non-symbolic
 + *   reference or an undefined reference.  If this is a prelude to
 + *   "writing" to the ref, the return value is the name of the ref
 + *   that will actually be created or changed.
 + *
 + * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one
 + * level of symbolic reference.  The value stored in sha1 for a symbolic
 + * reference will always be null_sha1 in this case, and the return
 + * value is the reference that the symref refers to directly.
 + *
 + * If flags is non-NULL, set the value that it points to the
 + * combination of REF_ISPACKED (if the reference was found among the
 + * packed references), REF_ISSYMREF (if the initial reference was a
 + * symbolic reference), REF_BAD_NAME (if the reference name is ill
 + * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
 + * (if the ref is malformed or has a bad name). See refs.h for more detail
 + * on each flag.
 + *
 + * If ref is not a properly-formatted, normalized reference, return
 + * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
 + * give up and return NULL.
 + *
 + * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
 + * name is invalid according to git-check-ref-format(1).  If the name
 + * is bad then the value stored in sha1 will be null_sha1 and the two
 + * flags REF_ISBROKEN and REF_BAD_NAME will be set.
 + *
 + * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
 + * directory and do not consist of all caps and underscores cannot be
 + * resolved. The function returns NULL for such ref names.
 + * Caps and underscores refers to the special refs, such as HEAD,
 + * FETCH_HEAD and friends, that all live outside of the refs/ directory.
 + */
 +#define RESOLVE_REF_READING 0x01
 +#define RESOLVE_REF_NO_RECURSE 0x02
 +#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
 +
 +extern const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 +                                    unsigned char *sha1, int *flags);
 +
 +extern char *resolve_refdup(const char *refname, int resolve_flags,
 +                          unsigned char *sha1, int *flags);
 +
 +extern int read_ref_full(const char *refname, int resolve_flags,
 +                       unsigned char *sha1, int *flags);
 +extern int read_ref(const char *refname, unsigned char *sha1);
 +
 +extern int ref_exists(const char *refname);
 +
 +extern int is_branch(const char *refname);
 +
 +/*
 + * If refname is a non-symbolic reference that refers to a tag object,
 + * and the tag can be (recursively) dereferenced to a non-tag object,
 + * store the SHA1 of the referred-to object to sha1 and return 0.  If
 + * any of these conditions are not met, return a non-zero value.
 + * Symbolic references are considered unpeelable, even if they
 + * ultimately resolve to a peelable tag.
 + */
 +extern int peel_ref(const char *refname, unsigned char *sha1);
 +
 +/**
 + * Resolve refname in the nested "gitlink" repository that is located
 + * at path.  If the resolution is successful, return 0 and set sha1 to
 + * the name of the object; otherwise, return a non-zero value.
 + */
 +extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1);
 +
 +/*
 + * Return true iff abbrev_name is a possible abbreviation for
 + * full_name according to the rules defined by ref_rev_parse_rules in
 + * refs.c.
 + */
 +extern int refname_match(const char *abbrev_name, const char *full_name);
 +
 +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);
 +
  /*
   * A ref_transaction represents a collection of ref updates
   * that should succeed or fail together.
@@@ -159,7 -67,7 +159,7 @@@ struct ref_transaction
   * single callback invocation.
   */
  typedef int each_ref_fn(const char *refname,
 -                      const unsigned char *sha1, int flags, void *cb_data);
 +                      const struct object_id *oid, int flags, void *cb_data);
  
  /*
   * The following functions invoke the specified callback function for
   * modifies the reference also returns a nonzero value to immediately
   * stop the iteration.
   */
 -extern int head_ref(each_ref_fn, void *);
 -extern int for_each_ref(each_ref_fn, void *);
 -extern int for_each_ref_in(const char *, each_ref_fn, void *);
 -extern int for_each_tag_ref(each_ref_fn, void *);
 -extern int for_each_branch_ref(each_ref_fn, void *);
 -extern int for_each_remote_ref(each_ref_fn, void *);
 -extern int for_each_replace_ref(each_ref_fn, void *);
 -extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
 -extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);
 +extern int head_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
 +extern int for_each_tag_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_branch_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
 +extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data);
  
  extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
  extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
@@@ -191,17 -99,47 +191,17 @@@ extern int for_each_remote_ref_submodul
  extern int head_ref_namespaced(each_ref_fn fn, void *cb_data);
  extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
  
 +/* can be used to learn about broken ref and symref */
 +extern int for_each_rawref(each_ref_fn fn, void *cb_data);
 +
  static inline const char *has_glob_specials(const char *pattern)
  {
        return strpbrk(pattern, "?*[");
  }
  
 -/* can be used to learn about broken ref and symref */
 -extern int for_each_rawref(each_ref_fn, void *);
 -
  extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
  extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames);
  
 -/*
 - * Lock the packed-refs file for writing.  Flags is passed to
 - * hold_lock_file_for_update().  Return 0 on success.
 - * Errno is set to something meaningful on error.
 - */
 -extern int lock_packed_refs(int flags);
 -
 -/*
 - * 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().
 - */
 -extern void add_packed_ref(const char *refname, const unsigned char *sha1);
 -
 -/*
 - * 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.
 - * Sets errno to something meaningful on error.
 - */
 -extern int commit_packed_refs(void);
 -
 -/*
 - * 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.)
 - */
 -extern void rollback_packed_refs(void);
 -
  /*
   * Flags for controlling behaviour of pack_refs()
   * PACK_REFS_PRUNE: Prune loose refs after packing
   */
  int pack_refs(unsigned int flags);
  
 -/*
 - * Rewrite the packed-refs file, omitting any refs listed in
 - * 'refnames'. On error, packed-refs will be unchanged, the return
 - * value is nonzero, and a message about the error is written to the
 - * 'err' strbuf.
 - *
 - * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
 - */
 -extern int repack_without_refs(struct string_list *refnames,
 -                             struct strbuf *err);
 -
 -extern int ref_exists(const char *);
 -
 -extern int is_branch(const char *refname);
 -
 -/*
 - * If refname is a non-symbolic reference that refers to a tag object,
 - * and the tag can be (recursively) dereferenced to a non-tag object,
 - * store the SHA1 of the referred-to object to sha1 and return 0.  If
 - * any of these conditions are not met, return a non-zero value.
 - * Symbolic references are considered unpeelable, even if they
 - * ultimately resolve to a peelable tag.
 - */
 -extern int peel_ref(const char *refname, unsigned char *sha1);
 -
  /*
   * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.
   * REF_NODEREF: act on the ref directly, instead of dereferencing
   * Other flags are reserved for internal use.
   */
  #define REF_NODEREF   0x01
 +#define REF_FORCE_CREATE_REFLOG 0x40
  
  /*
 - * Setup reflog before using. Set errno to something meaningful on failure.
 + * Setup reflog before using. Fill in err and return -1 on failure.
   */
 -int log_ref_setup(const char *refname, char *logfile, int bufsize);
 +int safe_create_reflog(const char *refname, int force_create, struct strbuf *err);
  
  /** Reads log for the value of ref during at_time. **/
  extern int read_ref_at(const char *refname, unsigned int flags,
  /** Check if a particular reflog exists */
  extern int reflog_exists(const char *refname);
  
 +/*
 + * Delete the specified reference. If old_sha1 is non-NULL, then
 + * verify that the current value of the reference is old_sha1 before
 + * deleting it. If old_sha1 is NULL, delete the reference if it
 + * exists, regardless of its old value. It is an error for old_sha1 to
 + * be NULL_SHA1. flags is passed through to ref_transaction_delete().
 + */
 +extern int delete_ref(const char *refname, const unsigned char *old_sha1,
 +                    unsigned int flags);
 +
 +/*
 + * Delete the specified references. If there are any problems, emit
 + * errors but attempt to keep going (i.e., the deletes are not done in
 + * an all-or-nothing transaction).
 + */
 +extern int delete_refs(struct string_list *refnames);
 +
  /** Delete a reflog */
  extern int delete_reflog(const char *refname);
  
@@@ -279,19 -224,23 +279,19 @@@ extern int for_each_reflog(each_ref_fn
   * to the rules described in Documentation/git-check-ref-format.txt.
   * If REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level
   * reference names.  If REFNAME_REFSPEC_PATTERN is set in flags, then
-  * allow a "*" wildcard character in place of one of the name
-  * components.  No leading or repeated slashes are accepted.
+  * allow a single "*" wildcard character in the refspec. No leading or
+  * repeated slashes are accepted.
   */
  extern int check_refname_format(const char *refname, int flags);
  
  extern const char *prettify_refname(const char *refname);
 +
  extern char *shorten_unambiguous_ref(const char *refname, int strict);
  
  /** rename ref, return 0 on success **/
  extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
  
 -/**
 - * Resolve refname in the nested "gitlink" repository that is located
 - * at path.  If the resolution is successful, return 0 and set sha1 to
 - * the name of the object; otherwise, return a non-zero value.
 - */
 -extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1);
 +extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
  
  enum action_on_err {
        UPDATE_REFS_MSG_ON_ERR,
@@@ -410,20 -359,6 +410,20 @@@ int ref_transaction_verify(struct ref_t
  int ref_transaction_commit(struct ref_transaction *transaction,
                           struct strbuf *err);
  
 +/*
 + * Like ref_transaction_commit(), but optimized for creating
 + * references when originally initializing a repository (e.g., by "git
 + * clone"). It writes the new references directly to packed-refs
 + * without locking the individual references.
 + *
 + * It is a bug to call this function when there might be other
 + * processes accessing the repository or if there are existing
 + * references that might conflict with the ones being created. All
 + * old_sha1 values must either be absent or NULL_SHA1.
 + */
 +int initial_ref_transaction_commit(struct ref_transaction *transaction,
 +                                 struct strbuf *err);
 +
  /*
   * Free an existing transaction and all associated data.
   */
@@@ -442,7 -377,6 +442,7 @@@ int update_ref(const char *msg, const c
               unsigned int flags, enum action_on_err onerr);
  
  extern int parse_hide_refs_config(const char *var, const char *value, const char *);
 +
  extern int ref_is_hidden(const char *);
  
  enum expire_reflog_flags {