Merge branch 'dt/refs-pseudo'
authorJunio C Hamano <gitster@pobox.com>
Tue, 25 Aug 2015 21:57:08 +0000 (14:57 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 25 Aug 2015 21:57:08 +0000 (14:57 -0700)
To prepare for allowing a different "ref" backend to be plugged in
to the system, update_ref()/delete_ref() have been taught about
ref-like things like MERGE_HEAD that are per-worktree (they will
always be written to the filesystem inside $GIT_DIR).

* dt/refs-pseudo:
pseudoref: check return values from read_ref()
sequencer: replace write_cherry_pick_head with update_ref
bisect: use update_ref
pseudorefs: create and use pseudoref update and delete functions
refs: add ref_type function
refs: introduce pseudoref and per-worktree ref concepts

1  2 
bisect.c
refs.c
refs.h
sequencer.c
diff --combined bisect.c
index f9da9d134680c7581231f27584b10e83a6110bcd,7eaa37a92986538a640671a8d607e47cccb2c759..041a13d093a21597c60d799c0f6943998f2d6a8a
+++ b/bisect.c
@@@ -19,11 -19,7 +19,10 @@@ static struct object_id *current_bad_oi
  
  static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
  static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
- static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
  
 +static const char *term_bad;
 +static const char *term_good;
 +
  /* Remember to update object flag allocation in object.h */
  #define COUNTED               (1u<<16)
  
@@@ -406,21 -402,15 +405,21 @@@ struct commit_list *find_bisection(stru
  static int register_ref(const char *refname, const struct object_id *oid,
                        int flags, void *cb_data)
  {
 -      if (!strcmp(refname, "bad")) {
 +      struct strbuf good_prefix = STRBUF_INIT;
 +      strbuf_addstr(&good_prefix, term_good);
 +      strbuf_addstr(&good_prefix, "-");
 +
 +      if (!strcmp(refname, term_bad)) {
                current_bad_oid = xmalloc(sizeof(*current_bad_oid));
                oidcpy(current_bad_oid, oid);
 -      } else if (starts_with(refname, "good-")) {
 +      } else if (starts_with(refname, good_prefix.buf)) {
                sha1_array_append(&good_revs, oid->hash);
        } else if (starts_with(refname, "skip-")) {
                sha1_array_append(&skipped_revs, oid->hash);
        }
  
 +      strbuf_release(&good_prefix);
 +
        return 0;
  }
  
@@@ -429,13 -419,10 +428,13 @@@ static int read_bisect_refs(void
        return for_each_ref_in("refs/bisect/", register_ref, NULL);
  }
  
 +static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
 +static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
 +
  static void read_bisect_paths(struct argv_array *array)
  {
        struct strbuf str = STRBUF_INIT;
 -      const char *filename = git_path("BISECT_NAMES");
 +      const char *filename = git_path_bisect_names();
        FILE *fp = fopen(filename, "r");
  
        if (!fp)
@@@ -646,7 -633,7 +645,7 @@@ static void exit_if_skipped_commits(str
                return;
  
        printf("There are only 'skip'ped commits left to test.\n"
 -             "The first bad commit could be any of:\n");
 +             "The first %s commit could be any of:\n", term_bad);
        print_commit_list(tried, "%s\n", "%s\n");
        if (bad)
                printf("%s\n", oid_to_hex(bad));
  
  static int is_expected_rev(const struct object_id *oid)
  {
 -      const char *filename = git_path("BISECT_EXPECTED_REV");
 +      const char *filename = git_path_bisect_expected_rev();
        struct stat st;
        struct strbuf str = STRBUF_INIT;
        FILE *fp;
        return res;
  }
  
- static void mark_expected_rev(char *bisect_rev_hex)
- {
-       int len = strlen(bisect_rev_hex);
-       const char *filename = git_path("BISECT_EXPECTED_REV");
-       int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
-       if (fd < 0)
-               die_errno("could not create file '%s'", filename);
-       bisect_rev_hex[len] = '\n';
-       write_or_die(fd, bisect_rev_hex, len + 1);
-       bisect_rev_hex[len] = '\0';
-       if (close(fd) < 0)
-               die("closing file %s: %s", filename, strerror(errno));
- }
- static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
+ static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)
  {
+       char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
  
-       mark_expected_rev(bisect_rev_hex);
+       memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
+       update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
  
        argv_checkout[2] = bisect_rev_hex;
        if (no_checkout) {
-               argv_update_ref[3] = bisect_rev_hex;
-               if (run_command_v_opt(argv_update_ref, RUN_GIT_CMD))
-                       die("update-ref --no-deref HEAD failed on %s",
-                           bisect_rev_hex);
+               update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
        } else {
                int res;
                res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
@@@ -744,24 -713,18 +725,24 @@@ static void handle_bad_merge_base(void
        if (is_expected_rev(current_bad_oid)) {
                char *bad_hex = oid_to_hex(current_bad_oid);
                char *good_hex = join_sha1_array_hex(&good_revs, ' ');
 -
 -              fprintf(stderr, "The merge base %s is bad.\n"
 -                      "This means the bug has been fixed "
 -                      "between %s and [%s].\n",
 -                      bad_hex, bad_hex, good_hex);
 -
 +              if (!strcmp(term_bad, "bad") && !strcmp(term_good, "good")) {
 +                      fprintf(stderr, "The merge base %s is bad.\n"
 +                              "This means the bug has been fixed "
 +                              "between %s and [%s].\n",
 +                              bad_hex, bad_hex, good_hex);
 +              } else {
 +                      fprintf(stderr, "The merge base %s is %s.\n"
 +                              "This means the first '%s' commit is "
 +                              "between %s and [%s].\n",
 +                              bad_hex, term_bad, term_good, bad_hex, good_hex);
 +              }
                exit(3);
        }
  
 -      fprintf(stderr, "Some good revs are not ancestor of the bad rev.\n"
 +      fprintf(stderr, "Some %s revs are not ancestor of the %s rev.\n"
                "git bisect cannot work properly in this case.\n"
 -              "Maybe you mistake good and bad revs?\n");
 +              "Maybe you mistook %s and %s revs?\n",
 +              term_good, term_bad, term_good, term_bad);
        exit(1);
  }
  
@@@ -773,10 -736,10 +754,10 @@@ static void handle_skipped_merge_base(c
  
        warning("the merge base between %s and [%s] "
                "must be skipped.\n"
 -              "So we cannot be sure the first bad commit is "
 +              "So we cannot be sure the first %s commit is "
                "between %s and %s.\n"
                "We continue anyway.",
 -              bad_hex, good_hex, mb_hex, bad_hex);
 +              bad_hex, good_hex, term_bad, mb_hex, bad_hex);
        free(good_hex);
  }
  
@@@ -807,7 -770,7 +788,7 @@@ static void check_merge_bases(int no_ch
                        handle_skipped_merge_base(mb);
                } else {
                        printf("Bisecting: a merge base must be tested\n");
-                       exit(bisect_checkout(sha1_to_hex(mb), no_checkout));
+                       exit(bisect_checkout(mb, no_checkout));
                }
        }
  
@@@ -857,7 -820,7 +838,7 @@@ static void check_good_are_ancestors_of
        int fd;
  
        if (!current_bad_oid)
 -              die("a bad revision is needed");
 +              die("a %s revision is needed", term_bad);
  
        /* Check if file BISECT_ANCESTORS_OK exists. */
        if (!stat(filename, &st) && S_ISREG(st.st_mode))
@@@ -907,36 -870,6 +888,36 @@@ static void show_diff_tree(const char *
        log_tree_commit(&opt, commit);
  }
  
 +/*
 + * The terms used for this bisect session are stored in BISECT_TERMS.
 + * We read them and store them to adapt the messages accordingly.
 + * Default is bad/good.
 + */
 +void read_bisect_terms(const char **read_bad, const char **read_good)
 +{
 +      struct strbuf str = STRBUF_INIT;
 +      const char *filename = git_path("BISECT_TERMS");
 +      FILE *fp = fopen(filename, "r");
 +
 +      if (!fp) {
 +              if (errno == ENOENT) {
 +                      *read_bad = "bad";
 +                      *read_good = "good";
 +                      return;
 +              } else {
 +                      die("could not read file '%s': %s", filename,
 +                              strerror(errno));
 +              }
 +      } else {
 +              strbuf_getline(&str, fp, '\n');
 +              *read_bad = strbuf_detach(&str, NULL);
 +              strbuf_getline(&str, fp, '\n');
 +              *read_good = strbuf_detach(&str, NULL);
 +      }
 +      strbuf_release(&str);
 +      fclose(fp);
 +}
 +
  /*
   * We use the convention that exiting with an exit code 10 means that
   * the bisection process finished successfully.
@@@ -951,9 -884,7 +932,8 @@@ int bisect_next_all(const char *prefix
        struct commit_list *tried;
        int reaches = 0, all = 0, nr, steps;
        const unsigned char *bisect_rev;
-       char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
  
 +      read_bisect_terms(&term_bad, &term_good);
        if (read_bisect_refs())
                die("reading bisect refs failed");
  
                 */
                exit_if_skipped_commits(tried, NULL);
  
 -              printf("%s was both good and bad\n",
 -                     oid_to_hex(current_bad_oid));
 +              printf("%s was both %s and %s\n",
 +                     oid_to_hex(current_bad_oid),
 +                     term_good,
 +                     term_bad);
                exit(1);
        }
  
        }
  
        bisect_rev = revs.commits->item->object.sha1;
-       memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
  
        if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
                exit_if_skipped_commits(tried, current_bad_oid);
-               printf("%s is the first %s commit\n", bisect_rev_hex,
 -              printf("%s is the first bad commit\n", sha1_to_hex(bisect_rev));
++              printf("%s is the first %s commit\n", sha1_to_hex(bisect_rev),
 +                      term_bad);
                show_diff_tree(prefix, revs.commits->item);
                /* This means the bisection process succeeded. */
                exit(10);
               "(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
               steps, (steps == 1 ? "" : "s"));
  
-       return bisect_checkout(bisect_rev_hex, no_checkout);
+       return bisect_checkout(bisect_rev, no_checkout);
  }
  
  static inline int log2i(int n)
diff --combined refs.c
index 835801905b597769aa5f3b5dd216075c263e6df5,1db3654b8e0518a73e7b847385cd7a9496013d07..b0e0fe560cebac68cb1a30b38307b3ac0094020d
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -19,14 -19,12 +19,14 @@@ struct ref_lock 
   * 1: End-of-component
   * 2: ., look for a preceding . to reject .. in refs
   * 3: {, look for a preceding @ to reject @{ in refs
 - * 4: A bad character: ASCII control characters, "~", "^", ":" or SP
 + * 4: A bad character: ASCII control characters, and
 + *    ":", "?", "[", "\", "^", "~", SP, or TAB
 + * 5: *, reject unless REFNAME_REFSPEC_PATTERN is set
   */
  static unsigned char refname_disposition[256] = {
        1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
 -      4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
 +      4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
   *
   * - any path component of it begins with ".", or
   * - it has double dots "..", or
 - * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
 - * - it ends with a "/".
 - * - it ends with ".lock"
 - * - it contains a "\" (backslash)
 + * - it has ASCII control characters, or
 + * - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
 + * - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
 + * - it ends with a "/", or
 + * - it ends with ".lock", or
 + * - it contains a "@{" portion
   */
 -static int check_refname_component(const char *refname, int flags)
 +static int check_refname_component(const char *refname, int *flags)
  {
        const char *cp;
        char last = '\0';
                        break;
                case 4:
                        return -1;
 +              case 5:
 +                      if (!(*flags & REFNAME_REFSPEC_PATTERN))
 +                              return -1; /* refspec can't be a pattern */
 +
 +                      /*
 +                       * Unset the pattern flag so that we only accept
 +                       * a single asterisk for one side of refspec.
 +                       */
 +                      *flags &= ~ REFNAME_REFSPEC_PATTERN;
 +                      break;
                }
                last = ch;
        }
@@@ -139,10 -125,18 +139,10 @@@ int check_refname_format(const char *re
  
        while (1) {
                /* We are at the start of a path component. */
 -              component_len = check_refname_component(refname, flags);
 -              if (component_len <= 0) {
 -                      if ((flags & REFNAME_REFSPEC_PATTERN) &&
 -                                      refname[0] == '*' &&
 -                                      (refname[1] == '\0' || refname[1] == '/')) {
 -                              /* Accept one wildcard as a full refname component. */
 -                              flags &= ~REFNAME_REFSPEC_PATTERN;
 -                              component_len = 1;
 -                      } else {
 -                              return -1;
 -                      }
 -              }
 +              component_len = check_refname_component(refname, &flags);
 +              if (component_len <= 0)
 +                      return -1;
 +
                component_count++;
                if (refname[component_len] == '\0')
                        break;
@@@ -1288,12 -1282,12 +1288,12 @@@ static void read_packed_refs(FILE *f, s
   */
  static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
  {
 -      const char *packed_refs_file;
 +      char *packed_refs_file;
  
        if (*refs->name)
 -              packed_refs_file = git_path_submodule(refs->name, "packed-refs");
 +              packed_refs_file = git_pathdup_submodule(refs->name, "packed-refs");
        else
 -              packed_refs_file = git_path("packed-refs");
 +              packed_refs_file = git_pathdup("packed-refs");
  
        if (refs->packed &&
            !stat_validity_check(&refs->packed->validity, packed_refs_file))
                        fclose(f);
                }
        }
 +      free(packed_refs_file);
        return refs->packed;
  }
  
@@@ -1326,13 -1319,7 +1326,13 @@@ static struct ref_dir *get_packed_refs(
        return get_packed_ref_dir(get_packed_ref_cache(refs));
  }
  
 -void add_packed_ref(const char *refname, const unsigned char *sha1)
 +/*
 + * Add a reference to the in-memory packed reference cache.  This may
 + * only be called while the packed-refs file is locked (see
 + * lock_packed_refs()).  To actually write the packed-refs file, call
 + * commit_packed_refs().
 + */
 +static void add_packed_ref(const char *refname, const unsigned char *sha1)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
@@@ -1352,23 -1339,19 +1352,23 @@@ static void read_loose_refs(const char 
  {
        struct ref_cache *refs = dir->ref_cache;
        DIR *d;
 -      const char *path;
        struct dirent *de;
        int dirnamelen = strlen(dirname);
        struct strbuf refname;
 +      struct strbuf path = STRBUF_INIT;
 +      size_t path_baselen;
  
        if (*refs->name)
 -              path = git_path_submodule(refs->name, "%s", dirname);
 +              strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
        else
 -              path = git_path("%s", dirname);
 +              strbuf_git_path(&path, "%s", dirname);
 +      path_baselen = path.len;
  
 -      d = opendir(path);
 -      if (!d)
 +      d = opendir(path.buf);
 +      if (!d) {
 +              strbuf_release(&path);
                return;
 +      }
  
        strbuf_init(&refname, dirnamelen + 257);
        strbuf_add(&refname, dirname, dirnamelen);
                unsigned char sha1[20];
                struct stat st;
                int flag;
 -              const char *refdir;
  
                if (de->d_name[0] == '.')
                        continue;
                if (ends_with(de->d_name, ".lock"))
                        continue;
                strbuf_addstr(&refname, de->d_name);
 -              refdir = *refs->name
 -                      ? git_path_submodule(refs->name, "%s", refname.buf)
 -                      : git_path("%s", refname.buf);
 -              if (stat(refdir, &st) < 0) {
 +              strbuf_addstr(&path, de->d_name);
 +              if (stat(path.buf, &st) < 0) {
                        ; /* silently ignore */
                } else if (S_ISDIR(st.st_mode)) {
                        strbuf_addch(&refname, '/');
                                         create_ref_entry(refname.buf, sha1, flag, 0));
                }
                strbuf_setlen(&refname, dirnamelen);
 +              strbuf_setlen(&path, path_baselen);
        }
        strbuf_release(&refname);
 +      strbuf_release(&path);
        closedir(d);
  }
  
@@@ -1485,15 -1469,14 +1485,15 @@@ static int resolve_gitlink_ref_recursiv
  {
        int fd, len;
        char buffer[128], *p;
 -      const char *path;
 +      char *path;
  
        if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
                return -1;
        path = *refs->name
 -              ? git_path_submodule(refs->name, "%s", refname)
 -              : git_path("%s", refname);
 +              ? git_pathdup_submodule(refs->name, "%s", refname)
 +              : git_pathdup("%s", refname);
        fd = open(path, O_RDONLY);
 +      free(path);
        if (fd < 0)
                return resolve_gitlink_packed_ref(refs, refname, sha1);
  
@@@ -1763,11 -1746,9 +1763,11 @@@ const char *resolve_ref_unsafe(const ch
        return ret;
  }
  
 -char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
 +char *resolve_refdup(const char *refname, int resolve_flags,
 +                   unsigned char *sha1, int *flags)
  {
 -      return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags));
 +      return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
 +                                                sha1, flags));
  }
  
  /* The argument to filter_refs */
@@@ -2146,8 -2127,7 +2146,8 @@@ int for_each_remote_ref_submodule(cons
  
  int for_each_replace_ref(each_ref_fn fn, void *cb_data)
  {
 -      return do_for_each_ref(&ref_cache, "refs/replace/", fn, 13, 0, cb_data);
 +      return do_for_each_ref(&ref_cache, git_replace_ref_base, fn,
 +                             strlen(git_replace_ref_base), 0, cb_data);
  }
  
  int head_ref_namespaced(each_ref_fn fn, void *cb_data)
@@@ -2290,14 -2270,25 +2290,14 @@@ static int verify_lock(struct ref_lock 
        return 0;
  }
  
 -static int remove_empty_directories(const char *file)
 +static int remove_empty_directories(struct strbuf *path)
  {
 -      /* we want to create a file but there is a directory there;
 +      /*
 +       * we want to create a file but there is a directory there;
         * if that is an empty directory (or a directory that contains
         * only empty directories), remove them.
         */
 -      struct strbuf path;
 -      int result, save_errno;
 -
 -      strbuf_init(&path, 20);
 -      strbuf_addstr(&path, file);
 -
 -      result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
 -      save_errno = errno;
 -
 -      strbuf_release(&path);
 -      errno = save_errno;
 -
 -      return result;
 +      return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY);
  }
  
  /*
@@@ -2397,8 -2388,7 +2397,8 @@@ static struct ref_lock *lock_ref_sha1_b
                                            unsigned int flags, int *type_p,
                                            struct strbuf *err)
  {
 -      const char *ref_file;
 +      struct strbuf ref_file = STRBUF_INIT;
 +      struct strbuf orig_ref_file = STRBUF_INIT;
        const char *orig_refname = refname;
        struct ref_lock *lock;
        int last_errno = 0;
        refname = resolve_ref_unsafe(refname, resolve_flags,
                                     lock->old_oid.hash, &type);
        if (!refname && errno == EISDIR) {
 -              /* we are trying to lock foo but we used to
 +              /*
 +               * we are trying to lock foo but we used to
                 * have foo/bar which now does not exist;
                 * it is normal for the empty directory 'foo'
                 * to remain.
                 */
 -              ref_file = git_path("%s", orig_refname);
 -              if (remove_empty_directories(ref_file)) {
 +              strbuf_git_path(&orig_ref_file, "%s", orig_refname);
 +              if (remove_empty_directories(&orig_ref_file)) {
                        last_errno = errno;
 -
                        if (!verify_refname_available(orig_refname, extras, skip,
                                                      get_loose_refs(&ref_cache), err))
                                strbuf_addf(err, "there are still refs under '%s'",
                                            orig_refname);
 -
                        goto error_return;
                }
                refname = resolve_ref_unsafe(orig_refname, resolve_flags,
        }
        lock->ref_name = xstrdup(refname);
        lock->orig_ref_name = xstrdup(orig_refname);
 -      ref_file = git_path("%s", refname);
 +      strbuf_git_path(&ref_file, "%s", refname);
  
   retry:
 -      switch (safe_create_leading_directories_const(ref_file)) {
 +      switch (safe_create_leading_directories_const(ref_file.buf)) {
        case SCLD_OK:
                break; /* success */
        case SCLD_VANISHED:
                /* fall through */
        default:
                last_errno = errno;
 -              strbuf_addf(err, "unable to create directory for %s", ref_file);
 +              strbuf_addf(err, "unable to create directory for %s",
 +                          ref_file.buf);
                goto error_return;
        }
  
 -      if (hold_lock_file_for_update(lock->lk, ref_file, lflags) < 0) {
 +      if (hold_lock_file_for_update(lock->lk, ref_file.buf, lflags) < 0) {
                last_errno = errno;
                if (errno == ENOENT && --attempts_remaining > 0)
                        /*
                         */
                        goto retry;
                else {
 -                      unable_to_lock_message(ref_file, errno, err);
 +                      unable_to_lock_message(ref_file.buf, errno, err);
                        goto error_return;
                }
        }
                last_errno = errno;
                goto error_return;
        }
 -      return lock;
 +      goto out;
  
   error_return:
        unlock_ref(lock);
 +      lock = NULL;
 +
 + out:
 +      strbuf_release(&ref_file);
 +      strbuf_release(&orig_ref_file);
        errno = last_errno;
 -      return NULL;
 +      return lock;
  }
  
  /*
@@@ -2550,12 -2535,8 +2550,12 @@@ static int write_packed_entry_fn(struc
        return 0;
  }
  
 -/* This should return a meaningful errno on failure */
 -int lock_packed_refs(int flags)
 +/*
 + * Lock the packed-refs file for writing. Flags is passed to
 + * hold_lock_file_for_update(). Return 0 on success. On errors, set
 + * errno appropriately and return a nonzero value.
 + */
 +static int lock_packed_refs(int flags)
  {
        static int timeout_configured = 0;
        static int timeout_value = 1000;
  }
  
  /*
 - * Commit the packed refs changes.
 - * On error we must make sure that errno contains a meaningful value.
 + * Write the current version of the packed refs cache from memory to
 + * disk. The packed-refs file must already be locked for writing (see
 + * lock_packed_refs()). Return zero on success. On errors, set errno
 + * and return a nonzero value
   */
 -int commit_packed_refs(void)
 +static int commit_packed_refs(void)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
        return error;
  }
  
 -void rollback_packed_refs(void)
 +/*
 + * Rollback the lockfile for the packed-refs file, and discard the
 + * in-memory packed reference cache.  (The packed-refs file will be
 + * read anew if it is needed again after this function is called.)
 + */
 +static void rollback_packed_refs(void)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
@@@ -2782,14 -2756,7 +2782,14 @@@ int pack_refs(unsigned int flags
        return 0;
  }
  
 -int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 +/*
 + * Rewrite the packed-refs file, omitting any refs listed in
 + * 'refnames'. On error, leave packed-refs unchanged, write an error
 + * message to 'err', and return a nonzero value.
 + *
 + * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
 + */
 +static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
  {
        struct ref_dir *packed;
        struct string_list_item *refname;
@@@ -2854,15 -2821,120 +2854,120 @@@ static int delete_ref_loose(struct ref_
        return 0;
  }
  
 -int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags)
+ static int is_per_worktree_ref(const char *refname)
+ {
+       return !strcmp(refname, "HEAD");
+ }
+ static int is_pseudoref_syntax(const char *refname)
+ {
+       const char *c;
+       for (c = refname; *c; c++) {
+               if (!isupper(*c) && *c != '-' && *c != '_')
+                       return 0;
+       }
+       return 1;
+ }
+ enum ref_type ref_type(const char *refname)
+ {
+       if (is_per_worktree_ref(refname))
+               return REF_TYPE_PER_WORKTREE;
+       if (is_pseudoref_syntax(refname))
+               return REF_TYPE_PSEUDOREF;
+        return REF_TYPE_NORMAL;
+ }
+ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
+                          const unsigned char *old_sha1, struct strbuf *err)
+ {
+       const char *filename;
+       int fd;
+       static struct lock_file lock;
+       struct strbuf buf = STRBUF_INIT;
+       int ret = -1;
+       strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
+       filename = git_path("%s", pseudoref);
+       fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+       if (fd < 0) {
+               strbuf_addf(err, "Could not open '%s' for writing: %s",
+                           filename, strerror(errno));
+               return -1;
+       }
+       if (old_sha1) {
+               unsigned char actual_old_sha1[20];
+               if (read_ref(pseudoref, actual_old_sha1))
+                       die("could not read ref '%s'", pseudoref);
+               if (hashcmp(actual_old_sha1, old_sha1)) {
+                       strbuf_addf(err, "Unexpected sha1 when writing %s", pseudoref);
+                       rollback_lock_file(&lock);
+                       goto done;
+               }
+       }
+       if (write_in_full(fd, buf.buf, buf.len) != buf.len) {
+               strbuf_addf(err, "Could not write to '%s'", filename);
+               rollback_lock_file(&lock);
+               goto done;
+       }
+       commit_lock_file(&lock);
+       ret = 0;
+ done:
+       strbuf_release(&buf);
+       return ret;
+ }
+ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)
+ {
+       static struct lock_file lock;
+       const char *filename;
+       filename = git_path("%s", pseudoref);
+       if (old_sha1 && !is_null_sha1(old_sha1)) {
+               int fd;
+               unsigned char actual_old_sha1[20];
+               fd = hold_lock_file_for_update(&lock, filename,
+                                              LOCK_DIE_ON_ERROR);
+               if (fd < 0)
+                       die_errno(_("Could not open '%s' for writing"), filename);
+               if (read_ref(pseudoref, actual_old_sha1))
+                       die("could not read ref '%s'", pseudoref);
+               if (hashcmp(actual_old_sha1, old_sha1)) {
+                       warning("Unexpected sha1 when deleting %s", pseudoref);
+                       rollback_lock_file(&lock);
+                       return -1;
+               }
+               unlink(filename);
+               rollback_lock_file(&lock);
+       } else {
+               unlink(filename);
+       }
+       return 0;
+ }
 +int delete_ref(const char *refname, const unsigned char *old_sha1,
 +             unsigned int flags)
  {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
  
 -              return delete_pseudoref(refname, sha1);
+       if (ref_type(refname) == REF_TYPE_PSEUDOREF)
++              return delete_pseudoref(refname, old_sha1);
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
 -          ref_transaction_delete(transaction, refname,
 -                                 (sha1 && !is_null_sha1(sha1)) ? sha1 : NULL,
 +          ref_transaction_delete(transaction, refname, old_sha1,
                                   flags, NULL, &err) ||
            ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
        return 0;
  }
  
 +int delete_refs(struct string_list *refnames)
 +{
 +      struct strbuf err = STRBUF_INIT;
 +      int i, result = 0;
 +
 +      if (!refnames->nr)
 +              return 0;
 +
 +      result = repack_without_refs(refnames, &err);
 +      if (result) {
 +              /*
 +               * If we failed to rewrite the packed-refs file, then
 +               * it is unsafe to try to remove loose refs, because
 +               * doing so might expose an obsolete packed value for
 +               * a reference that might even point at an object that
 +               * has been garbage collected.
 +               */
 +              if (refnames->nr == 1)
 +                      error(_("could not delete reference %s: %s"),
 +                            refnames->items[0].string, err.buf);
 +              else
 +                      error(_("could not delete references: %s"), err.buf);
 +
 +              goto out;
 +      }
 +
 +      for (i = 0; i < refnames->nr; i++) {
 +              const char *refname = refnames->items[i].string;
 +
 +              if (delete_ref(refname, NULL, 0))
 +                      result |= error(_("could not remove reference %s"), refname);
 +      }
 +
 +out:
 +      strbuf_release(&err);
 +      return result;
 +}
 +
  /*
   * People using contrib's git-new-workdir have .git/logs/refs ->
   * /some/other/path/.git/logs/refs, and that may live on another device.
  static int rename_tmp_log(const char *newrefname)
  {
        int attempts_remaining = 4;
 +      struct strbuf path = STRBUF_INIT;
 +      int ret = -1;
  
   retry:
 -      switch (safe_create_leading_directories_const(git_path("logs/%s", newrefname))) {
 +      strbuf_reset(&path);
 +      strbuf_git_path(&path, "logs/%s", newrefname);
 +      switch (safe_create_leading_directories_const(path.buf)) {
        case SCLD_OK:
                break; /* success */
        case SCLD_VANISHED:
                /* fall through */
        default:
                error("unable to create directory for %s", newrefname);
 -              return -1;
 +              goto out;
        }
  
 -      if (rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newrefname))) {
 +      if (rename(git_path(TMP_RENAMED_LOG), path.buf)) {
                if ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {
                        /*
                         * rename(a, b) when b is an existing
                         * directory ought to result in ISDIR, but
                         * Solaris 5.8 gives ENOTDIR.  Sheesh.
                         */
 -                      if (remove_empty_directories(git_path("logs/%s", newrefname))) {
 +                      if (remove_empty_directories(&path)) {
                                error("Directory not empty: logs/%s", newrefname);
 -                              return -1;
 +                              goto out;
                        }
                        goto retry;
                } else if (errno == ENOENT && --attempts_remaining > 0) {
                } else {
                        error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
                                newrefname, strerror(errno));
 -                      return -1;
 +                      goto out;
                }
        }
 -      return 0;
 +      ret = 0;
 +out:
 +      strbuf_release(&path);
 +      return ret;
  }
  
  static int rename_ref_available(const char *oldname, const char *newname)
@@@ -3035,14 -3062,7 +3140,14 @@@ int rename_ref(const char *oldrefname, 
        if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
            delete_ref(newrefname, sha1, REF_NODEREF)) {
                if (errno==EISDIR) {
 -                      if (remove_empty_directories(git_path("%s", newrefname))) {
 +                      struct strbuf path = STRBUF_INIT;
 +                      int result;
 +
 +                      strbuf_git_path(&path, "%s", newrefname);
 +                      result = remove_empty_directories(&path);
 +                      strbuf_release(&path);
 +
 +                      if (result) {
                                error("Directory not empty: %s", newrefname);
                                goto rollback;
                        }
@@@ -3159,21 -3179,25 +3264,21 @@@ static int should_autocreate_reflog(con
   * should_autocreate_reflog returns non-zero.  Otherwise, create it
   * regardless of the ref name.  Fill in *err and return -1 on failure.
   */
 -static int log_ref_setup(const char *refname, struct strbuf *sb_logfile, struct strbuf *err, int force_create)
 +static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)
  {
        int logfd, oflags = O_APPEND | O_WRONLY;
 -      char *logfile;
  
 -      strbuf_git_path(sb_logfile, "logs/%s", refname);
 -      logfile = sb_logfile->buf;
 -      /* make sure the rest of the function can't change "logfile" */
 -      sb_logfile = NULL;
 +      strbuf_git_path(logfile, "logs/%s", refname);
        if (force_create || should_autocreate_reflog(refname)) {
 -              if (safe_create_leading_directories(logfile) < 0) {
 +              if (safe_create_leading_directories(logfile->buf) < 0) {
                        strbuf_addf(err, "unable to create directory for %s: "
 -                                  "%s", logfile, strerror(errno));
 +                                  "%s", logfile->buf, strerror(errno));
                        return -1;
                }
                oflags |= O_CREAT;
        }
  
 -      logfd = open(logfile, oflags, 0666);
 +      logfd = open(logfile->buf, oflags, 0666);
        if (logfd < 0) {
                if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR))
                        return 0;
                if (errno == EISDIR) {
                        if (remove_empty_directories(logfile)) {
                                strbuf_addf(err, "There are still logs under "
 -                                          "'%s'", logfile);
 +                                          "'%s'", logfile->buf);
                                return -1;
                        }
 -                      logfd = open(logfile, oflags, 0666);
 +                      logfd = open(logfile->buf, oflags, 0666);
                }
  
                if (logfd < 0) {
                        strbuf_addf(err, "unable to append to %s: %s",
 -                                  logfile, strerror(errno));
 +                                  logfile->buf, strerror(errno));
                        return -1;
                }
        }
  
 -      adjust_shared_perm(logfile);
 +      adjust_shared_perm(logfile->buf);
        close(logfd);
        return 0;
  }
@@@ -3238,32 -3262,36 +3343,32 @@@ static int log_ref_write_fd(int fd, con
  
  static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
                           const unsigned char *new_sha1, const char *msg,
 -                         struct strbuf *sb_log_file, int flags,
 +                         struct strbuf *logfile, int flags,
                           struct strbuf *err)
  {
        int logfd, result, oflags = O_APPEND | O_WRONLY;
 -      char *log_file;
  
        if (log_all_ref_updates < 0)
                log_all_ref_updates = !is_bare_repository();
  
 -      result = log_ref_setup(refname, sb_log_file, err, flags & REF_FORCE_CREATE_REFLOG);
 +      result = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);
  
        if (result)
                return result;
 -      log_file = sb_log_file->buf;
 -      /* make sure the rest of the function can't change "log_file" */
 -      sb_log_file = NULL;
  
 -      logfd = open(log_file, oflags);
 +      logfd = open(logfile->buf, oflags);
        if (logfd < 0)
                return 0;
        result = log_ref_write_fd(logfd, old_sha1, new_sha1,
                                  git_committer_info(0), msg);
        if (result) {
 -              strbuf_addf(err, "unable to append to %s: %s", log_file,
 +              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", log_file,
 +              strbuf_addf(err, "unable to append to %s: %s", logfile->buf,
                            strerror(errno));
                return -1;
        }
@@@ -3384,7 -3412,7 +3489,7 @@@ static int commit_ref_update(struct ref
  int create_symref(const char *ref_target, const char *refs_heads_master,
                  const char *logmsg)
  {
 -      const char *lockpath;
 +      char *lockpath = NULL;
        char ref[1000];
        int fd, len, written;
        char *git_HEAD = git_pathdup("%s", ref_target);
                error("refname too long: %s", refs_heads_master);
                goto error_free_return;
        }
 -      lockpath = mkpath("%s.lock", git_HEAD);
 +      lockpath = mkpathdup("%s.lock", git_HEAD);
        fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
        if (fd < 0) {
                error("Unable to open %s for writing", lockpath);
        error_unlink_return:
                unlink_or_warn(lockpath);
        error_free_return:
 +              free(lockpath);
                free(git_HEAD);
                return -1;
        }
 +      free(lockpath);
  
  #ifndef NO_SYMLINK_HEAD
        done:
@@@ -3495,14 -3521,14 +3600,14 @@@ static int read_ref_at_ent(unsigned cha
                        hashcpy(cb->sha1, nsha1);
                        if (hashcmp(cb->osha1, nsha1))
                                warning("Log for ref %s has gap after %s.",
 -                                      cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822));
 +                                      cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
                }
                else if (cb->date == cb->at_time)
                        hashcpy(cb->sha1, nsha1);
                else if (hashcmp(nsha1, cb->sha1))
                        warning("Log for ref %s unexpectedly ended on %s.",
                                cb->refname, show_date(cb->date, cb->tz,
 -                                                 DATE_RFC2822));
 +                                                     DATE_MODE(RFC2822)));
                hashcpy(cb->osha1, osha1);
                hashcpy(cb->nsha1, nsha1);
                cb->found_it = 1;
@@@ -3961,17 -3987,25 +4066,25 @@@ int update_ref(const char *msg, const c
               const unsigned char *new_sha1, const unsigned char *old_sha1,
               unsigned int flags, enum action_on_err onerr)
  {
-       struct ref_transaction *t;
+       struct ref_transaction *t = NULL;
        struct strbuf err = STRBUF_INIT;
+       int ret = 0;
  
-       t = ref_transaction_begin(&err);
-       if (!t ||
-           ref_transaction_update(t, refname, new_sha1, old_sha1,
-                                  flags, msg, &err) ||
-           ref_transaction_commit(t, &err)) {
+       if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+               ret = write_pseudoref(refname, new_sha1, old_sha1, &err);
+       } else {
+               t = ref_transaction_begin(&err);
+               if (!t ||
+                   ref_transaction_update(t, refname, new_sha1, old_sha1,
+                                          flags, msg, &err) ||
+                   ref_transaction_commit(t, &err)) {
+                       ret = 1;
+                       ref_transaction_free(t);
+               }
+       }
+       if (ret) {
                const char *str = "update_ref failed for ref '%s': %s";
  
-               ref_transaction_free(t);
                switch (onerr) {
                case UPDATE_REFS_MSG_ON_ERR:
                        error(str, refname, err.buf);
                return 1;
        }
        strbuf_release(&err);
-       ref_transaction_free(t);
+       if (t)
+               ref_transaction_free(t);
        return 0;
  }
  
@@@ -4166,98 -4201,6 +4280,98 @@@ cleanup
        return ret;
  }
  
 +static int ref_present(const char *refname,
 +                     const struct object_id *oid, int flags, void *cb_data)
 +{
 +      struct string_list *affected_refnames = cb_data;
 +
 +      return string_list_has_string(affected_refnames, refname);
 +}
 +
 +int initial_ref_transaction_commit(struct ref_transaction *transaction,
 +                                 struct strbuf *err)
 +{
 +      struct ref_dir *loose_refs = get_loose_refs(&ref_cache);
 +      struct ref_dir *packed_refs = get_packed_refs(&ref_cache);
 +      int ret = 0, i;
 +      int n = transaction->nr;
 +      struct ref_update **updates = transaction->updates;
 +      struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
 +
 +      assert(err);
 +
 +      if (transaction->state != REF_TRANSACTION_OPEN)
 +              die("BUG: commit called for transaction that is not open");
 +
 +      /* Fail if a refname appears more than once in the transaction: */
 +      for (i = 0; i < n; i++)
 +              string_list_append(&affected_refnames, updates[i]->refname);
 +      string_list_sort(&affected_refnames);
 +      if (ref_update_reject_duplicates(&affected_refnames, err)) {
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +      /*
 +       * It's really undefined to call this function in an active
 +       * repository or when there are existing references: we are
 +       * only locking and changing packed-refs, so (1) any
 +       * simultaneous processes might try to change a reference at
 +       * the same time we do, and (2) any existing loose versions of
 +       * the references that we are setting would have precedence
 +       * over our values. But some remote helpers create the remote
 +       * "HEAD" and "master" branches before calling this function,
 +       * so here we really only check that none of the references
 +       * that we are creating already exists.
 +       */
 +      if (for_each_rawref(ref_present, &affected_refnames))
 +              die("BUG: initial ref transaction called with existing refs");
 +
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if ((update->flags & REF_HAVE_OLD) &&
 +                  !is_null_sha1(update->old_sha1))
 +                      die("BUG: initial ref transaction with old_sha1 set");
 +              if (verify_refname_available(update->refname,
 +                                           &affected_refnames, NULL,
 +                                           loose_refs, err) ||
 +                  verify_refname_available(update->refname,
 +                                           &affected_refnames, NULL,
 +                                           packed_refs, err)) {
 +                      ret = TRANSACTION_NAME_CONFLICT;
 +                      goto cleanup;
 +              }
 +      }
 +
 +      if (lock_packed_refs(0)) {
 +              strbuf_addf(err, "unable to lock packed-refs file: %s",
 +                          strerror(errno));
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if ((update->flags & REF_HAVE_NEW) &&
 +                  !is_null_sha1(update->new_sha1))
 +                      add_packed_ref(update->refname, update->new_sha1);
 +      }
 +
 +      if (commit_packed_refs()) {
 +              strbuf_addf(err, "unable to commit packed-refs file: %s",
 +                          strerror(errno));
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +cleanup:
 +      transaction->state = REF_TRANSACTION_CLOSED;
 +      string_list_clear(&affected_refnames, 0);
 +      return ret;
 +}
 +
  char *shorten_unambiguous_ref(const char *refname, int strict)
  {
        int i;
@@@ -4379,25 -4322,17 +4493,25 @@@ int parse_hide_refs_config(const char *
  
  int ref_is_hidden(const char *refname)
  {
 -      struct string_list_item *item;
 +      int i;
  
        if (!hide_refs)
                return 0;
 -      for_each_string_list_item(item, hide_refs) {
 +      for (i = hide_refs->nr - 1; i >= 0; i--) {
 +              const char *match = hide_refs->items[i].string;
 +              int neg = 0;
                int len;
 -              if (!starts_with(refname, item->string))
 +
 +              if (*match == '!') {
 +                      neg = 1;
 +                      match++;
 +              }
 +
 +              if (!starts_with(refname, match))
                        continue;
 -              len = strlen(item->string);
 +              len = strlen(match);
                if (!refname[len] || refname[len] == '/')
 -                      return 1;
 +                      return !neg;
        }
        return 0;
  }
diff --combined refs.h
index 6a3fa6d41d50a7a76fdb2dab4c18aaaba1d9b8eb,1927bda993da81d0d5a7011ff7b15fb8d4f62d92..e9a5f3230ab09b667e7ae28b2b0bcd26bd2f067e
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -1,98 -1,6 +1,98 @@@
  #ifndef REFS_H
  #define REFS_H
  
 +/*
 + * Resolve a reference, recursively following symbolic refererences.
 + *
 + * Store the referred-to object's name in sha1 and return the name of
 + * the non-symbolic reference that ultimately pointed at it.  The
 + * return value, if not NULL, is a pointer into either a static buffer
 + * or the input ref.
 + *
 + * If the reference cannot be resolved to an object, the behavior
 + * depends on the RESOLVE_REF_READING flag:
 + *
 + * - If RESOLVE_REF_READING is set, return NULL.
 + *
 + * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of
 + *   the last reference name in the chain, which will either be a non-symbolic
 + *   reference or an undefined reference.  If this is a prelude to
 + *   "writing" to the ref, the return value is the name of the ref
 + *   that will actually be created or changed.
 + *
 + * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one
 + * level of symbolic reference.  The value stored in sha1 for a symbolic
 + * reference will always be null_sha1 in this case, and the return
 + * value is the reference that the symref refers to directly.
 + *
 + * If flags is non-NULL, set the value that it points to the
 + * combination of REF_ISPACKED (if the reference was found among the
 + * packed references), REF_ISSYMREF (if the initial reference was a
 + * symbolic reference), REF_BAD_NAME (if the reference name is ill
 + * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
 + * (if the ref is malformed or has a bad name). See refs.h for more detail
 + * on each flag.
 + *
 + * If ref is not a properly-formatted, normalized reference, return
 + * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
 + * give up and return NULL.
 + *
 + * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
 + * name is invalid according to git-check-ref-format(1).  If the name
 + * is bad then the value stored in sha1 will be null_sha1 and the two
 + * flags REF_ISBROKEN and REF_BAD_NAME will be set.
 + *
 + * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
 + * directory and do not consist of all caps and underscores cannot be
 + * resolved. The function returns NULL for such ref names.
 + * Caps and underscores refers to the special refs, such as HEAD,
 + * FETCH_HEAD and friends, that all live outside of the refs/ directory.
 + */
 +#define RESOLVE_REF_READING 0x01
 +#define RESOLVE_REF_NO_RECURSE 0x02
 +#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
 +
 +extern const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 +                                    unsigned char *sha1, int *flags);
 +
 +extern char *resolve_refdup(const char *refname, int resolve_flags,
 +                          unsigned char *sha1, int *flags);
 +
 +extern int read_ref_full(const char *refname, int resolve_flags,
 +                       unsigned char *sha1, int *flags);
 +extern int read_ref(const char *refname, unsigned char *sha1);
 +
 +extern int ref_exists(const char *refname);
 +
 +extern int is_branch(const char *refname);
 +
 +/*
 + * If refname is a non-symbolic reference that refers to a tag object,
 + * and the tag can be (recursively) dereferenced to a non-tag object,
 + * store the SHA1 of the referred-to object to sha1 and return 0.  If
 + * any of these conditions are not met, return a non-zero value.
 + * Symbolic references are considered unpeelable, even if they
 + * ultimately resolve to a peelable tag.
 + */
 +extern int peel_ref(const char *refname, unsigned char *sha1);
 +
 +/**
 + * Resolve refname in the nested "gitlink" repository that is located
 + * at path.  If the resolution is successful, return 0 and set sha1 to
 + * the name of the object; otherwise, return a non-zero value.
 + */
 +extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1);
 +
 +/*
 + * Return true iff abbrev_name is a possible abbreviation for
 + * full_name according to the rules defined by ref_rev_parse_rules in
 + * refs.c.
 + */
 +extern int refname_match(const char *abbrev_name, const char *full_name);
 +
 +extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 +extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
 +
  /*
   * A ref_transaction represents a collection of ref updates
   * that should succeed or fail together.
@@@ -170,15 -78,15 +170,15 @@@ typedef int each_ref_fn(const char *ref
   * modifies the reference also returns a nonzero value to immediately
   * stop the iteration.
   */
 -extern int head_ref(each_ref_fn, void *);
 -extern int for_each_ref(each_ref_fn, void *);
 -extern int for_each_ref_in(const char *, each_ref_fn, void *);
 -extern int for_each_tag_ref(each_ref_fn, void *);
 -extern int for_each_branch_ref(each_ref_fn, void *);
 -extern int for_each_remote_ref(each_ref_fn, void *);
 -extern int for_each_replace_ref(each_ref_fn, void *);
 -extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
 -extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);
 +extern int head_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
 +extern int for_each_tag_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_branch_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);
 +extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
 +extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data);
  
  extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
  extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
@@@ -191,17 -99,47 +191,17 @@@ extern int for_each_remote_ref_submodul
  extern int head_ref_namespaced(each_ref_fn fn, void *cb_data);
  extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
  
 +/* can be used to learn about broken ref and symref */
 +extern int for_each_rawref(each_ref_fn fn, void *cb_data);
 +
  static inline const char *has_glob_specials(const char *pattern)
  {
        return strpbrk(pattern, "?*[");
  }
  
 -/* can be used to learn about broken ref and symref */
 -extern int for_each_rawref(each_ref_fn, void *);
 -
  extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
  extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames);
  
 -/*
 - * Lock the packed-refs file for writing.  Flags is passed to
 - * hold_lock_file_for_update().  Return 0 on success.
 - * Errno is set to something meaningful on error.
 - */
 -extern int lock_packed_refs(int flags);
 -
 -/*
 - * Add a reference to the in-memory packed reference cache.  This may
 - * only be called while the packed-refs file is locked (see
 - * lock_packed_refs()).  To actually write the packed-refs file, call
 - * commit_packed_refs().
 - */
 -extern void add_packed_ref(const char *refname, const unsigned char *sha1);
 -
 -/*
 - * Write the current version of the packed refs cache from memory to
 - * disk.  The packed-refs file must already be locked for writing (see
 - * lock_packed_refs()).  Return zero on success.
 - * Sets errno to something meaningful on error.
 - */
 -extern int commit_packed_refs(void);
 -
 -/*
 - * Rollback the lockfile for the packed-refs file, and discard the
 - * in-memory packed reference cache.  (The packed-refs file will be
 - * read anew if it is needed again after this function is called.)
 - */
 -extern void rollback_packed_refs(void);
 -
  /*
   * Flags for controlling behaviour of pack_refs()
   * PACK_REFS_PRUNE: Prune loose refs after packing
   */
  int pack_refs(unsigned int flags);
  
 -/*
 - * Rewrite the packed-refs file, omitting any refs listed in
 - * 'refnames'. On error, packed-refs will be unchanged, the return
 - * value is nonzero, and a message about the error is written to the
 - * 'err' strbuf.
 - *
 - * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
 - */
 -extern int repack_without_refs(struct string_list *refnames,
 -                             struct strbuf *err);
 -
 -extern int ref_exists(const char *);
 -
 -extern int is_branch(const char *refname);
 -
 -/*
 - * If refname is a non-symbolic reference that refers to a tag object,
 - * and the tag can be (recursively) dereferenced to a non-tag object,
 - * store the SHA1 of the referred-to object to sha1 and return 0.  If
 - * any of these conditions are not met, return a non-zero value.
 - * Symbolic references are considered unpeelable, even if they
 - * ultimately resolve to a peelable tag.
 - */
 -extern int peel_ref(const char *refname, unsigned char *sha1);
 -
  /*
   * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.
   * REF_NODEREF: act on the ref directly, instead of dereferencing
@@@ -240,23 -203,6 +240,23 @@@ extern int read_ref_at(const char *refn
  /** Check if a particular reflog exists */
  extern int reflog_exists(const char *refname);
  
 +/*
 + * Delete the specified reference. If old_sha1 is non-NULL, then
 + * verify that the current value of the reference is old_sha1 before
 + * deleting it. If old_sha1 is NULL, delete the reference if it
 + * exists, regardless of its old value. It is an error for old_sha1 to
 + * be NULL_SHA1. flags is passed through to ref_transaction_delete().
 + */
 +extern int delete_ref(const char *refname, const unsigned char *old_sha1,
 +                    unsigned int flags);
 +
 +/*
 + * Delete the specified references. If there are any problems, emit
 + * errors but attempt to keep going (i.e., the deletes are not done in
 + * an all-or-nothing transaction).
 + */
 +extern int delete_refs(struct string_list *refnames);
 +
  /** Delete a reflog */
  extern int delete_reflog(const char *refname);
  
@@@ -279,19 -225,23 +279,19 @@@ extern int for_each_reflog(each_ref_fn
   * to the rules described in Documentation/git-check-ref-format.txt.
   * If REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level
   * reference names.  If REFNAME_REFSPEC_PATTERN is set in flags, then
 - * allow a "*" wildcard character in place of one of the name
 - * components.  No leading or repeated slashes are accepted.
 + * allow a single "*" wildcard character in the refspec. No leading or
 + * repeated slashes are accepted.
   */
  extern int check_refname_format(const char *refname, int flags);
  
  extern const char *prettify_refname(const char *refname);
 +
  extern char *shorten_unambiguous_ref(const char *refname, int strict);
  
  /** rename ref, return 0 on success **/
  extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
  
 -/**
 - * Resolve refname in the nested "gitlink" repository that is located
 - * at path.  If the resolution is successful, return 0 and set sha1 to
 - * the name of the object; otherwise, return a non-zero value.
 - */
 -extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1);
 +extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
  
  enum action_on_err {
        UPDATE_REFS_MSG_ON_ERR,
@@@ -410,20 -360,6 +410,20 @@@ int ref_transaction_verify(struct ref_t
  int ref_transaction_commit(struct ref_transaction *transaction,
                           struct strbuf *err);
  
 +/*
 + * Like ref_transaction_commit(), but optimized for creating
 + * references when originally initializing a repository (e.g., by "git
 + * clone"). It writes the new references directly to packed-refs
 + * without locking the individual references.
 + *
 + * It is a bug to call this function when there might be other
 + * processes accessing the repository or if there are existing
 + * references that might conflict with the ones being created. All
 + * old_sha1 values must either be absent or NULL_SHA1.
 + */
 +int initial_ref_transaction_commit(struct ref_transaction *transaction,
 +                                 struct strbuf *err);
 +
  /*
   * Free an existing transaction and all associated data.
   */
@@@ -442,9 -378,16 +442,17 @@@ int update_ref(const char *msg, const c
               unsigned int flags, enum action_on_err onerr);
  
  extern int parse_hide_refs_config(const char *var, const char *value, const char *);
 +
  extern int ref_is_hidden(const char *);
  
+ enum ref_type {
+       REF_TYPE_PER_WORKTREE,
+       REF_TYPE_PSEUDOREF,
+       REF_TYPE_NORMAL,
+ };
+ enum ref_type ref_type(const char *refname);
  enum expire_reflog_flags {
        EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
        EXPIRE_REFLOGS_UPDATE_REF = 1 << 1,
diff --combined sequencer.c
index 147adfac818b53156b1ad269211014e21525619c,554a704e23b4975683456a10e745103ee683fab9..a0600aebcadb81e67921d953e8ed626d08529a9b
  const char sign_off_header[] = "Signed-off-by: ";
  static const char cherry_picked_prefix[] = "(cherry picked from commit ";
  
 +static GIT_PATH_FUNC(git_path_todo_file, SEQ_TODO_FILE)
 +static GIT_PATH_FUNC(git_path_opts_file, SEQ_OPTS_FILE)
 +static GIT_PATH_FUNC(git_path_seq_dir, SEQ_DIR)
 +static GIT_PATH_FUNC(git_path_head_file, SEQ_HEAD_FILE)
 +
  static int is_rfc2822_line(const char *buf, int len)
  {
        int i;
@@@ -163,23 -158,6 +163,6 @@@ static void free_message(struct commit 
        unuse_commit_buffer(commit, msg->message);
  }
  
- static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
- {
-       const char *filename;
-       int fd;
-       struct strbuf buf = STRBUF_INIT;
-       strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
-       filename = git_path("%s", pseudoref);
-       fd = open(filename, O_WRONLY | O_CREAT, 0666);
-       if (fd < 0)
-               die_errno(_("Could not open '%s' for writing"), filename);
-       if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
-               die_errno(_("Could not write to '%s'"), filename);
-       strbuf_release(&buf);
- }
  static void print_advice(int show_hint, struct replay_opts *opts)
  {
        char *msg = getenv("GIT_CHERRY_PICK_HELP");
                 * (typically rebase --interactive) wants to take care
                 * of the commit itself so remove CHERRY_PICK_HEAD
                 */
 -              unlink(git_path("CHERRY_PICK_HEAD"));
 +              unlink(git_path_cherry_pick_head());
                return;
        }
  
@@@ -472,6 -450,7 +455,6 @@@ static int do_pick_commit(struct commi
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
        struct commit_message msg = { NULL, NULL, NULL, NULL };
 -      char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
        int res, unborn = 0, allow;
  
         * reverse of it if we are revert.
         */
  
 -      defmsg = git_pathdup("MERGE_MSG");
 -
        if (opts->action == REPLAY_REVERT) {
                base = commit;
                base_label = msg.label;
        if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
                res = do_recursive_merge(base, next, base_label, next_label,
                                         head, &msgbuf, opts);
 -              write_message(&msgbuf, defmsg);
 +              write_message(&msgbuf, git_path_merge_msg());
        } else {
                struct commit_list *common = NULL;
                struct commit_list *remotes = NULL;
  
 -              write_message(&msgbuf, defmsg);
 +              write_message(&msgbuf, git_path_merge_msg());
  
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
         * write it at all.
         */
        if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))
-               write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
+               update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.sha1, NULL,
+                          REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
        if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))
-               write_cherry_pick_head(commit, "REVERT_HEAD");
+               update_ref(NULL, "REVERT_HEAD", commit->object.sha1, NULL,
+                          REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
  
        if (res) {
                error(opts->action == REPLAY_REVERT
                goto leave;
        }
        if (!opts->no_commit)
 -              res = run_git_commit(defmsg, opts, allow);
 +              res = run_git_commit(git_path_merge_msg(), opts, allow);
  
  leave:
        free_message(commit, &msg);
 -      free(defmsg);
  
        return res;
  }
@@@ -757,23 -741,24 +742,23 @@@ static int parse_insn_buffer(char *buf
  static void read_populate_todo(struct commit_list **todo_list,
                        struct replay_opts *opts)
  {
 -      const char *todo_file = git_path(SEQ_TODO_FILE);
        struct strbuf buf = STRBUF_INIT;
        int fd, res;
  
 -      fd = open(todo_file, O_RDONLY);
 +      fd = open(git_path_todo_file(), O_RDONLY);
        if (fd < 0)
 -              die_errno(_("Could not open %s"), todo_file);
 +              die_errno(_("Could not open %s"), git_path_todo_file());
        if (strbuf_read(&buf, fd, 0) < 0) {
                close(fd);
                strbuf_release(&buf);
 -              die(_("Could not read %s."), todo_file);
 +              die(_("Could not read %s."), git_path_todo_file());
        }
        close(fd);
  
        res = parse_insn_buffer(buf.buf, todo_list, opts);
        strbuf_release(&buf);
        if (res)
 -              die(_("Unusable instruction sheet: %s"), todo_file);
 +              die(_("Unusable instruction sheet: %s"), git_path_todo_file());
  }
  
  static int populate_opts_cb(const char *key, const char *value, void *data)
  
  static void read_populate_opts(struct replay_opts **opts_ptr)
  {
 -      const char *opts_file = git_path(SEQ_OPTS_FILE);
 -
 -      if (!file_exists(opts_file))
 +      if (!file_exists(git_path_opts_file()))
                return;
 -      if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
 -              die(_("Malformed options sheet: %s"), opts_file);
 +      if (git_config_from_file(populate_opts_cb, git_path_opts_file(), *opts_ptr) < 0)
 +              die(_("Malformed options sheet: %s"), git_path_opts_file());
  }
  
  static void walk_revs_populate_todo(struct commit_list **todo_list,
  
  static int create_seq_dir(void)
  {
 -      const char *seq_dir = git_path(SEQ_DIR);
 -
 -      if (file_exists(seq_dir)) {
 +      if (file_exists(git_path_seq_dir())) {
                error(_("a cherry-pick or revert is already in progress"));
                advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
                return -1;
        }
 -      else if (mkdir(seq_dir, 0777) < 0)
 -              die_errno(_("Could not create sequencer directory %s"), seq_dir);
 +      else if (mkdir(git_path_seq_dir(), 0777) < 0)
 +              die_errno(_("Could not create sequencer directory %s"),
 +                        git_path_seq_dir());
        return 0;
  }
  
  static void save_head(const char *head)
  {
 -      const char *head_file = git_path(SEQ_HEAD_FILE);
        static struct lock_file head_lock;
        struct strbuf buf = STRBUF_INIT;
        int fd;
  
 -      fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
 +      fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), LOCK_DIE_ON_ERROR);
        strbuf_addf(&buf, "%s\n", head);
        if (write_in_full(fd, buf.buf, buf.len) < 0)
 -              die_errno(_("Could not write to %s"), head_file);
 +              die_errno(_("Could not write to %s"), git_path_head_file());
        if (commit_lock_file(&head_lock) < 0)
 -              die(_("Error wrapping up %s."), head_file);
 +              die(_("Error wrapping up %s."), git_path_head_file());
  }
  
  static int reset_for_rollback(const unsigned char *sha1)
@@@ -873,8 -862,8 +858,8 @@@ static int rollback_single_pick(void
  {
        unsigned char head_sha1[20];
  
 -      if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
 -          !file_exists(git_path("REVERT_HEAD")))
 +      if (!file_exists(git_path_cherry_pick_head()) &&
 +          !file_exists(git_path_revert_head()))
                return error(_("no cherry-pick or revert in progress"));
        if (read_ref_full("HEAD", 0, head_sha1, NULL))
                return error(_("cannot resolve HEAD"));
  
  static int sequencer_rollback(struct replay_opts *opts)
  {
 -      const char *filename;
        FILE *f;
        unsigned char sha1[20];
        struct strbuf buf = STRBUF_INIT;
  
 -      filename = git_path(SEQ_HEAD_FILE);
 -      f = fopen(filename, "r");
 +      f = fopen(git_path_head_file(), "r");
        if (!f && errno == ENOENT) {
                /*
                 * There is no multiple-cherry-pick in progress.
                return rollback_single_pick();
        }
        if (!f)
 -              return error(_("cannot open %s: %s"), filename,
 +              return error(_("cannot open %s: %s"), git_path_head_file(),
                                                strerror(errno));
        if (strbuf_getline(&buf, f, '\n')) {
 -              error(_("cannot read %s: %s"), filename, ferror(f) ?
 -                      strerror(errno) : _("unexpected end of file"));
 +              error(_("cannot read %s: %s"), git_path_head_file(),
 +                    ferror(f) ?  strerror(errno) : _("unexpected end of file"));
                fclose(f);
                goto fail;
        }
        fclose(f);
        if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') {
                error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"),
 -                      filename);
 +                      git_path_head_file());
                goto fail;
        }
        if (reset_for_rollback(sha1))
@@@ -925,27 -916,28 +910,27 @@@ fail
  
  static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
  {
 -      const char *todo_file = git_path(SEQ_TODO_FILE);
        static struct lock_file todo_lock;
        struct strbuf buf = STRBUF_INIT;
        int fd;
  
 -      fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
 +      fd = hold_lock_file_for_update(&todo_lock, git_path_todo_file(), LOCK_DIE_ON_ERROR);
        if (format_todo(&buf, todo_list, opts) < 0)
 -              die(_("Could not format %s."), todo_file);
 +              die(_("Could not format %s."), git_path_todo_file());
        if (write_in_full(fd, buf.buf, buf.len) < 0) {
                strbuf_release(&buf);
 -              die_errno(_("Could not write to %s"), todo_file);
 +              die_errno(_("Could not write to %s"), git_path_todo_file());
        }
        if (commit_lock_file(&todo_lock) < 0) {
                strbuf_release(&buf);
 -              die(_("Error wrapping up %s."), todo_file);
 +              die(_("Error wrapping up %s."), git_path_todo_file());
        }
        strbuf_release(&buf);
  }
  
  static void save_opts(struct replay_opts *opts)
  {
 -      const char *opts_file = git_path(SEQ_OPTS_FILE);
 +      const char *opts_file = git_path_opts_file();
  
        if (opts->no_commit)
                git_config_set_in_file(opts_file, "options.no-commit", "true");
@@@ -1006,8 -998,8 +991,8 @@@ static int continue_single_pick(void
  {
        const char *argv[] = { "commit", NULL };
  
 -      if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
 -          !file_exists(git_path("REVERT_HEAD")))
 +      if (!file_exists(git_path_cherry_pick_head()) &&
 +          !file_exists(git_path_revert_head()))
                return error(_("no cherry-pick or revert in progress"));
        return run_command_v_opt(argv, RUN_GIT_CMD);
  }
@@@ -1016,14 -1008,14 +1001,14 @@@ static int sequencer_continue(struct re
  {
        struct commit_list *todo_list = NULL;
  
 -      if (!file_exists(git_path(SEQ_TODO_FILE)))
 +      if (!file_exists(git_path_todo_file()))
                return continue_single_pick();
        read_populate_opts(&opts);
        read_populate_todo(&todo_list, opts);
  
        /* Verify that the conflict has been resolved */
 -      if (file_exists(git_path("CHERRY_PICK_HEAD")) ||
 -          file_exists(git_path("REVERT_HEAD"))) {
 +      if (file_exists(git_path_cherry_pick_head()) ||
 +          file_exists(git_path_revert_head())) {
                int ret = continue_single_pick();
                if (ret)
                        return ret;