Merge branch 'mh/split-under-lock'
authorJunio C Hamano <gitster@pobox.com>
Mon, 25 Jul 2016 21:13:32 +0000 (14:13 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 25 Jul 2016 21:13:32 +0000 (14:13 -0700)
Further preparatory work on the refs API before the pluggable
backend series can land.

* mh/split-under-lock: (33 commits)
lock_ref_sha1_basic(): only handle REF_NODEREF mode
commit_ref_update(): remove the flags parameter
lock_ref_for_update(): don't resolve symrefs
lock_ref_for_update(): don't re-read non-symbolic references
refs: resolve symbolic refs first
ref_transaction_update(): check refname_is_safe() at a minimum
unlock_ref(): move definition higher in the file
lock_ref_for_update(): new function
add_update(): initialize the whole ref_update
verify_refname_available(): adjust constness in declaration
refs: don't dereference on rename
refs: allow log-only updates
delete_branches(): use resolve_refdup()
ref_transaction_commit(): correctly report close_ref() failure
ref_transaction_create(): disallow recursive pruning
refs: make error messages more consistent
lock_ref_sha1_basic(): remove unneeded local variable
read_raw_ref(): move docstring to header file
read_raw_ref(): improve docstring
read_raw_ref(): rename symref argument to referent
...

1  2 
builtin/branch.c
cache.h
dir.h
refs/files-backend.c
t/t1400-update-ref.sh
t/t3200-branch.sh
diff --combined builtin/branch.c
index bf0672578f3b1a875b7cc691a27f77e96f394909,ae5568845c2dec1b65e3812f35ccf32e2392619c..7df05437f11f76fb3a6c2c879fbb88fb63e71eac
@@@ -212,7 -212,7 +212,7 @@@ static int delete_branches(int argc, co
                        die(_("Couldn't look up commit object for HEAD"));
        }
        for (i = 0; i < argc; i++, strbuf_release(&bname)) {
-               const char *target;
+               char *target = NULL;
                int flags = 0;
  
                strbuf_branchname(&bname, argv[i]);
                name = mkpathdup(fmt, bname.buf);
  
                if (kinds == FILTER_REFS_BRANCHES) {
 -                      char *worktree = find_shared_symref("HEAD", name);
 -                      if (worktree) {
 +                      const struct worktree *wt =
 +                              find_shared_symref("HEAD", name);
 +                      if (wt) {
                                error(_("Cannot delete branch '%s' "
                                        "checked out at '%s'"),
 -                                    bname.buf, worktree);
 -                              free(worktree);
 +                                    bname.buf, wt->path);
                                ret = 1;
                                continue;
                        }
                }
  
-               target = resolve_ref_unsafe(name,
-                                           RESOLVE_REF_READING
-                                           | RESOLVE_REF_NO_RECURSE
-                                           | RESOLVE_REF_ALLOW_BAD_NAME,
-                                           sha1, &flags);
+               target = resolve_refdup(name,
+                                       RESOLVE_REF_READING
+                                       | RESOLVE_REF_NO_RECURSE
+                                       | RESOLVE_REF_ALLOW_BAD_NAME,
+                                       sha1, &flags);
                if (!target) {
                        error(remote_branch
                              ? _("remote-tracking branch '%s' not found.")
                    check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
                                        force)) {
                        ret = 1;
-                       continue;
+                       goto next;
                }
  
                if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
                              : _("Error deleting branch '%s'"),
                              bname.buf);
                        ret = 1;
-                       continue;
+                       goto next;
                }
                if (!quiet) {
                        printf(remote_branch
                               : find_unique_abbrev(sha1, DEFAULT_ABBREV));
                }
                delete_branch_config(bname.buf);
+       next:
+               free(target);
        }
  
        free(name);
@@@ -375,14 -378,12 +378,14 @@@ static char *get_head_description(void
                strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
                            state.branch);
        else if (state.detached_from) {
 -              /* TRANSLATORS: make sure these match _("HEAD detached at ")
 -                 and _("HEAD detached from ") in wt-status.c */
                if (state.detached_at)
 +                      /* TRANSLATORS: make sure this matches
 +                         "HEAD detached at " in wt-status.c */
                        strbuf_addf(&desc, _("(HEAD detached at %s)"),
                                state.detached_from);
                else
 +                      /* TRANSLATORS: make sure this matches
 +                         "HEAD detached from " in wt-status.c */
                        strbuf_addf(&desc, _("(HEAD detached from %s)"),
                                state.detached_from);
        }
@@@ -526,29 -527,6 +529,29 @@@ static void print_ref_list(struct ref_f
        ref_array_clear(&array);
  }
  
 +static void reject_rebase_or_bisect_branch(const char *target)
 +{
 +      struct worktree **worktrees = get_worktrees();
 +      int i;
 +
 +      for (i = 0; worktrees[i]; i++) {
 +              struct worktree *wt = worktrees[i];
 +
 +              if (!wt->is_detached)
 +                      continue;
 +
 +              if (is_worktree_being_rebased(wt, target))
 +                      die(_("Branch %s is being rebased at %s"),
 +                          target, wt->path);
 +
 +              if (is_worktree_being_bisected(wt, target))
 +                      die(_("Branch %s is being bisected at %s"),
 +                          target, wt->path);
 +      }
 +
 +      free_worktrees(worktrees);
 +}
 +
  static void rename_branch(const char *oldname, const char *newname, int force)
  {
        struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
  
        validate_new_branchname(newname, &newref, force, clobber_head_ok);
  
 +      reject_rebase_or_bisect_branch(oldref.buf);
 +
        strbuf_addf(&logmsg, "Branch: renamed %s to %s",
                 oldref.buf, newref.buf);
  
@@@ -614,11 -590,15 +617,11 @@@ static int edit_branch_description(cons
        if (!buf.len || buf.buf[buf.len-1] != '\n')
                strbuf_addch(&buf, '\n');
        strbuf_commented_addf(&buf,
 -                  "Please edit the description for the branch\n"
 -                  "  %s\n"
 -                  "Lines starting with '%c' will be stripped.\n",
 +                  _("Please edit the description for the branch\n"
 +                    "  %s\n"
 +                    "Lines starting with '%c' will be stripped.\n"),
                    branch_name, comment_line_char);
 -      if (write_file_gently(git_path(edit_description), "%s", buf.buf)) {
 -              strbuf_release(&buf);
 -              return error(_("could not write branch description template: %s"),
 -                           strerror(errno));
 -      }
 +      write_file_buf(git_path(edit_description), buf.buf, buf.len);
        strbuf_reset(&buf);
        if (launch_editor(git_path(edit_description), &buf, NULL)) {
                strbuf_release(&buf);
@@@ -653,7 -633,7 +656,7 @@@ int cmd_branch(int argc, const char **a
                        BRANCH_TRACK_EXPLICIT),
                OPT_SET_INT( 0, "set-upstream",  &track, N_("change upstream info"),
                        BRANCH_TRACK_OVERRIDE),
 -              OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"),
 +              OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
                OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
                OPT__COLOR(&branch_use_color, N_("use colored output")),
                OPT_SET_INT('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
                if (argc == 1 && track == BRANCH_TRACK_OVERRIDE &&
                    !branch_existed && remote_tracking) {
                        fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name);
 -                      fprintf(stderr, _("    git branch -d %s\n"), branch->name);
 -                      fprintf(stderr, _("    git branch --set-upstream-to %s\n"), branch->name);
 +                      fprintf(stderr, "    git branch -d %s\n", branch->name);
 +                      fprintf(stderr, "    git branch --set-upstream-to %s\n", branch->name);
                }
  
        } else
diff --combined cache.h
index 2bf97cc55f1170d25549b76144993acea1a807b5,4134f648eca353711764266fb4283eeb3acf8165..d559cf1234f61321a369eae6f79aa589e56de3c9
+++ b/cache.h
@@@ -367,8 -367,8 +367,8 @@@ extern void free_name_hash(struct index
  #define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
  #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
  #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
 -#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
 -#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
 +#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags), 0)
 +#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags), 0)
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
@@@ -581,8 -581,8 +581,8 @@@ extern int remove_file_from_index(struc
  #define ADD_CACHE_IGNORE_ERRORS       4
  #define ADD_CACHE_IGNORE_REMOVAL 8
  #define ADD_CACHE_INTENT 16
 -extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 -extern int add_file_to_index(struct index_state *, const char *path, int flags);
 +extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags, int force_mode);
 +extern int add_file_to_index(struct index_state *, const char *path, int flags, int force_mode);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
  extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
@@@ -654,7 -654,6 +654,7 @@@ extern int warn_on_object_refname_ambig
  extern const char *apply_default_whitespace;
  extern const char *apply_default_ignorewhitespace;
  extern const char *git_attributes_file;
 +extern const char *git_hooks_path;
  extern int zlib_compression_level;
  extern int core_compression_level;
  extern int core_compression_seen;
@@@ -701,14 -700,6 +701,14 @@@ extern int ref_paranoia
  extern char comment_line_char;
  extern int auto_comment_line_char;
  
 +/* Windows only */
 +enum hide_dotfiles_type {
 +      HIDE_DOTFILES_FALSE = 0,
 +      HIDE_DOTFILES_TRUE,
 +      HIDE_DOTFILES_DOTGITONLY
 +};
 +extern enum hide_dotfiles_type hide_dotfiles;
 +
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@@ -808,14 -799,11 +808,14 @@@ extern void check_repository_format(voi
   */
  extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
  extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 +extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
  
  extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
  extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 +extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
 +      __attribute__((format (printf, 2, 3)));
  extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
  extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
@@@ -970,6 -958,8 +970,6 @@@ static inline int is_empty_blob_sha1(co
  
  int git_mkstemp(char *path, size_t n, const char *template);
  
 -int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
 -
  /* set default permissions by passing mode arguments to open(2) */
  int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
  int git_mkstemp_mode(char *pattern, int mode);
@@@ -1003,6 -993,11 +1003,11 @@@ int adjust_shared_perm(const char *path
   * directory while we were working.  To be robust against this kind of
   * race, callers might want to try invoking the function again when it
   * returns SCLD_VANISHED.
+  *
+  * safe_create_leading_directories() temporarily changes path while it
+  * is working but restores it before returning.
+  * safe_create_leading_directories_const() doesn't modify path, even
+  * temporarily.
   */
  enum scld_error {
        SCLD_OK = 0,
@@@ -1166,8 -1161,6 +1171,8 @@@ extern int get_sha1_blob(const char *st
  extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
  extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
  
 +extern int get_oid(const char *str, struct object_id *oid);
 +
  typedef int each_abbrev_fn(const unsigned char *sha1, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
@@@ -1193,7 -1186,6 +1198,7 @@@ extern int get_oid_hex(const char *hex
   *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
   */
  extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 +extern char *oid_to_hex_r(char *out, const struct object_id *oid);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
  
@@@ -1605,16 -1597,6 +1610,16 @@@ extern const char *get_log_output_encod
  extern const char *get_commit_output_encoding(void);
  
  extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 +
 +enum config_scope {
 +      CONFIG_SCOPE_UNKNOWN = 0,
 +      CONFIG_SCOPE_SYSTEM,
 +      CONFIG_SCOPE_GLOBAL,
 +      CONFIG_SCOPE_REPO,
 +      CONFIG_SCOPE_CMDLINE,
 +};
 +
 +extern enum config_scope current_config_scope(void);
  extern const char *current_config_origin_type(void);
  extern const char *current_config_name(void);
  
@@@ -1707,8 -1689,6 +1712,8 @@@ extern int ignore_untracked_cache_confi
  struct key_value_info {
        const char *filename;
        int linenr;
 +      const char *origin_type;
 +      enum config_scope scope;
  };
  
  extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
@@@ -1734,6 -1714,7 +1739,6 @@@ extern int copy_file(const char *dst, c
  extern int copy_file_with_time(const char *dst, const char *src, int mode);
  
  extern void write_or_die(int fd, const void *buf, size_t count);
 -extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
  extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
  extern void fsync_or_die(int fd, const char *);
  
@@@ -1746,21 -1727,8 +1751,21 @@@ static inline ssize_t write_str_in_full
        return write_in_full(fd, str, strlen(str));
  }
  
 -extern int write_file(const char *path, const char *fmt, ...);
 -extern int write_file_gently(const char *path, const char *fmt, ...);
 +/**
 + * Open (and truncate) the file at path, write the contents of buf to it,
 + * and close it. Dies if any errors are encountered.
 + */
 +extern void write_file_buf(const char *path, const char *buf, size_t len);
 +
 +/**
 + * Like write_file_buf(), but format the contents into a buffer first.
 + * Additionally, write_file() will append a newline if one is not already
 + * present, making it convenient to write text files:
 + *
 + *   write_file(path, "counter: %d", ctr);
 + */
 +__attribute__((format (printf, 2, 3)))
 +extern void write_file(const char *path, const char *fmt, ...);
  
  /* pager.c */
  extern void setup_pager(void);
@@@ -1797,14 -1765,14 +1802,14 @@@ void packet_trace_identity(const char *
   * return 0 if success, 1 - if addition of a file failed and
   * ADD_FILES_IGNORE_ERRORS was specified in flags
   */
 -int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
 +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags, int force_mode);
  
  /* diff.c */
  extern int diff_auto_refresh_index;
  
  /* match-trees.c */
 -void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
 -void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
 +void shift_tree(const struct object_id *, const struct object_id *, struct object_id *, int);
 +void shift_tree_by(const struct object_id *, const struct object_id *, struct object_id *, const char *);
  
  /*
   * whitespace rules.
diff --combined dir.h
index bfde698c488adcc75b7a294476c622afb6f92b36,5f19acc272643d3de0b78ec1d4fada97b53e54f1..da1a858b3a12daba5bd348fcaa342534edaacdb6
--- 1/dir.h
--- 2/dir.h
+++ b/dir.h
@@@ -262,16 -262,40 +262,39 @@@ extern int is_empty_dir(const char *dir
  
  extern void setup_standard_excludes(struct dir_struct *dir);
  
+ /* Constants for remove_dir_recursively: */
+ /*
+  * If a non-directory is found within path, stop and return an error.
+  * (In this case some empty directories might already have been
+  * removed.)
+  */
  #define REMOVE_DIR_EMPTY_ONLY 01
+ /*
+  * If any Git work trees are found within path, skip them without
+  * considering it an error.
+  */
  #define REMOVE_DIR_KEEP_NESTED_GIT 02
+ /* Remove the contents of path, but leave path itself. */
  #define REMOVE_DIR_KEEP_TOPLEVEL 04
+ /*
+  * Remove path and its contents, recursively. flags is a combination
+  * of the above REMOVE_DIR_* constants. Return 0 on success.
+  *
+  * This function uses path as temporary scratch space, but restores it
+  * before returning.
+  */
  extern int remove_dir_recursively(struct strbuf *path, int flag);
  
  /* tries to remove the path with empty directories along it, ignores ENOENT */
  extern int remove_path(const char *path);
  
 -extern int strcmp_icase(const char *a, const char *b);
 -extern int strncmp_icase(const char *a, const char *b, size_t count);
 -extern int fnmatch_icase(const char *pattern, const char *string, int flags);
 +extern int fspathcmp(const char *a, const char *b);
 +extern int fspathncmp(const char *a, const char *b, size_t count);
  
  /*
   * The prefix part of pattern must not contains wildcards.
diff --combined refs/files-backend.c
index dac3a22297773c0854a0c19186022ee76c8a7b6b,bbf96ad83a5abc494fbbd99d2bd9cceef6252e97..4dd72b42c88e4e125ec5fa108d3e81bfc8a44483
@@@ -7,7 -7,6 +7,6 @@@
  
  struct ref_lock {
        char *ref_name;
-       char *orig_ref_name;
        struct lock_file *lk;
        struct object_id old_oid;
  };
@@@ -1388,38 -1387,8 +1387,8 @@@ static int resolve_missing_loose_ref(co
        return -1;
  }
  
- /*
-  * Read a raw ref from the filesystem or packed refs file.
-  *
-  * If the ref is a sha1, fill in sha1 and return 0.
-  *
-  * If the ref is symbolic, fill in *symref with the referrent
-  * (e.g. "refs/heads/master") and return 0.  The caller is responsible
-  * for validating the referrent.  Set REF_ISSYMREF in flags.
-  *
-  * If the ref doesn't exist, set errno to ENOENT and return -1.
-  *
-  * If the ref exists but is neither a symbolic ref nor a sha1, it is
-  * broken. Set REF_ISBROKEN in flags, set errno to EINVAL, and return
-  * -1.
-  *
-  * If there is another error reading the ref, set errno appropriately and
-  * return -1.
-  *
-  * Backend-specific flags might be set in flags as well, regardless of
-  * outcome.
-  *
-  * sb_path is workspace: the caller should allocate and free it.
-  *
-  * It is OK for refname to point into symref. In this case:
-  * - if the function succeeds with REF_ISSYMREF, symref will be
-  *   overwritten and the memory pointed to by refname might be changed
-  *   or even freed.
-  * - in all other cases, symref will be untouched, and therefore
-  *   refname will still be valid and unchanged.
-  */
  int read_raw_ref(const char *refname, unsigned char *sha1,
-                struct strbuf *symref, unsigned int *flags)
+                struct strbuf *referent, unsigned int *type)
  {
        struct strbuf sb_contents = STRBUF_INIT;
        struct strbuf sb_path = STRBUF_INIT;
        int ret = -1;
        int save_errno;
  
+       *type = 0;
        strbuf_reset(&sb_path);
        strbuf_git_path(&sb_path, "%s", refname);
        path = sb_path.buf;
@@@ -1448,7 -1418,7 +1418,7 @@@ stat_ref
        if (lstat(path, &st) < 0) {
                if (errno != ENOENT)
                        goto out;
-               if (resolve_missing_loose_ref(refname, sha1, flags)) {
+               if (resolve_missing_loose_ref(refname, sha1, type)) {
                        errno = ENOENT;
                        goto out;
                }
                }
                if (starts_with(sb_contents.buf, "refs/") &&
                    !check_refname_format(sb_contents.buf, 0)) {
-                       strbuf_swap(&sb_contents, symref);
-                       *flags |= REF_ISSYMREF;
+                       strbuf_swap(&sb_contents, referent);
+                       *type |= REF_ISSYMREF;
                        ret = 0;
                        goto out;
                }
  
        /* Is it a directory? */
        if (S_ISDIR(st.st_mode)) {
-               errno = EISDIR;
+               /*
+                * Even though there is a directory where the loose
+                * ref is supposed to be, there could still be a
+                * packed ref:
+                */
+               if (resolve_missing_loose_ref(refname, sha1, type)) {
+                       errno = EISDIR;
+                       goto out;
+               }
+               ret = 0;
                goto out;
        }
  
                while (isspace(*buf))
                        buf++;
  
-               strbuf_reset(symref);
-               strbuf_addstr(symref, buf);
-               *flags |= REF_ISSYMREF;
+               strbuf_reset(referent);
+               strbuf_addstr(referent, buf);
+               *type |= REF_ISSYMREF;
                ret = 0;
                goto out;
        }
         */
        if (get_sha1_hex(buf, sha1) ||
            (buf[40] != '\0' && !isspace(buf[40]))) {
-               *flags |= REF_ISBROKEN;
+               *type |= REF_ISBROKEN;
                errno = EINVAL;
                goto out;
        }
        return ret;
  }
  
+ static void unlock_ref(struct ref_lock *lock)
+ {
+       /* Do not free lock->lk -- atexit() still looks at them */
+       if (lock->lk)
+               rollback_lock_file(lock->lk);
+       free(lock->ref_name);
+       free(lock);
+ }
+ /*
+  * Lock refname, without following symrefs, and set *lock_p to point
+  * at a newly-allocated lock object. Fill in lock->old_oid, referent,
+  * and type similarly to read_raw_ref().
+  *
+  * The caller must verify that refname is a "safe" reference name (in
+  * the sense of refname_is_safe()) before calling this function.
+  *
+  * If the reference doesn't already exist, verify that refname doesn't
+  * have a D/F conflict with any existing references. extras and skip
+  * are passed to verify_refname_available_dir() for this check.
+  *
+  * If mustexist is not set and the reference is not found or is
+  * broken, lock the reference anyway but clear sha1.
+  *
+  * Return 0 on success. On failure, write an error message to err and
+  * return TRANSACTION_NAME_CONFLICT or TRANSACTION_GENERIC_ERROR.
+  *
+  * Implementation note: This function is basically
+  *
+  *     lock reference
+  *     read_raw_ref()
+  *
+  * but it includes a lot more code to
+  * - Deal with possible races with other processes
+  * - Avoid calling verify_refname_available_dir() when it can be
+  *   avoided, namely if we were successfully able to read the ref
+  * - Generate informative error messages in the case of failure
+  */
+ static int lock_raw_ref(const char *refname, int mustexist,
+                       const struct string_list *extras,
+                       const struct string_list *skip,
+                       struct ref_lock **lock_p,
+                       struct strbuf *referent,
+                       unsigned int *type,
+                       struct strbuf *err)
+ {
+       struct ref_lock *lock;
+       struct strbuf ref_file = STRBUF_INIT;
+       int attempts_remaining = 3;
+       int ret = TRANSACTION_GENERIC_ERROR;
+       assert(err);
+       *type = 0;
+       /* First lock the file so it can't change out from under us. */
+       *lock_p = lock = xcalloc(1, sizeof(*lock));
+       lock->ref_name = xstrdup(refname);
+       strbuf_git_path(&ref_file, "%s", refname);
+ retry:
+       switch (safe_create_leading_directories(ref_file.buf)) {
+       case SCLD_OK:
+               break; /* success */
+       case SCLD_EXISTS:
+               /*
+                * Suppose refname is "refs/foo/bar". We just failed
+                * to create the containing directory, "refs/foo",
+                * because there was a non-directory in the way. This
+                * indicates a D/F conflict, probably because of
+                * another reference such as "refs/foo". There is no
+                * reason to expect this error to be transitory.
+                */
+               if (verify_refname_available(refname, extras, skip, err)) {
+                       if (mustexist) {
+                               /*
+                                * To the user the relevant error is
+                                * that the "mustexist" reference is
+                                * missing:
+                                */
+                               strbuf_reset(err);
+                               strbuf_addf(err, "unable to resolve reference '%s'",
+                                           refname);
+                       } else {
+                               /*
+                                * The error message set by
+                                * verify_refname_available_dir() is OK.
+                                */
+                               ret = TRANSACTION_NAME_CONFLICT;
+                       }
+               } else {
+                       /*
+                        * The file that is in the way isn't a loose
+                        * reference. Report it as a low-level
+                        * failure.
+                        */
+                       strbuf_addf(err, "unable to create lock file %s.lock; "
+                                   "non-directory in the way",
+                                   ref_file.buf);
+               }
+               goto error_return;
+       case SCLD_VANISHED:
+               /* Maybe another process was tidying up. Try again. */
+               if (--attempts_remaining > 0)
+                       goto retry;
+               /* fall through */
+       default:
+               strbuf_addf(err, "unable to create directory for %s",
+                           ref_file.buf);
+               goto error_return;
+       }
+       if (!lock->lk)
+               lock->lk = xcalloc(1, sizeof(struct lock_file));
+       if (hold_lock_file_for_update(lock->lk, ref_file.buf, LOCK_NO_DEREF) < 0) {
+               if (errno == ENOENT && --attempts_remaining > 0) {
+                       /*
+                        * Maybe somebody just deleted one of the
+                        * directories leading to ref_file.  Try
+                        * again:
+                        */
+                       goto retry;
+               } else {
+                       unable_to_lock_message(ref_file.buf, errno, err);
+                       goto error_return;
+               }
+       }
+       /*
+        * Now we hold the lock and can read the reference without
+        * fear that its value will change.
+        */
+       if (read_raw_ref(refname, lock->old_oid.hash, referent, type)) {
+               if (errno == ENOENT) {
+                       if (mustexist) {
+                               /* Garden variety missing reference. */
+                               strbuf_addf(err, "unable to resolve reference '%s'",
+                                           refname);
+                               goto error_return;
+                       } else {
+                               /*
+                                * Reference is missing, but that's OK. We
+                                * know that there is not a conflict with
+                                * another loose reference because
+                                * (supposing that we are trying to lock
+                                * reference "refs/foo/bar"):
+                                *
+                                * - We were successfully able to create
+                                *   the lockfile refs/foo/bar.lock, so we
+                                *   know there cannot be a loose reference
+                                *   named "refs/foo".
+                                *
+                                * - We got ENOENT and not EISDIR, so we
+                                *   know that there cannot be a loose
+                                *   reference named "refs/foo/bar/baz".
+                                */
+                       }
+               } else if (errno == EISDIR) {
+                       /*
+                        * There is a directory in the way. It might have
+                        * contained references that have been deleted. If
+                        * we don't require that the reference already
+                        * exists, try to remove the directory so that it
+                        * doesn't cause trouble when we want to rename the
+                        * lockfile into place later.
+                        */
+                       if (mustexist) {
+                               /* Garden variety missing reference. */
+                               strbuf_addf(err, "unable to resolve reference '%s'",
+                                           refname);
+                               goto error_return;
+                       } else if (remove_dir_recursively(&ref_file,
+                                                         REMOVE_DIR_EMPTY_ONLY)) {
+                               if (verify_refname_available_dir(
+                                                   refname, extras, skip,
+                                                   get_loose_refs(&ref_cache),
+                                                   err)) {
+                                       /*
+                                        * The error message set by
+                                        * verify_refname_available() is OK.
+                                        */
+                                       ret = TRANSACTION_NAME_CONFLICT;
+                                       goto error_return;
+                               } else {
+                                       /*
+                                        * We can't delete the directory,
+                                        * but we also don't know of any
+                                        * references that it should
+                                        * contain.
+                                        */
+                                       strbuf_addf(err, "there is a non-empty directory '%s' "
+                                                   "blocking reference '%s'",
+                                                   ref_file.buf, refname);
+                                       goto error_return;
+                               }
+                       }
+               } else if (errno == EINVAL && (*type & REF_ISBROKEN)) {
+                       strbuf_addf(err, "unable to resolve reference '%s': "
+                                   "reference broken", refname);
+                       goto error_return;
+               } else {
+                       strbuf_addf(err, "unable to resolve reference '%s': %s",
+                                   refname, strerror(errno));
+                       goto error_return;
+               }
+               /*
+                * If the ref did not exist and we are creating it,
+                * make sure there is no existing packed ref whose
+                * name begins with our refname, nor a packed ref
+                * whose name is a proper prefix of our refname.
+                */
+               if (verify_refname_available_dir(
+                                   refname, extras, skip,
+                                   get_packed_refs(&ref_cache),
+                                   err)) {
+                       goto error_return;
+               }
+       }
+       ret = 0;
+       goto out;
+ error_return:
+       unlock_ref(lock);
+       *lock_p = NULL;
+ out:
+       strbuf_release(&ref_file);
+       return ret;
+ }
  /*
   * Peel the entry (if possible) and return its new peel_status.  If
   * repeel is true, re-peel the entry even if there is an old peeled
@@@ -1694,16 -1908,6 +1908,6 @@@ int do_for_each_ref(const char *submodu
        return do_for_each_entry(refs, base, do_one_ref, &data);
  }
  
- static void unlock_ref(struct ref_lock *lock)
- {
-       /* Do not free lock->lk -- atexit() still looks at them */
-       if (lock->lk)
-               rollback_lock_file(lock->lk);
-       free(lock->ref_name);
-       free(lock->orig_ref_name);
-       free(lock);
- }
  /*
   * 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
@@@ -1721,18 -1925,18 +1925,18 @@@ static int verify_lock(struct ref_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 {
 -                      hashclr(lock->old_oid.hash);
 +                      oidclr(&lock->old_oid);
                        return 0;
                }
        }
        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),
 +                          oid_to_hex(&lock->old_oid),
                            sha1_to_hex(old_sha1));
                errno = EBUSY;
                return -1;
@@@ -1758,19 -1962,17 +1962,17 @@@ static struct ref_lock *lock_ref_sha1_b
                                            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;
-       struct strbuf orig_ref_file = STRBUF_INIT;
-       const char *orig_refname = refname;
        struct ref_lock *lock;
        int last_errno = 0;
-       int type;
-       int lflags = 0;
+       int lflags = LOCK_NO_DEREF;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
-       int resolve_flags = 0;
+       int resolve_flags = RESOLVE_REF_NO_RECURSE;
        int attempts_remaining = 3;
+       int resolved;
  
        assert(err);
  
                resolve_flags |= RESOLVE_REF_READING;
        if (flags & REF_DELETING)
                resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
-       if (flags & REF_NODEREF) {
-               resolve_flags |= RESOLVE_REF_NO_RECURSE;
-               lflags |= LOCK_NO_DEREF;
-       }
  
-       refname = resolve_ref_unsafe(refname, resolve_flags,
-                                    lock->old_oid.hash, &type);
-       if (!refname && errno == EISDIR) {
+       strbuf_git_path(&ref_file, "%s", refname);
+       resolved = !!resolve_ref_unsafe(refname, resolve_flags,
+                                       lock->old_oid.hash, type);
+       if (!resolved && errno == EISDIR) {
                /*
                 * we are trying to lock foo but we used to
                 * have foo/bar which now does not exist;
                 * it is normal for the empty directory 'foo'
                 * to remain.
                 */
-               strbuf_git_path(&orig_ref_file, "%s", orig_refname);
-               if (remove_empty_directories(&orig_ref_file)) {
+               if (remove_empty_directories(&ref_file)) {
                        last_errno = errno;
-                       if (!verify_refname_available_dir(orig_refname, extras, skip,
+                       if (!verify_refname_available_dir(refname, extras, skip,
                                                          get_loose_refs(&ref_cache), err))
                                strbuf_addf(err, "there are still refs under '%s'",
-                                           orig_refname);
+                                           refname);
                        goto error_return;
                }
-               refname = resolve_ref_unsafe(orig_refname, resolve_flags,
-                                            lock->old_oid.hash, &type);
+               resolved = !!resolve_ref_unsafe(refname, resolve_flags,
+                                               lock->old_oid.hash, type);
        }
-       if (type_p)
-           *type_p = type;
-       if (!refname) {
+       if (!resolved) {
                last_errno = errno;
                if (last_errno != ENOTDIR ||
-                   !verify_refname_available_dir(orig_refname, extras, skip,
+                   !verify_refname_available_dir(refname, extras, skip,
                                                  get_loose_refs(&ref_cache), err))
-                       strbuf_addf(err, "unable to resolve reference %s: %s",
-                                   orig_refname, strerror(last_errno));
+                       strbuf_addf(err, "unable to resolve reference '%s': %s",
+                                   refname, strerror(last_errno));
  
                goto error_return;
        }
  
-       if (flags & REF_NODEREF)
-               refname = orig_refname;
        /*
         * If the ref did not exist and we are creating it, make sure
         * there is no existing packed ref whose name begins with our
        lock->lk = xcalloc(1, sizeof(struct lock_file));
  
        lock->ref_name = xstrdup(refname);
-       lock->orig_ref_name = xstrdup(orig_refname);
-       strbuf_git_path(&ref_file, "%s", refname);
  
   retry:
        switch (safe_create_leading_directories_const(ref_file.buf)) {
                /* 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;
        }
  
   out:
        strbuf_release(&ref_file);
-       strbuf_release(&orig_ref_file);
        errno = last_errno;
        return lock;
  }
@@@ -2110,7 -2300,7 +2300,7 @@@ static void prune_ref(struct ref_to_pru
        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);
@@@ -2322,8 -2512,8 +2512,8 @@@ out
  }
  
  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);
@@@ -2342,7 -2532,7 +2532,7 @@@ static int write_ref_to_lockfile(struc
                                 const unsigned char *sha1, struct strbuf *err);
  static int commit_ref_update(struct ref_lock *lock,
                             const unsigned char *sha1, const char *logmsg,
-                            int flags, struct strbuf *err);
+                            struct strbuf *err);
  
  int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
  {
        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;
  
                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;
  
        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);
        hashcpy(lock->old_oid.hash, orig_sha1);
  
        if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
-           commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) {
+           commit_ref_update(lock, orig_sha1, logmsg, &err)) {
                error("unable to write current sha1 into %s: %s", newrefname, err.buf);
                strbuf_release(&err);
                goto rollback;
        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);
        flag = log_all_ref_updates;
        log_all_ref_updates = 0;
        if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
-           commit_ref_update(lock, orig_sha1, NULL, 0, &err)) {
+           commit_ref_update(lock, orig_sha1, NULL, &err)) {
                error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
                strbuf_release(&err);
        }
@@@ -2457,6 -2655,30 +2655,30 @@@ static int close_ref(struct ref_lock *l
  
  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;
@@@ -2475,7 -2697,7 +2697,7 @@@ static int log_ref_setup(const char *re
        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;
                }
  
                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;
                        }
                }
  
                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;
                }
@@@ -2566,13 -2788,13 +2788,13 @@@ static int log_ref_write_1(const char *
        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;
        }
@@@ -2613,14 -2835,14 +2835,14 @@@ static int write_ref_to_lockfile(struc
        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;
            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;
        }
   */
  static int commit_ref_update(struct ref_lock *lock,
                             const unsigned char *sha1, const char *logmsg,
-                            int flags, struct strbuf *err)
+                            struct strbuf *err)
  {
        clear_loose_ref_cache(&ref_cache);
-       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_oid.hash, sha1, logmsg, flags, err) < 0)) {
+       if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {
                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);
                return -1;
        }
-       if (strcmp(lock->orig_ref_name, "HEAD") != 0) {
+       if (strcmp(lock->ref_name, "HEAD") != 0) {
                /*
                 * Special hack: If a branch is updated directly and HEAD
                 * points to it (may happen on the remote side of a push
                unsigned char head_sha1[20];
                int head_flag;
                const char *head_ref;
                head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
                                              head_sha1, &head_flag);
                if (head_ref && (head_flag & REF_ISSYMREF) &&
                        }
                }
        }
        if (commit_ref(lock)) {
-               error("Couldn't set %s", lock->ref_name);
+               strbuf_addf(err, "couldn't set '%s'", lock->ref_name);
                unlock_ref(lock);
                return -1;
        }
@@@ -2790,7 -3013,6 +3013,6 @@@ int set_worktree_head_symref(const cha
        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);
  
@@@ -3035,142 -3257,427 +3257,427 @@@ static int ref_update_reject_duplicates
        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;
                }
        return 0;
  }
  
+ /*
+  * If update is a direct update of head_ref (the reference pointed to
+  * by HEAD), then add an extra REF_LOG_ONLY update for HEAD.
+  */
+ static int split_head_update(struct ref_update *update,
+                            struct ref_transaction *transaction,
+                            const char *head_ref,
+                            struct string_list *affected_refnames,
+                            struct strbuf *err)
+ {
+       struct string_list_item *item;
+       struct ref_update *new_update;
+       if ((update->flags & REF_LOG_ONLY) ||
+           (update->flags & REF_ISPRUNING) ||
+           (update->flags & REF_UPDATE_VIA_HEAD))
+               return 0;
+       if (strcmp(update->refname, head_ref))
+               return 0;
+       /*
+        * First make sure that HEAD is not already in the
+        * transaction. This insertion is O(N) in the transaction
+        * size, but it happens at most once per transaction.
+        */
+       item = string_list_insert(affected_refnames, "HEAD");
+       if (item->util) {
+               /* An entry already existed */
+               strbuf_addf(err,
+                           "multiple updates for 'HEAD' (including one "
+                           "via its referent '%s') are not allowed",
+                           update->refname);
+               return TRANSACTION_NAME_CONFLICT;
+       }
+       new_update = ref_transaction_add_update(
+                       transaction, "HEAD",
+                       update->flags | REF_LOG_ONLY | REF_NODEREF,
+                       update->new_sha1, update->old_sha1,
+                       update->msg);
+       item->util = new_update;
+       return 0;
+ }
+ /*
+  * update is for a symref that points at referent and doesn't have
+  * REF_NODEREF set. Split it into two updates:
+  * - The original update, but with REF_LOG_ONLY and REF_NODEREF set
+  * - A new, separate update for the referent reference
+  * Note that the new update will itself be subject to splitting when
+  * the iteration gets to it.
+  */
+ static int split_symref_update(struct ref_update *update,
+                              const char *referent,
+                              struct ref_transaction *transaction,
+                              struct string_list *affected_refnames,
+                              struct strbuf *err)
+ {
+       struct string_list_item *item;
+       struct ref_update *new_update;
+       unsigned int new_flags;
+       /*
+        * First make sure that referent is not already in the
+        * transaction. This insertion is O(N) in the transaction
+        * size, but it happens at most once per symref in a
+        * transaction.
+        */
+       item = string_list_insert(affected_refnames, referent);
+       if (item->util) {
+               /* An entry already existed */
+               strbuf_addf(err,
+                           "multiple updates for '%s' (including one "
+                           "via symref '%s') are not allowed",
+                           referent, update->refname);
+               return TRANSACTION_NAME_CONFLICT;
+       }
+       new_flags = update->flags;
+       if (!strcmp(update->refname, "HEAD")) {
+               /*
+                * Record that the new update came via HEAD, so that
+                * when we process it, split_head_update() doesn't try
+                * to add another reflog update for HEAD. Note that
+                * this bit will be propagated if the new_update
+                * itself needs to be split.
+                */
+               new_flags |= REF_UPDATE_VIA_HEAD;
+       }
+       new_update = ref_transaction_add_update(
+                       transaction, referent, new_flags,
+                       update->new_sha1, update->old_sha1,
+                       update->msg);
+       new_update->parent_update = update;
+       /*
+        * Change the symbolic ref update to log only. Also, it
+        * doesn't need to check its old SHA-1 value, as that will be
+        * done when new_update is processed.
+        */
+       update->flags |= REF_LOG_ONLY | REF_NODEREF;
+       update->flags &= ~REF_HAVE_OLD;
+       item->util = new_update;
+       return 0;
+ }
+ /*
+  * Return the refname under which update was originally requested.
+  */
+ static const char *original_update_refname(struct ref_update *update)
+ {
+       while (update->parent_update)
+               update = update->parent_update;
+       return update->refname;
+ }
+ /*
+  * Prepare for carrying out update:
+  * - Lock the reference referred to by update.
+  * - Read the reference under lock.
+  * - Check that its old SHA-1 value (if specified) is correct, and in
+  *   any case record it in update->lock->old_oid for later use when
+  *   writing the reflog.
+  * - If it is a symref update without REF_NODEREF, split it up into a
+  *   REF_LOG_ONLY update of the symref and add a separate update for
+  *   the referent to transaction.
+  * - If it is an update of head_ref, add a corresponding REF_LOG_ONLY
+  *   update of HEAD.
+  */
+ static int lock_ref_for_update(struct ref_update *update,
+                              struct ref_transaction *transaction,
+                              const char *head_ref,
+                              struct string_list *affected_refnames,
+                              struct strbuf *err)
+ {
+       struct strbuf referent = STRBUF_INIT;
+       int mustexist = (update->flags & REF_HAVE_OLD) &&
+               !is_null_sha1(update->old_sha1);
+       int ret;
+       struct ref_lock *lock;
+       if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))
+               update->flags |= REF_DELETING;
+       if (head_ref) {
+               ret = split_head_update(update, transaction, head_ref,
+                                       affected_refnames, err);
+               if (ret)
+                       return ret;
+       }
+       ret = lock_raw_ref(update->refname, mustexist,
+                          affected_refnames, NULL,
+                          &update->lock, &referent,
+                          &update->type, err);
+       if (ret) {
+               char *reason;
+               reason = strbuf_detach(err, NULL);
+               strbuf_addf(err, "cannot lock ref '%s': %s",
+                           update->refname, reason);
+               free(reason);
+               return ret;
+       }
+       lock = update->lock;
+       if (update->type & REF_ISSYMREF) {
+               if (update->flags & REF_NODEREF) {
+                       /*
+                        * We won't be reading the referent as part of
+                        * the transaction, so we have to read it here
+                        * to record and possibly check old_sha1:
+                        */
+                       if (read_ref_full(update->refname,
+                                         mustexist ? RESOLVE_REF_READING : 0,
+                                         lock->old_oid.hash, NULL)) {
+                               if (update->flags & REF_HAVE_OLD) {
+                                       strbuf_addf(err, "cannot lock ref '%s': "
+                                                   "can't resolve old value",
+                                                   update->refname);
+                                       return TRANSACTION_GENERIC_ERROR;
+                               } else {
+                                       hashclr(lock->old_oid.hash);
+                               }
+                       }
+                       if ((update->flags & REF_HAVE_OLD) &&
+                           hashcmp(lock->old_oid.hash, update->old_sha1)) {
+                               strbuf_addf(err, "cannot lock ref '%s': "
+                                           "is at %s but expected %s",
+                                           update->refname,
+                                           sha1_to_hex(lock->old_oid.hash),
+                                           sha1_to_hex(update->old_sha1));
+                               return TRANSACTION_GENERIC_ERROR;
+                       }
+               } else {
+                       /*
+                        * Create a new update for the reference this
+                        * symref is pointing at. Also, we will record
+                        * and verify old_sha1 for this update as part
+                        * of processing the split-off update, so we
+                        * don't have to do it here.
+                        */
+                       ret = split_symref_update(update, referent.buf, transaction,
+                                                 affected_refnames, err);
+                       if (ret)
+                               return ret;
+               }
+       } else {
+               struct ref_update *parent_update;
+               /*
+                * If this update is happening indirectly because of a
+                * symref update, record the old SHA-1 in the parent
+                * update:
+                */
+               for (parent_update = update->parent_update;
+                    parent_update;
+                    parent_update = parent_update->parent_update) {
+                       oidcpy(&parent_update->lock->old_oid, &lock->old_oid);
+               }
+               if ((update->flags & REF_HAVE_OLD) &&
+                   hashcmp(lock->old_oid.hash, update->old_sha1)) {
+                       if (is_null_sha1(update->old_sha1))
+                               strbuf_addf(err, "cannot lock ref '%s': reference already exists",
+                                           original_update_refname(update));
+                       else
+                               strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s",
+                                           original_update_refname(update),
+                                           sha1_to_hex(lock->old_oid.hash),
+                                           sha1_to_hex(update->old_sha1));
+                       return TRANSACTION_GENERIC_ERROR;
+               }
+       }
+       if ((update->flags & REF_HAVE_NEW) &&
+           !(update->flags & REF_DELETING) &&
+           !(update->flags & REF_LOG_ONLY)) {
+               if (!(update->type & REF_ISSYMREF) &&
+                   !hashcmp(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(lock, 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': %s",
+                                   update->refname, write_err);
+                       free(write_err);
+                       return TRANSACTION_GENERIC_ERROR;
+               } else {
+                       update->flags |= REF_NEEDS_COMMIT;
+               }
+       }
+       if (!(update->flags & REF_NEEDS_COMMIT)) {
+               /*
+                * 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(lock)) {
+                       strbuf_addf(err, "couldn't close '%s.lock'",
+                                   update->refname);
+                       return TRANSACTION_GENERIC_ERROR;
+               }
+       }
+       return 0;
+ }
  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;
+       char *head_ref = NULL;
+       int head_type;
+       struct object_id head_oid;
  
        assert(err);
  
        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);
+       /*
+        * Fail if a refname appears more than once in the
+        * transaction. (If we end up splitting up any updates using
+        * split_symref_update() or split_head_update(), those
+        * functions will check that the new updates don't have the
+        * same refname as any existing ones.)
+        */
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+               struct string_list_item *item =
+                       string_list_append(&affected_refnames, update->refname);
+               /*
+                * We store a pointer to update in item->util, but at
+                * the moment we never use the value of this field
+                * except to check whether it is non-NULL.
+                */
+               item->util = update;
+       }
        string_list_sort(&affected_refnames);
        if (ref_update_reject_duplicates(&affected_refnames, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
  
+       /*
+        * Special hack: If a branch is updated directly and HEAD
+        * points to it (may happen on the remote side of a push
+        * for example) then logically the HEAD reflog should be
+        * updated too.
+        *
+        * A generic solution would require reverse symref lookups,
+        * but finding all symrefs pointing to a given branch would be
+        * rather costly for this rare event (the direct update of a
+        * branch) to be worth it. So let's cheat and check with HEAD
+        * only, which should cover 99% of all usage scenarios (even
+        * 100% of the default ones).
+        *
+        * So if HEAD is a symbolic reference, then record the name of
+        * the reference that it points to. If we see an update of
+        * head_ref within the transaction, then split_head_update()
+        * arranges for the reflog of HEAD to be updated, too.
+        */
+       head_ref = resolve_refdup("HEAD", RESOLVE_REF_NO_RECURSE,
+                                 head_oid.hash, &head_type);
+       if (head_ref && !(head_type & REF_ISSYMREF)) {
+               free(head_ref);
+               head_ref = NULL;
+       }
        /*
         * Acquire all locks, verify old values if provided, check
         * that new values are valid, and write new values to the
         * 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))
-                       update->flags |= REF_DELETING;
-               update->lock = lock_ref_sha1_basic(
-                               update->refname,
-                               ((update->flags & REF_HAVE_OLD) ?
-                                update->old_sha1 : NULL),
-                               &affected_refnames, NULL,
-                               update->flags,
-                               &update->type,
-                               err);
-               if (!update->lock) {
-                       char *reason;
-                       ret = (errno == ENOTDIR)
-                               ? TRANSACTION_NAME_CONFLICT
-                               : TRANSACTION_GENERIC_ERROR;
-                       reason = strbuf_detach(err, NULL);
-                       strbuf_addf(err, "cannot lock ref '%s': %s",
-                                   update->refname, reason);
-                       free(reason);
+               ret = lock_ref_for_update(update, transaction, head_ref,
+                                         &affected_refnames, err);
+               if (ret)
                        goto cleanup;
-               }
-               if ((update->flags & REF_HAVE_NEW) &&
-                   !(update->flags & REF_DELETING)) {
-                       int overwriting_symref = ((update->type & REF_ISSYMREF) &&
-                                                 (update->flags & REF_NODEREF));
-                       if (!overwriting_symref &&
-                           !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,
-                                                        err)) {
-                               char *write_err = strbuf_detach(err, NULL);
+       }
  
-                               /*
-                                * The lock was freed upon failure of
-                                * write_ref_to_lockfile():
-                                */
+       /* Perform updates first so live commits remain referenced */
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+               struct ref_lock *lock = update->lock;
+               if (update->flags & REF_NEEDS_COMMIT ||
+                   update->flags & REF_LOG_ONLY) {
+                       if (log_ref_write(lock->ref_name, lock->old_oid.hash,
+                                         update->new_sha1,
+                                         update->msg, update->flags, err)) {
+                               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);
                                update->lock = NULL;
-                               strbuf_addf(err,
-                                           "cannot update the ref '%s': %s",
-                                           update->refname, write_err);
-                               free(write_err);
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
-                       } else {
-                               update->flags |= REF_NEEDS_COMMIT;
-                       }
-               }
-               if (!(update->flags & REF_NEEDS_COMMIT)) {
-                       /*
-                        * We didn't have to write anything to the lockfile.
-                        * Close it to free up the file descriptor:
-                        */
-                       if (close_ref(update->lock)) {
-                               strbuf_addf(err, "Couldn't close %s.lock",
-                                           update->refname);
-                               goto cleanup;
                        }
                }
-       }
-       /* Perform updates first so live commits remain referenced */
-       for (i = 0; i < n; i++) {
-               struct ref_update *update = updates[i];
                if (update->flags & REF_NEEDS_COMMIT) {
-                       if (commit_ref_update(update->lock,
-                                             update->new_sha1, update->msg,
-                                             update->flags, err)) {
-                               /* freed by commit_ref_update(): */
+                       clear_loose_ref_cache(&ref_cache);
+                       if (commit_ref(lock)) {
+                               strbuf_addf(err, "couldn't set '%s'", lock->ref_name);
+                               unlock_ref(lock);
                                update->lock = NULL;
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
-                       } else {
-                               /* freed by commit_ref_update(): */
-                               update->lock = NULL;
                        }
                }
        }
        /* 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;
  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);
+       free(head_ref);
        string_list_clear(&affected_refnames, 0);
        return ret;
  }
  
@@@ -3213,8 -3722,6 +3722,6 @@@ int initial_ref_transaction_commit(stru
                                   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);
                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;
        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))
                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))
diff --combined t/t1400-update-ref.sh
index 75fa6548c4baeae4192402817ee2264d2f3cb5a9,d226930412b98d56eb3e6e519ce8226c29586abd..d4fb9770600df75d3162ffbc6c567b1ae152fccd
@@@ -23,7 -23,7 +23,7 @@@ test_expect_success setup 
  m=refs/heads/master
  n_dir=refs/heads/gu
  n=$n_dir/fixes
- outside=foo
+ outside=refs/foo
  
  test_expect_success \
        "create $m" \
@@@ -361,7 -361,7 +361,7 @@@ test_expect_success 'stdin test setup' 
  
  test_expect_success '-z fails without --stdin' '
        test_must_fail git update-ref -z $m $m $m 2>err &&
 -      grep "usage: git update-ref" err
 +      test_i18ngrep "usage: git update-ref" err
  '
  
  test_expect_success 'stdin works with no input' '
@@@ -479,7 -479,7 +479,7 @@@ test_expect_success 'stdin fails with d
        create $a $m
        EOF
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err
+       grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed." err
  '
  
  test_expect_success 'stdin create ref works' '
@@@ -880,7 -880,7 +880,7 @@@ test_expect_success 'stdin -z fails opt
  test_expect_success 'stdin -z fails with duplicate refs' '
        printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
-       grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err
+       grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed." err
  '
  
  test_expect_success 'stdin -z create ref works' '
@@@ -1102,6 -1102,41 +1102,41 @@@ test_expect_success 'stdin -z delete re
        test_must_fail git rev-parse --verify -q $c
  '
  
+ test_expect_success 'fails with duplicate HEAD update' '
+       git branch target1 $A &&
+       git checkout target1 &&
+       cat >stdin <<-EOF &&
+       update refs/heads/target1 $C
+       option no-deref
+       update HEAD $B
+       EOF
+       test_must_fail git update-ref --stdin <stdin 2>err &&
+       grep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err &&
+       echo "refs/heads/target1" >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
+       echo "$A" >expect &&
+       git rev-parse refs/heads/target1 >actual &&
+       test_cmp expect actual
+ '
+ test_expect_success 'fails with duplicate ref update via symref' '
+       git branch target2 $A &&
+       git symbolic-ref refs/heads/symref2 refs/heads/target2 &&
+       cat >stdin <<-EOF &&
+       update refs/heads/target2 $C
+       update refs/heads/symref2 $B
+       EOF
+       test_must_fail git update-ref --stdin <stdin 2>err &&
+       grep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err &&
+       echo "refs/heads/target2" >expect &&
+       git symbolic-ref refs/heads/symref2 >actual &&
+       test_cmp expect actual &&
+       echo "$A" >expect &&
+       git rev-parse refs/heads/target2 >actual &&
+       test_cmp expect actual
+ '
  run_with_limited_open_files () {
        (ulimit -n 32 && "$@")
  }
diff --combined t/t3200-branch.sh
index ac9c76479933f6f1c750aa4a6f544a8b5a343439,42811604bb8346950e273add0f35020ab73c9599..8a833f354e331966f5f927ae94ac8686dd6d166f
@@@ -79,6 -79,15 +79,15 @@@ test_expect_success 'git branch -m dump
        test_i18ngrep "branch name required" err
  '
  
+ test_expect_success 'git branch -m m broken_symref should work' '
+       test_when_finished "git branch -D broken_symref" &&
+       git branch -l m &&
+       git symbolic-ref refs/heads/broken_symref refs/heads/i_am_broken &&
+       git branch -m m broken_symref &&
+       git reflog exists refs/heads/broken_symref &&
+       test_must_fail git reflog exists refs/heads/i_am_broken
+ '
  test_expect_success 'git branch -m m m/m should work' '
        git branch -l m &&
        git branch -m m m/m &&
@@@ -550,7 -559,7 +559,7 @@@ If you wanted to make '"'master'"' trac
      git branch -d origin/master
      git branch --set-upstream-to origin/master
  EOF
 -      test_cmp expected actual
 +      test_i18ncmp expected actual
  '
  
  test_expect_success '--set-upstream with two args only shows the deprecation message' '
        cat >expected <<EOF &&
  The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
  EOF
 -      test_cmp expected actual
 +      test_i18ncmp expected actual
  '
  
  test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' '
        cat >expected <<EOF &&
  The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
  EOF
 -      test_cmp expected actual
 +      test_i18ncmp expected actual
  '
  
  test_expect_success '--set-upstream-to notices an error to set branch as own upstream' '