verify_refname_available(): adjust constness in declaration
[gitweb.git] / refs / files-backend.c
index 81f68f846b69af65badfbe8c25d4a03601fb23c8..1d1b72d57d1c895280e54e79d0e52192a70094de 100644 (file)
@@ -513,9 +513,6 @@ static void sort_ref_dir(struct ref_dir *dir)
        dir->sorted = dir->nr = i;
 }
 
-/* Include broken references in a do_for_each_ref*() iteration: */
-#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
-
 /*
  * Return true iff the reference described by entry can be resolved to
  * an object in the database.  Emit a warning if the referred-to
@@ -1272,8 +1269,6 @@ static struct ref_dir *get_loose_refs(struct ref_cache *refs)
        return get_ref_dir(refs->loose);
 }
 
-/* We allow "recursive" symbolic refs. Only within reason, though */
-#define MAXDEPTH 5
 #define MAXREFLEN (1024)
 
 /*
@@ -1303,7 +1298,7 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
        char buffer[128], *p;
        char *path;
 
-       if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
+       if (recursion > SYMREF_MAXDEPTH || strlen(refname) > MAXREFLEN)
                return -1;
        path = *refs->name
                ? git_pathdup_submodule(refs->name, "%s", refname)
@@ -1371,13 +1366,11 @@ static struct ref_entry *get_packed_ref(const char *refname)
 }
 
 /*
- * A loose ref file doesn't exist; check for a packed ref.  The
- * options are forwarded from resolve_safe_unsafe().
+ * A loose ref file doesn't exist; check for a packed ref.
  */
 static int resolve_missing_loose_ref(const char *refname,
-                                    int resolve_flags,
                                     unsigned char *sha1,
-                                    int *flags)
+                                    unsigned int *flags)
 {
        struct ref_entry *entry;
 
@@ -1388,205 +1381,138 @@ static int resolve_missing_loose_ref(const char *refname,
        entry = get_packed_ref(refname);
        if (entry) {
                hashcpy(sha1, entry->u.value.oid.hash);
-               if (flags)
-                       *flags |= REF_ISPACKED;
-               return 0;
-       }
-       /* The reference is not a packed reference, either. */
-       if (resolve_flags & RESOLVE_REF_READING) {
-               errno = ENOENT;
-               return -1;
-       } else {
-               hashclr(sha1);
+               *flags |= REF_ISPACKED;
                return 0;
        }
+       /* refname is not a packed reference. */
+       return -1;
 }
 
-/* This function needs to return a meaningful errno on failure */
-static const char *resolve_ref_1(const char *refname,
-                                int resolve_flags,
-                                unsigned char *sha1,
-                                int *flags,
-                                struct strbuf *sb_refname,
-                                struct strbuf *sb_path,
-                                struct strbuf *sb_contents)
+int read_raw_ref(const char *refname, unsigned char *sha1,
+                struct strbuf *referent, unsigned int *type)
 {
-       int depth = MAXDEPTH;
-       int bad_name = 0;
+       struct strbuf sb_contents = STRBUF_INIT;
+       struct strbuf sb_path = STRBUF_INIT;
+       const char *path;
+       const char *buf;
+       struct stat st;
+       int fd;
+       int ret = -1;
+       int save_errno;
 
-       if (flags)
-               *flags = 0;
+       *type = 0;
+       strbuf_reset(&sb_path);
+       strbuf_git_path(&sb_path, "%s", refname);
+       path = sb_path.buf;
 
-       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
-               if (flags)
-                       *flags |= REF_BAD_NAME;
+stat_ref:
+       /*
+        * We might have to loop back here to avoid a race
+        * condition: first we lstat() the file, then we try
+        * to read it as a link or as a file.  But if somebody
+        * changes the type of the file (file <-> directory
+        * <-> symlink) between the lstat() and reading, then
+        * we don't want to report that as an error but rather
+        * try again starting with the lstat().
+        */
 
-               if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
-                   !refname_is_safe(refname)) {
-                       errno = EINVAL;
-                       return NULL;
+       if (lstat(path, &st) < 0) {
+               if (errno != ENOENT)
+                       goto out;
+               if (resolve_missing_loose_ref(refname, sha1, type)) {
+                       errno = ENOENT;
+                       goto out;
                }
-               /*
-                * dwim_ref() uses REF_ISBROKEN to distinguish between
-                * missing refs and refs that were present but invalid,
-                * to complain about the latter to stderr.
-                *
-                * We don't know whether the ref exists, so don't set
-                * REF_ISBROKEN yet.
-                */
-               bad_name = 1;
+               ret = 0;
+               goto out;
        }
-       for (;;) {
-               const char *path;
-               struct stat st;
-               char *buf;
-               int fd;
 
-               if (--depth < 0) {
-                       errno = ELOOP;
-                       return NULL;
-               }
-
-               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
-                * condition: first we lstat() the file, then we try
-                * to read it as a link or as a file.  But if somebody
-                * changes the type of the file (file <-> directory
-                * <-> symlink) between the lstat() and reading, then
-                * we don't want to report that as an error but rather
-                * try again starting with the lstat().
-                */
-       stat_ref:
-               if (lstat(path, &st) < 0) {
-                       if (errno != ENOENT)
-                               return NULL;
-                       if (resolve_missing_loose_ref(refname, resolve_flags,
-                                                     sha1, flags))
-                               return NULL;
-                       if (bad_name) {
-                               hashclr(sha1);
-                               if (flags)
-                                       *flags |= REF_ISBROKEN;
-                       }
-                       return refname;
-               }
-
-               /* Follow "normalized" - ie "refs/.." symlinks by hand */
-               if (S_ISLNK(st.st_mode)) {
-                       strbuf_reset(sb_contents);
-                       if (strbuf_readlink(sb_contents, path, 0) < 0) {
-                               if (errno == ENOENT || errno == EINVAL)
-                                       /* inconsistent with lstat; retry */
-                                       goto stat_ref;
-                               else
-                                       return NULL;
-                       }
-                       if (starts_with(sb_contents->buf, "refs/") &&
-                           !check_refname_format(sb_contents->buf, 0)) {
-                               strbuf_swap(sb_refname, sb_contents);
-                               refname = sb_refname->buf;
-                               if (flags)
-                                       *flags |= REF_ISSYMREF;
-                               if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
-                                       hashclr(sha1);
-                                       return refname;
-                               }
-                               continue;
-                       }
-               }
-
-               /* Is it a directory? */
-               if (S_ISDIR(st.st_mode)) {
-                       errno = EISDIR;
-                       return NULL;
-               }
-
-               /*
-                * Anything else, just open it and try to use it as
-                * a ref
-                */
-               fd = open(path, O_RDONLY);
-               if (fd < 0) {
-                       if (errno == ENOENT)
+       /* Follow "normalized" - ie "refs/.." symlinks by hand */
+       if (S_ISLNK(st.st_mode)) {
+               strbuf_reset(&sb_contents);
+               if (strbuf_readlink(&sb_contents, path, 0) < 0) {
+                       if (errno == ENOENT || errno == EINVAL)
                                /* inconsistent with lstat; retry */
                                goto stat_ref;
                        else
-                               return NULL;
+                               goto out;
                }
-               strbuf_reset(sb_contents);
-               if (strbuf_read(sb_contents, fd, 256) < 0) {
-                       int save_errno = errno;
-                       close(fd);
-                       errno = save_errno;
-                       return NULL;
+               if (starts_with(sb_contents.buf, "refs/") &&
+                   !check_refname_format(sb_contents.buf, 0)) {
+                       strbuf_swap(&sb_contents, referent);
+                       *type |= REF_ISSYMREF;
+                       ret = 0;
+                       goto out;
                }
-               close(fd);
-               strbuf_rtrim(sb_contents);
+       }
 
+       /* Is it a directory? */
+       if (S_ISDIR(st.st_mode)) {
                /*
-                * Is it a symbolic ref?
+                * Even though there is a directory where the loose
+                * ref is supposed to be, there could still be a
+                * packed ref:
                 */
-               if (!starts_with(sb_contents->buf, "ref:")) {
-                       /*
-                        * Please note that FETCH_HEAD has a second
-                        * line containing other data.
-                        */
-                       if (get_sha1_hex(sb_contents->buf, sha1) ||
-                           (sb_contents->buf[40] != '\0' && !isspace(sb_contents->buf[40]))) {
-                               if (flags)
-                                       *flags |= REF_ISBROKEN;
-                               errno = EINVAL;
-                               return NULL;
-                       }
-                       if (bad_name) {
-                               hashclr(sha1);
-                               if (flags)
-                                       *flags |= REF_ISBROKEN;
-                       }
-                       return refname;
+               if (resolve_missing_loose_ref(refname, sha1, type)) {
+                       errno = EISDIR;
+                       goto out;
                }
-               if (flags)
-                       *flags |= REF_ISSYMREF;
-               buf = sb_contents->buf + 4;
+               ret = 0;
+               goto out;
+       }
+
+       /*
+        * Anything else, just open it and try to use it as
+        * a ref
+        */
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               if (errno == ENOENT)
+                       /* inconsistent with lstat; retry */
+                       goto stat_ref;
+               else
+                       goto out;
+       }
+       strbuf_reset(&sb_contents);
+       if (strbuf_read(&sb_contents, fd, 256) < 0) {
+               int save_errno = errno;
+               close(fd);
+               errno = save_errno;
+               goto out;
+       }
+       close(fd);
+       strbuf_rtrim(&sb_contents);
+       buf = sb_contents.buf;
+       if (starts_with(buf, "ref:")) {
+               buf += 4;
                while (isspace(*buf))
                        buf++;
-               strbuf_reset(sb_refname);
-               strbuf_addstr(sb_refname, buf);
-               refname = sb_refname->buf;
-               if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
-                       hashclr(sha1);
-                       return refname;
-               }
-               if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
-                       if (flags)
-                               *flags |= REF_ISBROKEN;
-
-                       if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
-                           !refname_is_safe(buf)) {
-                               errno = EINVAL;
-                               return NULL;
-                       }
-                       bad_name = 1;
-               }
+
+               strbuf_reset(referent);
+               strbuf_addstr(referent, buf);
+               *type |= REF_ISSYMREF;
+               ret = 0;
+               goto out;
        }
-}
 
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
-                              unsigned char *sha1, int *flags)
-{
-       static struct strbuf sb_refname = STRBUF_INIT;
-       struct strbuf sb_contents = STRBUF_INIT;
-       struct strbuf sb_path = STRBUF_INIT;
-       const char *ret;
+       /*
+        * Please note that FETCH_HEAD has additional
+        * data after the sha.
+        */
+       if (get_sha1_hex(buf, sha1) ||
+           (buf[40] != '\0' && !isspace(buf[40]))) {
+               *type |= REF_ISBROKEN;
+               errno = EINVAL;
+               goto out;
+       }
 
-       ret = resolve_ref_1(refname, resolve_flags, sha1, flags,
-                           &sb_refname, &sb_path, &sb_contents);
+       ret = 0;
+
+out:
+       save_errno = errno;
        strbuf_release(&sb_path);
        strbuf_release(&sb_contents);
+       errno = save_errno;
        return ret;
 }
 
@@ -1727,10 +1653,13 @@ static int do_for_each_entry(struct ref_cache *refs, const char *base,
  * value, stop the iteration and return that value; otherwise, return
  * 0.
  */
-static int do_for_each_ref(struct ref_cache *refs, const char *base,
-                          each_ref_fn fn, int trim, int flags, void *cb_data)
+int do_for_each_ref(const char *submodule, const char *base,
+                   each_ref_fn fn, int trim, int flags, void *cb_data)
 {
        struct ref_entry_cb data;
+       struct ref_cache *refs;
+
+       refs = get_ref_cache(submodule);
        data.base = base;
        data.trim = trim;
        data.flags = flags;
@@ -1745,86 +1674,6 @@ static int do_for_each_ref(struct ref_cache *refs, const char *base,
        return do_for_each_entry(refs, base, do_one_ref, &data);
 }
 
-static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       struct object_id oid;
-       int flag;
-
-       if (submodule) {
-               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, oid.hash, &flag))
-               return fn("HEAD", &oid, flag, cb_data);
-
-       return 0;
-}
-
-int head_ref(each_ref_fn fn, void *cb_data)
-{
-       return do_head_ref(NULL, fn, cb_data);
-}
-
-int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return do_head_ref(submodule, fn, cb_data);
-}
-
-int for_each_ref(each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(&ref_cache, "", fn, 0, 0, cb_data);
-}
-
-int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(get_ref_cache(submodule), "", fn, 0, 0, cb_data);
-}
-
-int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(&ref_cache, prefix, fn, strlen(prefix), 0, cb_data);
-}
-
-int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
-{
-       unsigned int flag = 0;
-
-       if (broken)
-               flag = DO_FOR_EACH_INCLUDE_BROKEN;
-       return do_for_each_ref(&ref_cache, prefix, fn, 0, flag, cb_data);
-}
-
-int for_each_ref_in_submodule(const char *submodule, const char *prefix,
-               each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(get_ref_cache(submodule), prefix, fn, strlen(prefix), 0, cb_data);
-}
-
-int for_each_replace_ref(each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(&ref_cache, git_replace_ref_base, fn,
-                              strlen(git_replace_ref_base), 0, cb_data);
-}
-
-int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
-{
-       struct strbuf buf = STRBUF_INIT;
-       int ret;
-       strbuf_addf(&buf, "%srefs/", get_git_namespace());
-       ret = do_for_each_ref(&ref_cache, buf.buf, fn, 0, 0, cb_data);
-       strbuf_release(&buf);
-       return ret;
-}
-
-int for_each_rawref(each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(&ref_cache, "", fn, 0,
-                              DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
-}
-
 static void unlock_ref(struct ref_lock *lock)
 {
        /* Do not free lock->lk -- atexit() still looks at them */
@@ -1852,7 +1701,7 @@ static int verify_lock(struct ref_lock *lock,
                          lock->old_oid.hash, NULL)) {
                if (old_sha1) {
                        int save_errno = errno;
-                       strbuf_addf(err, "can't verify ref %s", lock->ref_name);
+                       strbuf_addf(err, "can't verify ref '%s'", lock->ref_name);
                        errno = save_errno;
                        return -1;
                } else {
@@ -1861,7 +1710,7 @@ static int verify_lock(struct ref_lock *lock,
                }
        }
        if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) {
-               strbuf_addf(err, "ref %s is at %s but expected %s",
+               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));
@@ -1889,7 +1738,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                                            const unsigned char *old_sha1,
                                            const struct string_list *extras,
                                            const struct string_list *skip,
-                                           unsigned int flags, int *type_p,
+                                           unsigned int flags, int *type,
                                            struct strbuf *err)
 {
        struct strbuf ref_file = STRBUF_INIT;
@@ -1897,7 +1746,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        const char *orig_refname = refname;
        struct ref_lock *lock;
        int last_errno = 0;
-       int type;
        int lflags = 0;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
        int resolve_flags = 0;
@@ -1917,7 +1765,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        }
 
        refname = resolve_ref_unsafe(refname, resolve_flags,
-                                    lock->old_oid.hash, &type);
+                                    lock->old_oid.hash, type);
        if (!refname && errno == EISDIR) {
                /*
                 * we are trying to lock foo but we used to
@@ -1935,16 +1783,14 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                        goto error_return;
                }
                refname = resolve_ref_unsafe(orig_refname, resolve_flags,
-                                            lock->old_oid.hash, &type);
+                                            lock->old_oid.hash, type);
        }
-       if (type_p)
-           *type_p = type;
        if (!refname) {
                last_errno = errno;
                if (last_errno != ENOTDIR ||
                    !verify_refname_available_dir(orig_refname, extras, skip,
                                                  get_loose_refs(&ref_cache), err))
-                       strbuf_addf(err, "unable to resolve reference %s: %s",
+                       strbuf_addf(err, "unable to resolve reference '%s': %s",
                                    orig_refname, strerror(last_errno));
 
                goto error_return;
@@ -1982,7 +1828,7 @@ 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",
+               strbuf_addf(err, "unable to create directory for '%s'",
                            ref_file.buf);
                goto error_return;
        }
@@ -2241,7 +2087,7 @@ static void prune_ref(struct ref_to_prune *r)
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_delete(transaction, r->name, r->sha1,
-                                  REF_ISPRUNING, NULL, &err) ||
+                                  REF_ISPRUNING | REF_NODEREF, NULL, &err) ||
            ref_transaction_commit(transaction, &err)) {
                ref_transaction_free(transaction);
                error("%s", err.buf);
@@ -2453,8 +2299,8 @@ static int rename_tmp_log(const char *newrefname)
 }
 
 int verify_refname_available(const char *newname,
-                            struct string_list *extras,
-                            struct string_list *skip,
+                            const struct string_list *extras,
+                            const struct string_list *skip,
                             struct strbuf *err)
 {
        struct ref_dir *packed_refs = get_packed_refs(&ref_cache);
@@ -2482,20 +2328,18 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
        struct ref_lock *lock;
        struct stat loginfo;
        int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
-       const char *symref = NULL;
        struct strbuf err = STRBUF_INIT;
 
        if (log && S_ISLNK(loginfo.st_mode))
                return error("reflog for %s is a symlink", oldrefname);
 
-       symref = resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING,
-                                   orig_sha1, &flag);
+       if (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                               orig_sha1, &flag))
+               return error("refname %s not found", oldrefname);
+
        if (flag & REF_ISSYMREF)
                return error("refname %s is a symbolic ref, renaming it is not supported",
                        oldrefname);
-       if (!symref)
-               return error("refname %s not found", oldrefname);
-
        if (!rename_ref_available(oldrefname, newrefname))
                return 1;
 
@@ -2508,8 +2352,16 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
                goto rollback;
        }
 
-       if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
-           delete_ref(newrefname, sha1, REF_NODEREF)) {
+       /*
+        * Since we are doing a shallow lookup, sha1 is not the
+        * correct value to pass to delete_ref as old_sha1. But that
+        * doesn't matter, because an old_sha1 check wouldn't add to
+        * the safety anyway; we want to delete the reference whatever
+        * its current value.
+        */
+       if (!read_ref_full(newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                          sha1, NULL) &&
+           delete_ref(newrefname, NULL, REF_NODEREF)) {
                if (errno==EISDIR) {
                        struct strbuf path = STRBUF_INIT;
                        int result;
@@ -2533,7 +2385,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 
        logmoved = log;
 
-       lock = lock_ref_sha1_basic(newrefname, NULL, NULL, NULL, 0, NULL, &err);
+       lock = lock_ref_sha1_basic(newrefname, NULL, NULL, NULL, REF_NODEREF,
+                                  NULL, &err);
        if (!lock) {
                error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf);
                strbuf_release(&err);
@@ -2551,7 +2404,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
        return 0;
 
  rollback:
-       lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, NULL, 0, NULL, &err);
+       lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, NULL, REF_NODEREF,
+                                  NULL, &err);
        if (!lock) {
                error("unable to lock %s for rollback: %s", oldrefname, err.buf);
                strbuf_release(&err);
@@ -2588,6 +2442,30 @@ static int close_ref(struct ref_lock *lock)
 
 static int commit_ref(struct ref_lock *lock)
 {
+       char *path = get_locked_file_path(lock->lk);
+       struct stat st;
+
+       if (!lstat(path, &st) && S_ISDIR(st.st_mode)) {
+               /*
+                * There is a directory at the path we want to rename
+                * the lockfile to. Hopefully it is empty; try to
+                * delete it.
+                */
+               size_t len = strlen(path);
+               struct strbuf sb_path = STRBUF_INIT;
+
+               strbuf_attach(&sb_path, path, len, len);
+
+               /*
+                * If this fails, commit_lock_file() will also fail
+                * and will report the problem.
+                */
+               remove_empty_directories(&sb_path);
+               strbuf_release(&sb_path);
+       } else {
+               free(path);
+       }
+
        if (commit_lock_file(lock->lk))
                return -1;
        return 0;
@@ -2606,7 +2484,7 @@ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct str
        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: "
+                       strbuf_addf(err, "unable to create directory for '%s': "
                                    "%s", logfile->buf, strerror(errno));
                        return -1;
                }
@@ -2620,7 +2498,7 @@ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct str
 
                if (errno == EISDIR) {
                        if (remove_empty_directories(logfile)) {
-                               strbuf_addf(err, "There are still logs under "
+                               strbuf_addf(err, "there are still logs under "
                                            "'%s'", logfile->buf);
                                return -1;
                        }
@@ -2628,7 +2506,7 @@ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct str
                }
 
                if (logfd < 0) {
-                       strbuf_addf(err, "unable to append to %s: %s",
+                       strbuf_addf(err, "unable to append to '%s': %s",
                                    logfile->buf, strerror(errno));
                        return -1;
                }
@@ -2697,13 +2575,13 @@ static int log_ref_write_1(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) {
-               strbuf_addf(err, "unable to append to %s: %s", logfile->buf,
+               strbuf_addf(err, "unable to append to '%s': %s", logfile->buf,
                            strerror(errno));
                close(logfd);
                return -1;
        }
        if (close(logfd)) {
-               strbuf_addf(err, "unable to append to %s: %s", logfile->buf,
+               strbuf_addf(err, "unable to append to '%s': %s", logfile->buf,
                            strerror(errno));
                return -1;
        }
@@ -2744,14 +2622,14 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
        o = parse_object(sha1);
        if (!o) {
                strbuf_addf(err,
-                           "Trying to write ref %s with nonexistent object %s",
+                           "trying to write ref '%s' with nonexistent object %s",
                            lock->ref_name, sha1_to_hex(sha1));
                unlock_ref(lock);
                return -1;
        }
        if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
                strbuf_addf(err,
-                           "Trying to write non-commit object %s to branch %s",
+                           "trying to write non-commit object %s to branch '%s'",
                            sha1_to_hex(sha1), lock->ref_name);
                unlock_ref(lock);
                return -1;
@@ -2761,7 +2639,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
            write_in_full(fd, &term, 1) != 1 ||
            close_ref(lock) < 0) {
                strbuf_addf(err,
-                           "Couldn't write %s", get_lock_file_path(lock->lk));
+                           "couldn't write '%s'", get_lock_file_path(lock->lk));
                unlock_ref(lock);
                return -1;
        }
@@ -2782,7 +2660,7 @@ static int commit_ref_update(struct ref_lock *lock,
            (strcmp(lock->ref_name, lock->orig_ref_name) &&
             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",
+               strbuf_addf(err, "cannot update the ref '%s': %s",
                            lock->ref_name, old_msg);
                free(old_msg);
                unlock_ref(lock);
@@ -2816,8 +2694,8 @@ static int commit_ref_update(struct ref_lock *lock,
                        }
                }
        }
-       if (commit_ref(lock)) {
-               error("Couldn't set %s", lock->ref_name);
+       if (!(flags & REF_LOG_ONLY) && commit_ref(lock)) {
+               strbuf_addf(err, "couldn't set '%s'", lock->ref_name);
                unlock_ref(lock);
                return -1;
        }
@@ -2894,6 +2772,42 @@ int create_symref(const char *refname, const char *target, const char *logmsg)
        return ret;
 }
 
+int set_worktree_head_symref(const char *gitdir, const char *target)
+{
+       static struct lock_file head_lock;
+       struct ref_lock *lock;
+       struct strbuf head_path = STRBUF_INIT;
+       const char *head_rel;
+       int ret;
+
+       strbuf_addf(&head_path, "%s/HEAD", absolute_path(gitdir));
+       if (hold_lock_file_for_update(&head_lock, head_path.buf,
+                                     LOCK_NO_DEREF) < 0) {
+               struct strbuf err = STRBUF_INIT;
+               unable_to_lock_message(head_path.buf, errno, &err);
+               error("%s", err.buf);
+               strbuf_release(&err);
+               strbuf_release(&head_path);
+               return -1;
+       }
+
+       /* head_rel will be "HEAD" for the main tree, "worktrees/wt/HEAD" for
+          linked trees */
+       head_rel = remove_leading_path(head_path.buf,
+                                      absolute_path(get_git_common_dir()));
+       /* to make use of create_symref_locked(), initialize ref_lock */
+       lock = xcalloc(1, sizeof(struct ref_lock));
+       lock->lk = &head_lock;
+       lock->ref_name = xstrdup(head_rel);
+       lock->orig_ref_name = xstrdup(head_rel);
+
+       ret = create_symref_locked(lock, head_rel, target, NULL);
+
+       unlock_ref(lock); /* will free lock */
+       strbuf_release(&head_path);
+       return ret;
+}
+
 int reflog_exists(const char *refname)
 {
        struct stat st;
@@ -3130,7 +3044,7 @@ static int ref_update_reject_duplicates(struct string_list *refnames,
        for (i = 1; i < n; i++)
                if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) {
                        strbuf_addf(err,
-                                   "Multiple updates for ref '%s' not allowed.",
+                                   "multiple updates for ref '%s' not allowed.",
                                    refnames->items[i].string);
                        return 1;
                }
@@ -3141,8 +3055,6 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                           struct strbuf *err)
 {
        int ret = 0, i;
-       int n = transaction->nr;
-       struct ref_update **updates = transaction->updates;
        struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
        struct string_list_item *ref_to_delete;
        struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
@@ -3152,14 +3064,15 @@ int ref_transaction_commit(struct ref_transaction *transaction,
        if (transaction->state != REF_TRANSACTION_OPEN)
                die("BUG: commit called for transaction that is not open");
 
-       if (!n) {
+       if (!transaction->nr) {
                transaction->state = REF_TRANSACTION_CLOSED;
                return 0;
        }
 
        /* 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);
+       for (i = 0; i < transaction->nr; i++)
+               string_list_append(&affected_refnames,
+                                  transaction->updates[i]->refname);
        string_list_sort(&affected_refnames);
        if (ref_update_reject_duplicates(&affected_refnames, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
@@ -3172,8 +3085,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
         * lockfiles, ready to be activated. Only keep one lockfile
         * open at a time to avoid running out of file descriptors.
         */
-       for (i = 0; i < n; i++) {
-               struct ref_update *update = updates[i];
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
 
                if ((update->flags & REF_HAVE_NEW) &&
                    is_null_sha1(update->new_sha1))
@@ -3199,7 +3112,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                        goto cleanup;
                }
                if ((update->flags & REF_HAVE_NEW) &&
-                   !(update->flags & REF_DELETING)) {
+                   !(update->flags & REF_DELETING) &&
+                   !(update->flags & REF_LOG_ONLY)) {
                        int overwriting_symref = ((update->type & REF_ISSYMREF) &&
                                                  (update->flags & REF_NODEREF));
 
@@ -3231,22 +3145,25 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                }
                if (!(update->flags & REF_NEEDS_COMMIT)) {
                        /*
-                        * We didn't have to write anything to the lockfile.
-                        * Close it to free up the file descriptor:
+                        * We didn't call write_ref_to_lockfile(), so
+                        * the lockfile is still open. Close it to
+                        * free up the file descriptor:
                         */
                        if (close_ref(update->lock)) {
-                               strbuf_addf(err, "Couldn't close %s.lock",
+                               strbuf_addf(err, "couldn't close '%s.lock'",
                                            update->refname);
+                               ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        }
                }
        }
 
        /* Perform updates first so live commits remain referenced */
-       for (i = 0; i < n; i++) {
-               struct ref_update *update = updates[i];
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
 
-               if (update->flags & REF_NEEDS_COMMIT) {
+               if (update->flags & REF_NEEDS_COMMIT ||
+                   update->flags & REF_LOG_ONLY) {
                        if (commit_ref_update(update->lock,
                                              update->new_sha1, update->msg,
                                              update->flags, err)) {
@@ -3262,10 +3179,11 @@ int ref_transaction_commit(struct ref_transaction *transaction,
        }
 
        /* Perform deletes now that updates are safely completed */
-       for (i = 0; i < n; i++) {
-               struct ref_update *update = updates[i];
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
 
-               if (update->flags & REF_DELETING) {
+               if (update->flags & REF_DELETING &&
+                   !(update->flags & REF_LOG_ONLY)) {
                        if (delete_ref_loose(update->lock, update->type, err)) {
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
@@ -3288,9 +3206,9 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 cleanup:
        transaction->state = REF_TRANSACTION_CLOSED;
 
-       for (i = 0; i < n; i++)
-               if (updates[i]->lock)
-                       unlock_ref(updates[i]->lock);
+       for (i = 0; i < transaction->nr; i++)
+               if (transaction->updates[i]->lock)
+                       unlock_ref(transaction->updates[i]->lock);
        string_list_clear(&refs_to_delete, 0);
        string_list_clear(&affected_refnames, 0);
        return ret;
@@ -3308,8 +3226,6 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
                                   struct strbuf *err)
 {
        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);
@@ -3318,8 +3234,9 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
                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);
+       for (i = 0; i < transaction->nr; i++)
+               string_list_append(&affected_refnames,
+                                  transaction->updates[i]->refname);
        string_list_sort(&affected_refnames);
        if (ref_update_reject_duplicates(&affected_refnames, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
@@ -3341,8 +3258,8 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
        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];
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
 
                if ((update->flags & REF_HAVE_OLD) &&
                    !is_null_sha1(update->old_sha1))
@@ -3362,8 +3279,8 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
                goto cleanup;
        }
 
-       for (i = 0; i < n; i++) {
-               struct ref_update *update = updates[i];
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
 
                if ((update->flags & REF_HAVE_NEW) &&
                    !is_null_sha1(update->new_sha1))
@@ -3445,7 +3362,8 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
         * reference itself, plus we might need to update the
         * reference if --updateref was specified:
         */
-       lock = lock_ref_sha1_basic(refname, sha1, NULL, NULL, 0, &type, &err);
+       lock = lock_ref_sha1_basic(refname, sha1, NULL, NULL, REF_NODEREF,
+                                  &type, &err);
        if (!lock) {
                error("cannot lock ref '%s': %s", refname, err.buf);
                strbuf_release(&err);