bisect: use update_ref
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 67d6745e28ca44be1042e0ac41f9577ae6f8619b..522b19b5394364567539a8d7ded5a4a6194994f2 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;
 };
 
 /*
@@ -63,6 +62,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
@@ -162,7 +166,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 +174,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 +354,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 +631,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 +679,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 +727,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 +1246,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 +1263,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*
@@ -1374,21 +1378,38 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
                                         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;
                        }
@@ -1438,7 +1459,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;
 }
 
@@ -1448,7 +1469,7 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
 {
        int fd, len;
        char buffer[128], *p;
-       char *path;
+       const char *path;
 
        if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
                return -1;
@@ -1525,7 +1546,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;
@@ -1541,7 +1562,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;
@@ -1572,7 +1597,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;
@@ -1582,7 +1607,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
@@ -1709,6 +1736,16 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
        }
 }
 
+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 *ref, int resolve_flags, unsigned char *sha1, int *flags)
 {
        return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags));
@@ -1739,13 +1776,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 {
@@ -1819,9 +1857,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;
                }
        }
@@ -1830,7 +1868,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;
@@ -1845,7 +1883,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;
        }
 
@@ -1865,7 +1903,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;
                }
        }
@@ -1880,17 +1918,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)
@@ -2010,18 +2048,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;
 }
@@ -2096,12 +2134,12 @@ 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;
@@ -2201,27 +2239,35 @@ 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)
@@ -2342,7 +2388,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                                            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;
@@ -2354,7 +2400,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;
@@ -2365,7 +2410,7 @@ 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
                 * have foo/bar which now does not exist;
@@ -2384,7 +2429,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *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;
@@ -2404,7 +2449,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;
@@ -2423,7 +2468,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        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:
@@ -2436,8 +2481,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                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)
                        /*
@@ -2451,7 +2495,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                        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);
@@ -2481,18 +2529,28 @@ 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)
 {
+       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
@@ -2590,24 +2648,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;
@@ -2763,11 +2821,113 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
        return 0;
 }
 
+static int is_per_worktree_ref(const char *refname)
+{
+       return !strcmp(refname, "HEAD");
+}
+
+static int is_pseudoref_syntax(const char *refname)
+{
+       const char *c;
+
+       for (c = refname; *c; c++) {
+               if (!isupper(*c) && *c != '-' && *c != '_')
+                       return 0;
+       }
+
+       return 1;
+}
+
+enum ref_type ref_type(const char *refname)
+{
+       if (is_per_worktree_ref(refname))
+               return REF_TYPE_PER_WORKTREE;
+       if (is_pseudoref_syntax(refname))
+               return REF_TYPE_PSEUDOREF;
+       return REF_TYPE_NORMAL;
+}
+
+static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
+                          const unsigned char *old_sha1, struct strbuf *err)
+{
+       const char *filename;
+       int fd;
+       static struct lock_file lock;
+       struct strbuf buf = STRBUF_INIT;
+       int ret = -1;
+
+       strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
+
+       filename = git_path("%s", pseudoref);
+       fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+       if (fd < 0) {
+               strbuf_addf(err, "Could not open '%s' for writing: %s",
+                           filename, strerror(errno));
+               return -1;
+       }
+
+       if (old_sha1) {
+               unsigned char actual_old_sha1[20];
+               read_ref(pseudoref, actual_old_sha1);
+               if (hashcmp(actual_old_sha1, old_sha1)) {
+                       strbuf_addf(err, "Unexpected sha1 when writing %s", pseudoref);
+                       rollback_lock_file(&lock);
+                       goto done;
+               }
+       }
+
+       if (write_in_full(fd, buf.buf, buf.len) != buf.len) {
+               strbuf_addf(err, "Could not write to '%s'", filename);
+               rollback_lock_file(&lock);
+               goto done;
+       }
+
+       commit_lock_file(&lock);
+       ret = 0;
+done:
+       strbuf_release(&buf);
+       return ret;
+}
+
+static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)
+{
+       static struct lock_file lock;
+       const char *filename;
+
+       filename = git_path("%s", pseudoref);
+
+       if (old_sha1 && !is_null_sha1(old_sha1)) {
+               int fd;
+               unsigned char actual_old_sha1[20];
+
+               fd = hold_lock_file_for_update(&lock, filename,
+                                              LOCK_DIE_ON_ERROR);
+               if (fd < 0)
+                       die_errno(_("Could not open '%s' for writing"), filename);
+               read_ref(pseudoref, actual_old_sha1);
+               if (hashcmp(actual_old_sha1, old_sha1)) {
+                       warning("Unexpected sha1 when deleting %s", pseudoref);
+                       rollback_lock_file(&lock);
+                       return -1;
+               }
+
+               unlink(filename);
+               rollback_lock_file(&lock);
+       } else {
+               unlink(filename);
+       }
+
+       return 0;
+}
+
 int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags)
 {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
 
+       if (ref_type(refname) == REF_TYPE_PSEUDOREF)
+               return delete_pseudoref(refname, sha1);
+
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_delete(transaction, refname,
@@ -2798,7 +2958,7 @@ static int rename_tmp_log(const char *newrefname)
        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:
@@ -2857,9 +3017,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)
 {
@@ -2918,11 +3080,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;
        }
 
@@ -2938,9 +3101,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:
@@ -2959,7 +3124,6 @@ static int close_ref(struct ref_lock *lock)
 {
        if (close_lock_file(lock->lk))
                return -1;
-       lock->lock_fd = -1;
        return 0;
 }
 
@@ -2967,7 +3131,6 @@ static int commit_ref(struct ref_lock *lock)
 {
        if (commit_lock_file(lock->lk))
                return -1;
-       lock->lock_fd = -1;
        return 0;
 }
 
@@ -2997,21 +3160,35 @@ 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 *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;
@@ -3024,20 +3201,16 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)
 
                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;
                }
        }
@@ -3047,6 +3220,17 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)
        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)
@@ -3073,18 +3257,24 @@ 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 *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)
@@ -3092,21 +3282,30 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
        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/");
@@ -3114,36 +3313,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;
@@ -3155,12 +3354,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;
        }
@@ -3183,14 +3387,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;
 }
@@ -3203,6 +3414,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
        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);
@@ -3251,8 +3463,11 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
 #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;
@@ -3576,11 +3791,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;
@@ -3768,17 +3984,25 @@ int update_ref(const char *msg, const char *refname,
               const unsigned char *new_sha1, const unsigned char *old_sha1,
               unsigned int flags, enum action_on_err onerr)
 {
-       struct ref_transaction *t;
+       struct ref_transaction *t = NULL;
        struct strbuf err = STRBUF_INIT;
+       int ret = 0;
 
-       t = ref_transaction_begin(&err);
-       if (!t ||
-           ref_transaction_update(t, refname, new_sha1, old_sha1,
-                                  flags, msg, &err) ||
-           ref_transaction_commit(t, &err)) {
+       if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+               ret = write_pseudoref(refname, new_sha1, old_sha1, &err);
+       } else {
+               t = ref_transaction_begin(&err);
+               if (!t ||
+                   ref_transaction_update(t, refname, new_sha1, old_sha1,
+                                          flags, msg, &err) ||
+                   ref_transaction_commit(t, &err)) {
+                       ret = 1;
+                       ref_transaction_free(t);
+               }
+       }
+       if (ret) {
                const char *str = "update_ref failed for ref '%s': %s";
 
-               ref_transaction_free(t);
                switch (onerr) {
                case UPDATE_REFS_MSG_ON_ERR:
                        error(str, refname, err.buf);
@@ -3793,7 +4017,8 @@ int update_ref(const char *msg, const char *refname,
                return 1;
        }
        strbuf_release(&err);
-       ref_transaction_free(t);
+       if (t)
+               ref_transaction_free(t);
        return 0;
 }
 
@@ -3870,7 +4095,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;
@@ -3881,20 +4106,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 {
@@ -3920,11 +4150,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 {
@@ -4222,9 +4451,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);