Merge 'hv/submodule-config' to 'sb/submodule-helper'
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 305bb54169b41cfb53c2eff260cb6cd80f1810c4..84b1aff8421de069c0e89e5a145642c8efe5d412 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -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;
 };
 
 /*
@@ -20,12 +19,14 @@ struct ref_lock {
  * 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,
@@ -63,6 +64,11 @@ static unsigned char refname_disposition[256] = {
  */
 #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
@@ -71,12 +77,14 @@ static unsigned char refname_disposition[256] = {
  *
  * - 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';
@@ -97,6 +105,16 @@ static int check_refname_component(const char *refname, int flags)
                        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;
        }
@@ -121,18 +139,10 @@ int check_refname_format(const char *refname, int flags)
 
        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;
@@ -162,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
@@ -170,7 +180,7 @@ struct ref_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;
@@ -350,12 +360,10 @@ static struct ref_entry *create_ref_entry(const char *refname,
        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;
@@ -629,7 +637,7 @@ static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2
                /* 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);
@@ -677,7 +685,7 @@ static int ref_resolves_to_object(struct ref_entry *entry)
 {
        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;
        }
@@ -725,7 +733,7 @@ static int do_one_ref(struct ref_entry *entry, void *cb_data)
        /* 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;
@@ -1244,6 +1252,8 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                        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;
                        }
@@ -1259,7 +1269,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                    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*
@@ -1278,12 +1288,12 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
  */
 static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
 {
-       const char *packed_refs_file;
+       char *packed_refs_file;
 
        if (*refs->name)
-               packed_refs_file = git_path_submodule(refs->name, "packed-refs");
+               packed_refs_file = git_pathdup_submodule(refs->name, "packed-refs");
        else
-               packed_refs_file = git_path("packed-refs");
+               packed_refs_file = git_pathdup("packed-refs");
 
        if (refs->packed &&
            !stat_validity_check(&refs->packed->validity, packed_refs_file))
@@ -1302,6 +1312,7 @@ static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
                        fclose(f);
                }
        }
+       free(packed_refs_file);
        return refs->packed;
 }
 
@@ -1315,7 +1326,13 @@ static struct ref_dir *get_packed_refs(struct ref_cache *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);
@@ -1335,19 +1352,23 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 {
        struct ref_cache *refs = dir->ref_cache;
        DIR *d;
-       const char *path;
        struct dirent *de;
        int dirnamelen = strlen(dirname);
        struct strbuf refname;
+       struct strbuf path = STRBUF_INIT;
+       size_t path_baselen;
 
        if (*refs->name)
-               path = git_path_submodule(refs->name, "%s", dirname);
+               strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
        else
-               path = git_path("%s", dirname);
+               strbuf_git_path(&path, "%s", dirname);
+       path_baselen = path.len;
 
-       d = opendir(path);
-       if (!d)
+       d = opendir(path.buf);
+       if (!d) {
+               strbuf_release(&path);
                return;
+       }
 
        strbuf_init(&refname, dirnamelen + 257);
        strbuf_add(&refname, dirname, dirnamelen);
@@ -1356,17 +1377,14 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
                unsigned char sha1[20];
                struct stat st;
                int flag;
-               const char *refdir;
 
                if (de->d_name[0] == '.')
                        continue;
                if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(&refname, de->d_name);
-               refdir = *refs->name
-                       ? git_path_submodule(refs->name, "%s", refname.buf)
-                       : git_path("%s", refname.buf);
-               if (stat(refdir, &st) < 0) {
+               strbuf_addstr(&path, de->d_name);
+               if (stat(path.buf, &st) < 0) {
                        ; /* silently ignore */
                } else if (S_ISDIR(st.st_mode)) {
                        strbuf_addch(&refname, '/');
@@ -1404,6 +1422,8 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 
                        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;
                        }
@@ -1411,8 +1431,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
                                         create_ref_entry(refname.buf, sha1, flag, 0));
                }
                strbuf_setlen(&refname, dirnamelen);
+               strbuf_setlen(&path, path_baselen);
        }
        strbuf_release(&refname);
+       strbuf_release(&path);
        closedir(d);
 }
 
@@ -1453,7 +1475,7 @@ static int resolve_gitlink_packed_ref(struct ref_cache *refs,
        if (ref == NULL)
                return -1;
 
-       hashcpy(sha1, ref->u.value.sha1);
+       hashcpy(sha1, ref->u.value.oid.hash);
        return 0;
 }
 
@@ -1468,9 +1490,10 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
        if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
                return -1;
        path = *refs->name
-               ? git_path_submodule(refs->name, "%s", refname)
-               : git_path("%s", refname);
+               ? git_pathdup_submodule(refs->name, "%s", refname)
+               : git_pathdup("%s", refname);
        fd = open(path, O_RDONLY);
+       free(path);
        if (fd < 0)
                return resolve_gitlink_packed_ref(refs, refname, sha1);
 
@@ -1540,7 +1563,7 @@ static int resolve_missing_loose_ref(const char *refname,
         */
        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;
@@ -1556,7 +1579,11 @@ static int resolve_missing_loose_ref(const char *refname,
 }
 
 /* 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;
@@ -1587,7 +1614,7 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
                bad_name = 1;
        }
        for (;;) {
-               char path[PATH_MAX];
+               const char *path;
                struct stat st;
                char *buf;
                int fd;
@@ -1597,7 +1624,9 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
                        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
@@ -1724,9 +1753,21 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
        }
 }
 
-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 */
@@ -1754,13 +1795,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 {
@@ -1834,9 +1876,9 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
        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;
                }
        }
@@ -1845,7 +1887,7 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
        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;
@@ -1860,7 +1902,7 @@ int peel_ref(const char *refname, unsigned char *sha1)
                            || !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;
        }
 
@@ -1880,7 +1922,7 @@ int peel_ref(const char *refname, unsigned char *sha1)
                if (r) {
                        if (peel_entry(r, 0))
                                return -1;
-                       hashcpy(sha1, r->u.value.peeled);
+                       hashcpy(sha1, r->u.value.peeled.hash);
                        return 0;
                }
        }
@@ -1895,17 +1937,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)
@@ -2025,18 +2067,18 @@ static int do_for_each_ref(struct ref_cache *refs, const char *base,
 
 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;
 }
@@ -2104,19 +2146,20 @@ int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *c
 
 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;
@@ -2216,48 +2259,45 @@ static void unlock_ref(struct ref_lock *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)
+static int remove_empty_directories(struct strbuf *path)
 {
-       /* we want to create a file but there is a directory there;
+       /*
+        * we want to create a file but there is a directory there;
         * if that is an empty directory (or a directory that contains
         * only empty directories), remove them.
         */
-       struct strbuf path;
-       int result, save_errno;
-
-       strbuf_init(&path, 20);
-       strbuf_addstr(&path, file);
-
-       result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
-       save_errno = errno;
-
-       strbuf_release(&path);
-       errno = save_errno;
-
-       return result;
+       return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY);
 }
 
 /*
@@ -2357,7 +2397,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                                            unsigned int flags, int *type_p,
                                            struct strbuf *err)
 {
-       char *ref_file;
+       struct strbuf ref_file = STRBUF_INIT;
+       struct strbuf orig_ref_file = STRBUF_INIT;
        const char *orig_refname = refname;
        struct ref_lock *lock;
        int last_errno = 0;
@@ -2369,7 +2410,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        assert(err);
 
        lock = xcalloc(1, sizeof(struct ref_lock));
-       lock->lock_fd = -1;
 
        if (mustexist)
                resolve_flags |= RESOLVE_REF_READING;
@@ -2380,26 +2420,25 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        }
 
        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
+               /*
+                * 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_refname);
-               if (remove_empty_directories(ref_file)) {
+               strbuf_git_path(&orig_ref_file, "%s", orig_refname);
+               if (remove_empty_directories(&orig_ref_file)) {
                        last_errno = errno;
-
                        if (!verify_refname_available(orig_refname, extras, skip,
                                                      get_loose_refs(&ref_cache), err))
                                strbuf_addf(err, "there are still refs under '%s'",
                                            orig_refname);
-
                        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;
@@ -2419,7 +2458,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
         * 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;
@@ -2435,10 +2474,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        }
        lock->ref_name = xstrdup(refname);
        lock->orig_ref_name = xstrdup(orig_refname);
-       ref_file = git_path("%s", refname);
+       strbuf_git_path(&ref_file, "%s", refname);
 
  retry:
-       switch (safe_create_leading_directories(ref_file)) {
+       switch (safe_create_leading_directories_const(ref_file.buf)) {
        case SCLD_OK:
                break; /* success */
        case SCLD_VANISHED:
@@ -2447,12 +2486,12 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                /* fall through */
        default:
                last_errno = errno;
-               strbuf_addf(err, "unable to create directory for %s", ref_file);
+               strbuf_addf(err, "unable to create directory for %s",
+                           ref_file.buf);
                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.buf, lflags) < 0) {
                last_errno = errno;
                if (errno == ENOENT && --attempts_remaining > 0)
                        /*
@@ -2462,16 +2501,25 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                         */
                        goto retry;
                else {
-                       unable_to_lock_message(ref_file, errno, err);
+                       unable_to_lock_message(ref_file.buf, errno, err);
                        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;
+       }
+       goto out;
 
  error_return:
        unlock_ref(lock);
+       lock = NULL;
+
+ out:
+       strbuf_release(&ref_file);
+       strbuf_release(&orig_ref_file);
        errno = last_errno;
-       return NULL;
+       return lock;
 }
 
 /*
@@ -2496,18 +2544,32 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
        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
@@ -2523,10 +2585,12 @@ int lock_packed_refs(int flags)
 }
 
 /*
- * 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);
@@ -2555,7 +2619,12 @@ int commit_packed_refs(void)
        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);
@@ -2605,24 +2674,24 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
        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;
@@ -2713,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;
@@ -2778,15 +2854,15 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
        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);
@@ -2799,6 +2875,44 @@ int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flag
        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.
@@ -2811,9 +2925,13 @@ int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flag
 static int rename_tmp_log(const char *newrefname)
 {
        int attempts_remaining = 4;
+       struct strbuf path = STRBUF_INIT;
+       int ret = -1;
 
  retry:
-       switch (safe_create_leading_directories(git_path("logs/%s", newrefname))) {
+       strbuf_reset(&path);
+       strbuf_git_path(&path, "logs/%s", newrefname);
+       switch (safe_create_leading_directories_const(path.buf)) {
        case SCLD_OK:
                break; /* success */
        case SCLD_VANISHED:
@@ -2822,19 +2940,19 @@ static int rename_tmp_log(const char *newrefname)
                /* fall through */
        default:
                error("unable to create directory for %s", newrefname);
-               return -1;
+               goto out;
        }
 
-       if (rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newrefname))) {
+       if (rename(git_path(TMP_RENAMED_LOG), path.buf)) {
                if ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {
                        /*
                         * 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", newrefname))) {
+                       if (remove_empty_directories(&path)) {
                                error("Directory not empty: logs/%s", newrefname);
-                               return -1;
+                               goto out;
                        }
                        goto retry;
                } else if (errno == ENOENT && --attempts_remaining > 0) {
@@ -2847,10 +2965,13 @@ static int rename_tmp_log(const char *newrefname)
                } else {
                        error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
                                newrefname, strerror(errno));
-                       return -1;
+                       goto out;
                }
        }
-       return 0;
+       ret = 0;
+out:
+       strbuf_release(&path);
+       return ret;
 }
 
 static int rename_ref_available(const char *oldname, const char *newname)
@@ -2872,9 +2993,11 @@ static int rename_ref_available(const char *oldname, const char *newname)
        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)
 {
@@ -2912,7 +3035,14 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
        if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
            delete_ref(newrefname, sha1, REF_NODEREF)) {
                if (errno==EISDIR) {
-                       if (remove_empty_directories(git_path("%s", newrefname))) {
+                       struct strbuf path = STRBUF_INIT;
+                       int result;
+
+                       strbuf_git_path(&path, "%s", newrefname);
+                       result = remove_empty_directories(&path);
+                       strbuf_release(&path);
+
+                       if (result) {
                                error("Directory not empty: %s", newrefname);
                                goto rollback;
                        }
@@ -2933,11 +3063,12 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
                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;
        }
 
@@ -2953,9 +3084,11 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 
        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:
@@ -2974,7 +3107,6 @@ static int close_ref(struct ref_lock *lock)
 {
        if (close_lock_file(lock->lk))
                return -1;
-       lock->lock_fd = -1;
        return 0;
 }
 
@@ -2982,7 +3114,6 @@ static int commit_ref(struct ref_lock *lock)
 {
        if (commit_lock_file(lock->lk))
                return -1;
-       lock->lock_fd = -1;
        return 0;
 }
 
@@ -3012,56 +3143,73 @@ static int copy_msg(char *buf, const char *msg)
        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 *logfile, struct strbuf *err, int force_create)
 {
        int logfd, oflags = O_APPEND | O_WRONLY;
 
-       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"))) {
-               if (safe_create_leading_directories(logfile) < 0) {
-                       int save_errno = errno;
-                       error("unable to create directory for %s", logfile);
-                       errno = save_errno;
+       strbuf_git_path(logfile, "logs/%s", refname);
+       if (force_create || should_autocreate_reflog(refname)) {
+               if (safe_create_leading_directories(logfile->buf) < 0) {
+                       strbuf_addf(err, "unable to create directory for %s: "
+                                   "%s", logfile->buf, strerror(errno));
                        return -1;
                }
                oflags |= O_CREAT;
        }
 
-       logfd = open(logfile, oflags, 0666);
+       logfd = open(logfile->buf, oflags, 0666);
        if (logfd < 0) {
                if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR))
                        return 0;
 
                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->buf);
                                return -1;
                        }
-                       logfd = open(logfile, oflags, 0666);
+                       logfd = open(logfile->buf, 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->buf, strerror(errno));
                        return -1;
                }
        }
 
-       adjust_shared_perm(logfile);
+       adjust_shared_perm(logfile->buf);
        close(logfd);
        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)
@@ -3088,40 +3236,51 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
        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 *logfile, int flags,
+                          struct strbuf *err)
 {
        int logfd, result, oflags = O_APPEND | O_WRONLY;
-       char log_file[PATH_MAX];
 
        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, logfile, err, flags & REF_FORCE_CREATE_REFLOG);
+
        if (result)
                return result;
 
-       logfd = open(log_file, oflags);
+       logfd = open(logfile->buf, oflags);
        if (logfd < 0)
                return 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", logfile->buf,
+                           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", logfile->buf,
+                           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/");
@@ -3129,36 +3288,36 @@ int is_branch(const char *refname)
 
 /*
  * 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;
@@ -3170,12 +3329,17 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
  * 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;
        }
@@ -3198,14 +3362,21 @@ static int commit_ref_update(struct ref_lock *lock,
                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;
 }
@@ -3213,11 +3384,12 @@ static int commit_ref_update(struct ref_lock *lock,
 int create_symref(const char *ref_target, const char *refs_heads_master,
                  const char *logmsg)
 {
-       const char *lockpath;
+       char *lockpath = NULL;
        char ref[1000];
        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);
@@ -3239,7 +3411,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
                error("refname too long: %s", refs_heads_master);
                goto error_free_return;
        }
-       lockpath = mkpath("%s.lock", git_HEAD);
+       lockpath = mkpathdup("%s.lock", git_HEAD);
        fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
        if (fd < 0) {
                error("Unable to open %s for writing", lockpath);
@@ -3259,15 +3431,20 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
        error_unlink_return:
                unlink_or_warn(lockpath);
        error_free_return:
+               free(lockpath);
                free(git_HEAD);
                return -1;
        }
+       free(lockpath);
 
 #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;
@@ -3318,14 +3495,14 @@ static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
                        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;
@@ -3591,11 +3768,12 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data
                                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;
@@ -3885,7 +4063,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                                ? 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;
@@ -3896,20 +4074,25 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                                                  (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 {
@@ -3935,11 +4118,10 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 
                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 {
@@ -3984,6 +4166,98 @@ int ref_transaction_commit(struct ref_transaction *transaction,
        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;
@@ -4237,9 +4511,9 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
                        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);