Merge branch 'jk/interpret-branch-name'
authorJunio C Hamano <gitster@pobox.com>
Tue, 14 Mar 2017 22:23:18 +0000 (15:23 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 14 Mar 2017 22:23:18 +0000 (15:23 -0700)
"git branch @" created refs/heads/@ as a branch, and in general the
code that handled @{-1} and @{upstream} was a bit too loose in
disambiguating.

* jk/interpret-branch-name:
checkout: restrict @-expansions when finding branch
strbuf_check_ref_format(): expand only local branches
branch: restrict @-expansions when deleting
t3204: test git-branch @-expansion corner cases
interpret_branch_name: allow callers to restrict expansions
strbuf_branchname: add docstring
strbuf_branchname: drop return value
interpret_branch_name: move docstring to header file
interpret_branch_name(): handle auto-namelen for @{-1}

1  2 
builtin/branch.c
builtin/checkout.c
builtin/merge.c
cache.h
refs.c
strbuf.h
diff --combined builtin/branch.c
index 94f7de7fa5da4a27dab99c582c5dfbbf25169048,0c924612ebd1ef4f84f82e6028bf04547f5f4b9d..291fe90de3f2f76db3add485e7dcf368b9ed35f4
@@@ -28,7 -28,6 +28,7 @@@ static const char * const builtin_branc
        N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
        N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
        N_("git branch [<options>] [-r | -a] [--points-at]"),
 +      N_("git branch [<options>] [-r | -a] [--format]"),
        NULL
  };
  
@@@ -38,11 -37,11 +38,11 @@@ static unsigned char head_sha1[20]
  static int branch_use_color = -1;
  static char branch_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
 -      GIT_COLOR_NORMAL,       /* PLAIN */
 -      GIT_COLOR_RED,          /* REMOTE */
 -      GIT_COLOR_NORMAL,       /* LOCAL */
 -      GIT_COLOR_GREEN,        /* CURRENT */
 -      GIT_COLOR_BLUE,         /* UPSTREAM */
 +      GIT_COLOR_NORMAL,       /* PLAIN */
 +      GIT_COLOR_RED,          /* REMOTE */
 +      GIT_COLOR_NORMAL,       /* LOCAL */
 +      GIT_COLOR_GREEN,        /* CURRENT */
 +      GIT_COLOR_BLUE,         /* UPSTREAM */
  };
  enum color_branch {
        BRANCH_COLOR_RESET = 0,
@@@ -191,17 -190,20 +191,20 @@@ static int delete_branches(int argc, co
        int ret = 0;
        int remote_branch = 0;
        struct strbuf bname = STRBUF_INIT;
+       unsigned allowed_interpret;
  
        switch (kinds) {
        case FILTER_REFS_REMOTES:
                fmt = "refs/remotes/%s";
                /* For subsequent UI messages */
                remote_branch = 1;
+               allowed_interpret = INTERPRET_BRANCH_REMOTE;
  
                force = 1;
                break;
        case FILTER_REFS_BRANCHES:
                fmt = "refs/heads/%s";
+               allowed_interpret = INTERPRET_BRANCH_LOCAL;
                break;
        default:
                die(_("cannot use -a with -d"));
                char *target = NULL;
                int flags = 0;
  
-               strbuf_branchname(&bname, argv[i]);
+               strbuf_branchname(&bname, argv[i], allowed_interpret);
                free(name);
                name = mkpathdup(fmt, bname.buf);
  
                        goto next;
                }
  
 -              if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
 +              if (delete_ref(NULL, name, is_null_sha1(sha1) ? NULL : sha1,
                               REF_NODEREF)) {
                        error(remote_branch
                              ? _("Error deleting remote-tracking branch '%s'")
        return(ret);
  }
  
 -static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
 -              int show_upstream_ref)
 +static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
  {
 -      int ours, theirs;
 -      char *ref = NULL;
 -      struct branch *branch = branch_get(branch_name);
 -      const char *upstream;
 -      struct strbuf fancy = STRBUF_INIT;
 -      int upstream_is_gone = 0;
 -      int added_decoration = 1;
 -
 -      if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {
 -              if (!upstream)
 -                      return;
 -              upstream_is_gone = 1;
 -      }
 -
 -      if (show_upstream_ref) {
 -              ref = shorten_unambiguous_ref(upstream, 0);
 -              if (want_color(branch_use_color))
 -                      strbuf_addf(&fancy, "%s%s%s",
 -                                      branch_get_color(BRANCH_COLOR_UPSTREAM),
 -                                      ref, branch_get_color(BRANCH_COLOR_RESET));
 -              else
 -                      strbuf_addstr(&fancy, ref);
 -      }
 +      int i, max = 0;
 +      for (i = 0; i < refs->nr; i++) {
 +              struct ref_array_item *it = refs->items[i];
 +              const char *desc = it->refname;
 +              int w;
  
 -      if (upstream_is_gone) {
 -              if (show_upstream_ref)
 -                      strbuf_addf(stat, _("[%s: gone]"), fancy.buf);
 -              else
 -                      added_decoration = 0;
 -      } else if (!ours && !theirs) {
 -              if (show_upstream_ref)
 -                      strbuf_addf(stat, _("[%s]"), fancy.buf);
 -              else
 -                      added_decoration = 0;
 -      } else if (!ours) {
 -              if (show_upstream_ref)
 -                      strbuf_addf(stat, _("[%s: behind %d]"), fancy.buf, theirs);
 -              else
 -                      strbuf_addf(stat, _("[behind %d]"), theirs);
 +              skip_prefix(it->refname, "refs/heads/", &desc);
 +              skip_prefix(it->refname, "refs/remotes/", &desc);
 +              if (it->kind == FILTER_REFS_DETACHED_HEAD) {
 +                      char *head_desc = get_head_description();
 +                      w = utf8_strwidth(head_desc);
 +                      free(head_desc);
 +              } else
 +                      w = utf8_strwidth(desc);
  
 -      } else if (!theirs) {
 -              if (show_upstream_ref)
 -                      strbuf_addf(stat, _("[%s: ahead %d]"), fancy.buf, ours);
 -              else
 -                      strbuf_addf(stat, _("[ahead %d]"), ours);
 -      } else {
 -              if (show_upstream_ref)
 -                      strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
 -                                  fancy.buf, ours, theirs);
 -              else
 -                      strbuf_addf(stat, _("[ahead %d, behind %d]"),
 -                                  ours, theirs);
 +              if (it->kind == FILTER_REFS_REMOTES)
 +                      w += remote_bonus;
 +              if (w > max)
 +                      max = w;
        }
 -      strbuf_release(&fancy);
 -      if (added_decoration)
 -              strbuf_addch(stat, ' ');
 -      free(ref);
 +      return max;
  }
  
 -static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
 -                           struct ref_filter *filter, const char *refname)
 +static const char *quote_literal_for_format(const char *s)
  {
 -      struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
 -      const char *sub = _(" **** invalid ref ****");
 -      struct commit *commit = item->commit;
 +      static struct strbuf buf = STRBUF_INIT;
  
 -      if (!parse_commit(commit)) {
 -              pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
 -              sub = subject.buf;
 +      strbuf_reset(&buf);
 +      while (*s) {
 +              const char *ep = strchrnul(s, '%');
 +              if (s < ep)
 +                      strbuf_add(&buf, s, ep - s);
 +              if (*ep == '%') {
 +                      strbuf_addstr(&buf, "%%");
 +                      s = ep + 1;
 +              } else {
 +                      s = ep;
 +              }
        }
 -
 -      if (item->kind == FILTER_REFS_BRANCHES)
 -              fill_tracking_info(&stat, refname, filter->verbose > 1);
 -
 -      strbuf_addf(out, " %s %s%s",
 -              find_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),
 -              stat.buf, sub);
 -      strbuf_release(&stat);
 -      strbuf_release(&subject);
 +      return buf.buf;
  }
  
 -static char *get_head_description(void)
 +static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)
  {
 -      struct strbuf desc = STRBUF_INIT;
 -      struct wt_status_state state;
 -      memset(&state, 0, sizeof(state));
 -      wt_status_get_state(&state, 1);
 -      if (state.rebase_in_progress ||
 -          state.rebase_interactive_in_progress)
 -              strbuf_addf(&desc, _("(no branch, rebasing %s)"),
 -                          state.branch);
 -      else if (state.bisect_in_progress)
 -              strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
 -                          state.branch);
 -      else if (state.detached_from) {
 -              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);
 -      }
 -      else
 -              strbuf_addstr(&desc, _("(no branch)"));
 -      free(state.branch);
 -      free(state.onto);
 -      free(state.detached_from);
 -      return strbuf_detach(&desc, NULL);
 -}
 +      struct strbuf fmt = STRBUF_INIT;
 +      struct strbuf local = STRBUF_INIT;
 +      struct strbuf remote = STRBUF_INIT;
  
 -static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
 -                                    struct ref_filter *filter, const char *remote_prefix)
 -{
 -      char c;
 -      int current = 0;
 -      int color;
 -      struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
 -      const char *prefix_to_show = "";
 -      const char *prefix_to_skip = NULL;
 -      const char *desc = item->refname;
 -      char *to_free = NULL;
 +      strbuf_addf(&fmt, "%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)",
 +                  branch_get_color(BRANCH_COLOR_CURRENT));
  
 -      switch (item->kind) {
 -      case FILTER_REFS_BRANCHES:
 -              prefix_to_skip = "refs/heads/";
 -              skip_prefix(desc, prefix_to_skip, &desc);
 -              if (!filter->detached && !strcmp(desc, head))
 -                      current = 1;
 +      if (filter->verbose) {
 +              strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
 +              strbuf_addf(&local, "%s", branch_get_color(BRANCH_COLOR_RESET));
 +              strbuf_addf(&local, " %%(objectname:short=7) ");
 +
 +              if (filter->verbose > 1)
 +                      strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
 +                                  "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
 +                                  branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
                else
 -                      color = BRANCH_COLOR_LOCAL;
 -              break;
 -      case FILTER_REFS_REMOTES:
 -              prefix_to_skip = "refs/remotes/";
 -              skip_prefix(desc, prefix_to_skip, &desc);
 -              color = BRANCH_COLOR_REMOTE;
 -              prefix_to_show = remote_prefix;
 -              break;
 -      case FILTER_REFS_DETACHED_HEAD:
 -              desc = to_free = get_head_description();
 -              current = 1;
 -              break;
 -      default:
 -              color = BRANCH_COLOR_PLAIN;
 -              break;
 -      }
 -
 -      c = ' ';
 -      if (current) {
 -              c = '*';
 -              color = BRANCH_COLOR_CURRENT;
 -      }
 +                      strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
  
 -      strbuf_addf(&name, "%s%s", prefix_to_show, desc);
 -      if (filter->verbose) {
 -              int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
 -              strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
 -                          maxwidth + utf8_compensation, name.buf,
 +              strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)"
 +                          "%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)",
 +                          branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),
                            branch_get_color(BRANCH_COLOR_RESET));
 -      } else
 -              strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
 -                          name.buf, branch_get_color(BRANCH_COLOR_RESET));
 -
 -      if (item->symref) {
 -              const char *symref = item->symref;
 -              if (prefix_to_skip)
 -                      skip_prefix(symref, prefix_to_skip, &symref);
 -              strbuf_addf(&out, " -> %s", symref);
 -      }
 -      else if (filter->verbose)
 -              /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
 -              add_verbose_info(&out, item, filter, desc);
 -      if (column_active(colopts)) {
 -              assert(!filter->verbose && "--column and --verbose are incompatible");
 -              string_list_append(&output, out.buf);
        } else {
 -              printf("%s\n", out.buf);
 +              strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
 +                          branch_get_color(BRANCH_COLOR_RESET));
 +              strbuf_addf(&remote, "%s%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
 +                          branch_get_color(BRANCH_COLOR_REMOTE), quote_literal_for_format(remote_prefix),
 +                          branch_get_color(BRANCH_COLOR_RESET));
        }
 -      strbuf_release(&name);
 -      strbuf_release(&out);
 -      free(to_free);
 -}
  
 -static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
 -{
 -      int i, max = 0;
 -      for (i = 0; i < refs->nr; i++) {
 -              struct ref_array_item *it = refs->items[i];
 -              const char *desc = it->refname;
 -              int w;
 -
 -              skip_prefix(it->refname, "refs/heads/", &desc);
 -              skip_prefix(it->refname, "refs/remotes/", &desc);
 -              w = utf8_strwidth(desc);
 +      strbuf_addf(&fmt, "%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)", local.buf, remote.buf);
  
 -              if (it->kind == FILTER_REFS_REMOTES)
 -                      w += remote_bonus;
 -              if (w > max)
 -                      max = w;
 -      }
 -      return max;
 +      strbuf_release(&local);
 +      strbuf_release(&remote);
 +      return strbuf_detach(&fmt, NULL);
  }
  
 -static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)
 +static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
  {
        int i;
        struct ref_array array;
        int maxwidth = 0;
        const char *remote_prefix = "";
 +      struct strbuf out = STRBUF_INIT;
 +      char *to_free = NULL;
  
        /*
         * If we are listing more than just remote branches,
  
        memset(&array, 0, sizeof(array));
  
 -      verify_ref_format("%(refname)%(symref)");
        filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
  
        if (filter->verbose)
                maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
  
 -      /*
 -       * If no sorting parameter is given then we default to sorting
 -       * by 'refname'. This would give us an alphabetically sorted
 -       * array with the 'HEAD' ref at the beginning followed by
 -       * local branches 'refs/heads/...' and finally remote-tacking
 -       * branches 'refs/remotes/...'.
 -       */
 -      if (!sorting)
 -              sorting = ref_default_sorting();
 +      if (!format)
 +              format = to_free = build_format(filter, maxwidth, remote_prefix);
 +      verify_ref_format(format);
 +
        ref_array_sort(sorting, &array);
  
 -      for (i = 0; i < array.nr; i++)
 -              format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
 +      for (i = 0; i < array.nr; i++) {
 +              format_ref_array_item(array.items[i], format, 0, &out);
 +              if (column_active(colopts)) {
 +                      assert(!filter->verbose && "--column and --verbose are incompatible");
 +                       /* format to a string_list to let print_columns() do its job */
 +                      string_list_append(&output, out.buf);
 +              } else {
 +                      fwrite(out.buf, 1, out.len, stdout);
 +                      putchar('\n');
 +              }
 +              strbuf_release(&out);
 +      }
  
        ref_array_clear(&array);
 +      free(to_free);
  }
  
  static void reject_rebase_or_bisect_branch(const char *target)
@@@ -471,15 -591,14 +474,15 @@@ static void rename_branch(const char *o
  
        if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
                die(_("Branch rename failed"));
 -      strbuf_release(&logmsg);
  
        if (recovery)
                warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
  
 -      if (replace_each_worktree_head_symref(oldref.buf, newref.buf))
 +      if (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
                die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
  
 +      strbuf_release(&logmsg);
 +
        strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
        strbuf_release(&oldref);
        strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
@@@ -529,9 -648,7 +532,9 @@@ int cmd_branch(int argc, const char **a
        const char *new_upstream = NULL;
        enum branch_track track;
        struct ref_filter filter;
 +      int icase = 0;
        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
 +      const char *format = NULL;
  
        struct option options[] = {
                OPT_GROUP(N_("Generic options")),
                        OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
                        N_("print only branches of the object"), 0, parse_opt_object_name
                },
 +              OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
 +              OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
                OPT_END(),
        };
  
 +      setup_ref_filter_porcelain_msg();
 +
        memset(&filter, 0, sizeof(filter));
        filter.kind = FILTER_REFS_BRANCHES;
        filter.abbrev = -1;
  
        if (filter.abbrev == -1)
                filter.abbrev = DEFAULT_ABBREV;
 +      filter.ignore_case = icase;
 +
        finalize_colopts(&colopts, -1);
        if (filter.verbose) {
                if (explicitly_enable_column(colopts))
                if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
                        filter.kind |= FILTER_REFS_DETACHED_HEAD;
                filter.name_patterns = argv;
 -              print_ref_list(&filter, sorting);
 +              /*
 +               * If no sorting parameter is given then we default to sorting
 +               * by 'refname'. This would give us an alphabetically sorted
 +               * array with the 'HEAD' ref at the beginning followed by
 +               * local branches 'refs/heads/...' and finally remote-tacking
 +               * branches 'refs/remotes/...'.
 +               */
 +              if (!sorting)
 +                      sorting = ref_default_sorting();
 +              sorting->ignore_case = icase;
 +              print_ref_list(&filter, sorting, format);
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 0);
                return 0;
diff --combined builtin/checkout.c
index f174f503033ea7f1274fc8f09c49c0ae378c9eee,767ca9e18c2d3c859e31117cb0d2583b24ffe496..81f07c3ef271db08e36ee7a89b1d128d099ecb96
@@@ -56,8 -56,8 +56,8 @@@ static int post_checkout_hook(struct co
                              int changed)
  {
        return run_hook_le(NULL, "post-checkout",
 -                         sha1_to_hex(old ? old->object.oid.hash : null_sha1),
 -                         sha1_to_hex(new ? new->object.oid.hash : null_sha1),
 +                         oid_to_hex(old ? &old->object.oid : &null_oid),
 +                         oid_to_hex(new ? &new->object.oid : &null_oid),
                           changed ? "1" : "0", NULL);
        /* "new" can be NULL when checking out from the index before
           a commit exists. */
@@@ -274,7 -274,7 +274,7 @@@ static int checkout_paths(const struct 
  
        lock_file = xcalloc(1, sizeof(struct lock_file));
  
 -      hold_locked_index(lock_file, 1);
 +      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(&opts->pathspec) < 0)
                return error(_("index file corrupt"));
  
@@@ -452,7 -452,7 +452,7 @@@ static void setup_branch_path(struct br
  {
        struct strbuf buf = STRBUF_INIT;
  
-       strbuf_branchname(&buf, branch->name);
+       strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
        if (strcmp(buf.buf, branch->name))
                branch->name = xstrdup(buf.buf);
        strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
@@@ -467,7 -467,7 +467,7 @@@ static int merge_working_tree(const str
        int ret;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
  
 -      hold_locked_index(lock_file, 1);
 +      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(NULL) < 0)
                return error(_("index file corrupt"));
  
@@@ -612,25 -612,22 +612,25 @@@ static void update_refs_for_switch(cons
        const char *old_desc, *reflog_msg;
        if (opts->new_branch) {
                if (opts->new_orphan_branch) {
 -                      if (opts->new_branch_log && !log_all_ref_updates) {
 +                      char *refname;
 +
 +                      refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
 +                      if (opts->new_branch_log &&
 +                          !should_autocreate_reflog(refname)) {
                                int ret;
 -                              char *refname;
                                struct strbuf err = STRBUF_INIT;
  
 -                              refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
                                ret = safe_create_reflog(refname, 1, &err);
 -                              free(refname);
                                if (ret) {
                                        fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
                                                opts->new_orphan_branch, err.buf);
                                        strbuf_release(&err);
 +                                      free(refname);
                                        return;
                                }
                                strbuf_release(&err);
                        }
 +                      free(refname);
                }
                else
                        create_branch(opts->new_branch, new->name,
diff --combined builtin/merge.c
index a96d4fb501bf1441b52a313313b8c04f3187e4d9,84375db71408827c0002aa0cd3ba7b6aad86c4d2..848a29855611d22bf19f3740a577a2b312294257
@@@ -46,7 -46,6 +46,7 @@@ static const char * const builtin_merge
        N_("git merge [<options>] [<commit>...]"),
        N_("git merge [<options>] <msg> HEAD <commit>"),
        N_("git merge --abort"),
 +      N_("git merge --continue"),
        NULL
  };
  
@@@ -66,7 -65,6 +66,7 @@@ static int option_renormalize
  static int verbosity;
  static int allow_rerere_auto;
  static int abort_current_merge;
 +static int continue_current_merge;
  static int allow_unrelated_histories;
  static int show_progress = -1;
  static int default_to_upstream = 1;
@@@ -225,8 -223,6 +225,8 @@@ static struct option builtin_merge_opti
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "abort", &abort_current_merge,
                N_("abort the current in-progress merge")),
 +      OPT_BOOL(0, "continue", &continue_current_merge,
 +              N_("continue the current in-progress merge")),
        OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
                 N_("allow merging unrelated histories")),
        OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
@@@ -438,7 -434,7 +438,7 @@@ static void merge_name(const char *remo
        char *found_ref;
        int len, early;
  
-       strbuf_branchname(&bname, remote);
+       strbuf_branchname(&bname, remote, 0);
        remote = bname.buf;
  
        memset(branch_head, 0, sizeof(branch_head));
@@@ -638,7 -634,7 +638,7 @@@ static int try_merge_strategy(const cha
  {
        static struct lock_file lock;
  
 -      hold_locked_index(&lock, 1);
 +      hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
        if (active_cache_changed &&
            write_locked_index(&the_index, &lock, COMMIT_LOCK))
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
  
 -              hold_locked_index(&lock, 1);
 +              hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
                clean = merge_recursive(&o, head,
                                remoteheads->item, reversed, &result);
                if (clean < 0)
@@@ -785,7 -781,7 +785,7 @@@ static int merge_trivial(struct commit 
        struct commit_list *parents, **pptr = &parents;
        static struct lock_file lock;
  
 -      hold_locked_index(&lock, 1);
 +      hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
        if (active_cache_changed &&
            write_locked_index(&the_index, &lock, COMMIT_LOCK))
@@@ -1129,7 -1125,6 +1129,7 @@@ int cmd_merge(int argc, const char **ar
        const char *best_strategy = NULL, *wt_strategy = NULL;
        struct commit_list *remoteheads, *p;
        void *branch_to_free;
 +      int orig_argc = argc;
  
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_merge_usage, builtin_merge_options);
                int nargc = 2;
                const char *nargv[] = {"reset", "--merge", NULL};
  
 +              if (orig_argc != 2)
 +                      usage_msg_opt(_("--abort expects no arguments"),
 +                            builtin_merge_usage, builtin_merge_options);
 +
                if (!file_exists(git_path_merge_head()))
                        die(_("There is no merge to abort (MERGE_HEAD missing)."));
  
                goto done;
        }
  
 +      if (continue_current_merge) {
 +              int nargc = 1;
 +              const char *nargv[] = {"commit", NULL};
 +
 +              if (orig_argc != 2)
 +                      usage_msg_opt(_("--continue expects no arguments"),
 +                            builtin_merge_usage, builtin_merge_options);
 +
 +              if (!file_exists(git_path_merge_head()))
 +                      die(_("There is no merge in progress (MERGE_HEAD missing)."));
 +
 +              /* Invoke 'git commit' */
 +              ret = cmd_commit(nargc, nargv, prefix);
 +              goto done;
 +      }
 +
        if (read_cache_unmerged())
                die_resolve_conflict("merge");
  
diff --combined cache.h
index 8c0e6442076eb91571dc0c5cf55db478ab47d2ad,7aea88534d06de7fe973ac66b9e941b8d6fceea9..c958269715cc73e218604f6c3b82f547d376b050
+++ b/cache.h
@@@ -507,16 -507,14 +507,16 @@@ extern int is_nonbare_repository_dir(st
  #define READ_GITFILE_ERR_NO_PATH 6
  #define READ_GITFILE_ERR_NOT_A_REPO 7
  #define READ_GITFILE_ERR_TOO_LARGE 8
 +extern void read_gitfile_error_die(int error_code, const char *path, const char *dir);
  extern const char *read_gitfile_gently(const char *path, int *return_error_code);
  #define read_gitfile(path) read_gitfile_gently((path), NULL)
 -extern const char *resolve_gitdir(const char *suspect);
 +extern const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
 +#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
 +
  extern void set_git_work_tree(const char *tree);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
 -extern const char **get_pathspec(const char *prefix, const char **pathspec);
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
@@@ -633,7 -631,7 +633,7 @@@ extern int chmod_index_entry(struct ind
  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);
  extern int index_name_is_other(const struct index_state *, const char *, int);
 -extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
 +extern void *read_blob_data_from_index(const struct index_state *, const char *, unsigned long *);
  
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
@@@ -695,6 -693,7 +695,6 @@@ extern int minimum_abbrev, default_abbr
  extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
 -extern int log_all_ref_updates;
  extern int warn_ambiguous_refs;
  extern int warn_on_object_refname_ambiguity;
  extern const char *apply_default_whitespace;
@@@ -762,14 -761,6 +762,14 @@@ enum hide_dotfiles_type 
  };
  extern enum hide_dotfiles_type hide_dotfiles;
  
 +enum log_refs_config {
 +      LOG_REFS_UNSET = -1,
 +      LOG_REFS_NONE = 0,
 +      LOG_REFS_NORMAL,
 +      LOG_REFS_ALWAYS
 +};
 +extern enum log_refs_config log_all_ref_updates;
 +
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@@ -1045,6 -1036,9 +1045,6 @@@ static inline int is_empty_tree_oid(con
        return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
  }
  
 -
 -int git_mkstemp(char *path, size_t n, const char *template);
 -
  /* 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);
@@@ -1069,9 -1063,8 +1069,9 @@@ int adjust_shared_perm(const char *path
  
  /*
   * Create the directory containing the named path, using care to be
 - * somewhat safe against races.  Return one of the scld_error values
 - * to indicate success/failure.
 + * somewhat safe against races. Return one of the scld_error values to
 + * indicate success/failure. On error, set errno to describe the
 + * problem.
   *
   * SCLD_VANISHED indicates that one of the ancestor directories of the
   * path existed at one point during the function call and then
@@@ -1095,49 -1088,6 +1095,49 @@@ enum scld_error 
  enum scld_error safe_create_leading_directories(char *path);
  enum scld_error safe_create_leading_directories_const(const char *path);
  
 +/*
 + * Callback function for raceproof_create_file(). This function is
 + * expected to do something that makes dirname(path) permanent despite
 + * the fact that other processes might be cleaning up empty
 + * directories at the same time. Usually it will create a file named
 + * path, but alternatively it could create another file in that
 + * directory, or even chdir() into that directory. The function should
 + * return 0 if the action was completed successfully. On error, it
 + * should return a nonzero result and set errno.
 + * raceproof_create_file() treats two errno values specially:
 + *
 + * - ENOENT -- dirname(path) does not exist. In this case,
 + *             raceproof_create_file() tries creating dirname(path)
 + *             (and any parent directories, if necessary) and calls
 + *             the function again.
 + *
 + * - EISDIR -- the file already exists and is a directory. In this
 + *             case, raceproof_create_file() removes the directory if
 + *             it is empty (and recursively any empty directories that
 + *             it contains) and calls the function again.
 + *
 + * Any other errno causes raceproof_create_file() to fail with the
 + * callback's return value and errno.
 + *
 + * Obviously, this function should be OK with being called again if it
 + * fails with ENOENT or EISDIR. In other scenarios it will not be
 + * called again.
 + */
 +typedef int create_file_fn(const char *path, void *cb);
 +
 +/*
 + * Create a file in dirname(path) by calling fn, creating leading
 + * directories if necessary. Retry a few times in case we are racing
 + * with another process that is trying to clean up the directory that
 + * contains path. See the documentation for create_file_fn for more
 + * details.
 + *
 + * Return the value and set the errno that resulted from the most
 + * recent call of fn. fn is always called at least once, and will be
 + * called more than once if it returns ENOENT or EISDIR.
 + */
 +int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
 +
  int mkdir_in_gitdir(const char *path);
  extern char *expand_user_path(const char *path);
  const char *enter_repo(const char *path, int strict);
@@@ -1146,13 -1096,9 +1146,13 @@@ static inline int is_absolute_path(cons
        return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
 +char *strbuf_realpath(struct strbuf *resolved, const char *path,
 +                    int die_on_error);
  const char *real_path(const char *path);
  const char *real_path_if_valid(const char *path);
 +char *real_pathdup(const char *path, int die_on_error);
  const char *absolute_path(const char *path);
 +char *absolute_pathdup(const char *path);
  const char *remove_leading_path(const char *in, const char *prefix);
  const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
  int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
@@@ -1211,8 -1157,7 +1211,8 @@@ extern int write_sha1_file(const void *
  extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 -extern int git_open(const char *name);
 +extern int git_open_cloexec(const char *name, int flags);
 +#define git_open(name) git_open_cloexec(name, O_RDONLY)
  extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
@@@ -1226,19 -1171,6 +1226,19 @@@ extern int finalize_object_file(const c
  
  extern int has_sha1_pack(const unsigned char *sha1);
  
 +/*
 + * Open the loose object at path, check its sha1, and return the contents,
 + * type, and size. If the object is a blob, then "contents" may return NULL,
 + * to allow streaming of large blobs.
 + *
 + * Returns 0 on success, negative on error (details may be written to stderr).
 + */
 +int read_loose_object(const char *path,
 +                    const unsigned char *expected_sha1,
 +                    enum object_type *type,
 +                    unsigned long *size,
 +                    void **contents);
 +
  /*
   * Return true iff we have an object named sha1, whether local or in
   * an alternate object database, and whether packed or loose.  This
@@@ -1360,7 -1292,37 +1360,37 @@@ extern char *oid_to_hex_r(char *out, co
  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 */
  
- extern int interpret_branch_name(const char *str, int len, struct strbuf *);
+ /*
+  * This reads short-hand syntax that not only evaluates to a commit
+  * object name, but also can act as if the end user spelled the name
+  * of the branch from the command line.
+  *
+  * - "@{-N}" finds the name of the Nth previous branch we were on, and
+  *   places the name of the branch in the given buf and returns the
+  *   number of characters parsed if successful.
+  *
+  * - "<branch>@{upstream}" finds the name of the other ref that
+  *   <branch> is configured to merge with (missing <branch> defaults
+  *   to the current branch), and places the name of the branch in the
+  *   given buf and returns the number of characters parsed if
+  *   successful.
+  *
+  * If the input is not of the accepted format, it returns a negative
+  * number to signal an error.
+  *
+  * If the input was ok but there are not N branch switches in the
+  * reflog, it returns 0.
+  *
+  * If "allowed" is non-zero, it is a treated as a bitfield of allowable
+  * expansions: local branches ("refs/heads/"), remote branches
+  * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
+  * allowed, even ones to refs outside of those namespaces.
+  */
+ #define INTERPRET_BRANCH_LOCAL (1<<0)
+ #define INTERPRET_BRANCH_REMOTE (1<<1)
+ #define INTERPRET_BRANCH_HEAD (1<<2)
+ extern int interpret_branch_name(const char *str, int len, struct strbuf *,
+                                unsigned allowed);
  extern int get_oid_mb(const char *str, struct object_id *oid);
  
  extern int validate_headref(const char *ref);
@@@ -1790,8 -1752,6 +1820,8 @@@ extern int git_default_config(const cha
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
  extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
                                        const char *name, const char *buf, size_t len, void *data);
 +extern int git_config_from_blob_sha1(config_fn_t fn, const char *name,
 +                                   const unsigned char *sha1, void *data);
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern void git_config(config_fn_t fn, void *);
@@@ -1860,11 -1820,8 +1890,11 @@@ extern int git_config_include(const cha
   *
   * (i.e., what gets handed to a config_fn_t). The caller provides the section;
   * we return -1 if it does not match, 0 otherwise. The subsection and key
 - * out-parameters are filled by the function (and subsection is NULL if it is
 + * out-parameters are filled by the function (and *subsection is NULL if it is
   * missing).
 + *
 + * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if
 + * there is no subsection at all.
   */
  extern int parse_config_key(const char *var,
                            const char *section,
diff --combined refs.c
index 4d6bf9237b5947c93dd366ccc1511f2a73649ae6,da847f6901ba50ba51f793b5b0462c0d268f4046..b0d58948a5b5bb1a96c43f0363408aab070bd1a9
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -3,7 -3,6 +3,7 @@@
   */
  
  #include "cache.h"
 +#include "hashmap.h"
  #include "lockfile.h"
  #include "refs.h"
  #include "refs/refs-internal.h"
@@@ -405,7 -404,7 +405,7 @@@ int refname_match(const char *abbrev_na
  static char *substitute_branch_name(const char **string, int *len)
  {
        struct strbuf buf = STRBUF_INIT;
-       int ret = interpret_branch_name(*string, *len, &buf);
+       int ret = interpret_branch_name(*string, *len, &buf, 0);
  
        if (ret == *len) {
                size_t size;
@@@ -592,8 -591,8 +592,8 @@@ static int delete_pseudoref(const char 
        return 0;
  }
  
 -int delete_ref(const char *refname, const unsigned char *old_sha1,
 -             unsigned int flags)
 +int delete_ref(const char *msg, const char *refname,
 +             const unsigned char *old_sha1, unsigned int flags)
  {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_delete(transaction, refname, old_sha1,
 -                                 flags, NULL, &err) ||
 +                                 flags, msg, &err) ||
            ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
                ref_transaction_free(transaction);
@@@ -639,17 -638,12 +639,17 @@@ int copy_reflog_msg(char *buf, const ch
  
  int should_autocreate_reflog(const char *refname)
  {
 -      if (!log_all_ref_updates)
 +      switch (log_all_ref_updates) {
 +      case LOG_REFS_ALWAYS:
 +              return 1;
 +      case LOG_REFS_NORMAL:
 +              return starts_with(refname, "refs/heads/") ||
 +                      starts_with(refname, "refs/remotes/") ||
 +                      starts_with(refname, "refs/notes/") ||
 +                      !strcmp(refname, "HEAD");
 +      default:
                return 0;
 -      return starts_with(refname, "refs/heads/") ||
 -              starts_with(refname, "refs/remotes/") ||
 -              starts_with(refname, "refs/notes/") ||
 -              !strcmp(refname, "HEAD");
 +      }
  }
  
  int is_branch(const char *refname)
@@@ -1035,10 -1029,10 +1035,10 @@@ static struct string_list *hide_refs
  
  int parse_hide_refs_config(const char *var, const char *value, const char *section)
  {
 +      const char *key;
        if (!strcmp("transfer.hiderefs", var) ||
 -          /* NEEDSWORK: use parse_config_key() once both are merged */
 -          (starts_with(var, section) && var[strlen(section)] == '.' &&
 -           !strcmp(var + strlen(section), ".hiderefs"))) {
 +          (!parse_config_key(var, section, NULL, NULL, &key) &&
 +           !strcmp(key, "hiderefs"))) {
                char *ref;
                int len;
  
@@@ -1235,10 -1229,10 +1235,10 @@@ int for_each_rawref(each_ref_fn fn, voi
  }
  
  /* This function needs to return a meaningful errno on failure */
 -static const char *resolve_ref_recursively(struct ref_store *refs,
 -                                         const char *refname,
 -                                         int resolve_flags,
 -                                         unsigned char *sha1, int *flags)
 +const char *resolve_ref_recursively(struct ref_store *refs,
 +                                  const char *refname,
 +                                  int resolve_flags,
 +                                  unsigned char *sha1, int *flags)
  {
        static struct strbuf sb_refname = STRBUF_INIT;
        int unused_flags;
@@@ -1358,102 -1352,62 +1358,102 @@@ int resolve_gitlink_ref(const char *sub
        return 0;
  }
  
 +struct submodule_hash_entry
 +{
 +      struct hashmap_entry ent; /* must be the first member! */
 +
 +      struct ref_store *refs;
 +
 +      /* NUL-terminated name of submodule: */
 +      char submodule[FLEX_ARRAY];
 +};
 +
 +static int submodule_hash_cmp(const void *entry, const void *entry_or_key,
 +                            const void *keydata)
 +{
 +      const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key;
 +      const char *submodule = keydata ? keydata : e2->submodule;
 +
 +      return strcmp(e1->submodule, submodule);
 +}
 +
 +static struct submodule_hash_entry *alloc_submodule_hash_entry(
 +              const char *submodule, struct ref_store *refs)
 +{
 +      struct submodule_hash_entry *entry;
 +
 +      FLEX_ALLOC_STR(entry, submodule, submodule);
 +      hashmap_entry_init(entry, strhash(submodule));
 +      entry->refs = refs;
 +      return entry;
 +}
 +
  /* A pointer to the ref_store for the main repository: */
  static struct ref_store *main_ref_store;
  
 -/* A linked list of ref_stores for submodules: */
 -static struct ref_store *submodule_ref_stores;
 +/* A hashmap of ref_stores, stored by submodule name: */
 +static struct hashmap submodule_ref_stores;
  
 -void base_ref_store_init(struct ref_store *refs,
 -                       const struct ref_storage_be *be,
 -                       const char *submodule)
 +/*
 + * Return the ref_store instance for the specified submodule (or the
 + * main repository if submodule is NULL). If that ref_store hasn't
 + * been initialized yet, return NULL.
 + */
 +static struct ref_store *lookup_ref_store(const char *submodule)
 +{
 +      struct submodule_hash_entry *entry;
 +
 +      if (!submodule)
 +              return main_ref_store;
 +
 +      if (!submodule_ref_stores.tablesize)
 +              /* It's initialized on demand in register_ref_store(). */
 +              return NULL;
 +
 +      entry = hashmap_get_from_hash(&submodule_ref_stores,
 +                                    strhash(submodule), submodule);
 +      return entry ? entry->refs : NULL;
 +}
 +
 +/*
 + * Register the specified ref_store to be the one that should be used
 + * for submodule (or the main repository if submodule is NULL). It is
 + * a fatal error to call this function twice for the same submodule.
 + */
 +static void register_ref_store(struct ref_store *refs, const char *submodule)
  {
 -      refs->be = be;
        if (!submodule) {
                if (main_ref_store)
                        die("BUG: main_ref_store initialized twice");
  
 -              refs->submodule = "";
 -              refs->next = NULL;
                main_ref_store = refs;
        } else {
 -              if (lookup_ref_store(submodule))
 +              if (!submodule_ref_stores.tablesize)
 +                      hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);
 +
 +              if (hashmap_put(&submodule_ref_stores,
 +                              alloc_submodule_hash_entry(submodule, refs)))
                        die("BUG: ref_store for submodule '%s' initialized twice",
                            submodule);
 -
 -              refs->submodule = xstrdup(submodule);
 -              refs->next = submodule_ref_stores;
 -              submodule_ref_stores = refs;
        }
  }
  
 -struct ref_store *ref_store_init(const char *submodule)
 +/*
 + * Create, record, and return a ref_store instance for the specified
 + * submodule (or the main repository if submodule is NULL).
 + */
 +static struct ref_store *ref_store_init(const char *submodule)
  {
        const char *be_name = "files";
        struct ref_storage_be *be = find_ref_storage_backend(be_name);
 +      struct ref_store *refs;
  
        if (!be)
                die("BUG: reference backend %s is unknown", be_name);
  
 -      if (!submodule || !*submodule)
 -              return be->init(NULL);
 -      else
 -              return be->init(submodule);
 -}
 -
 -struct ref_store *lookup_ref_store(const char *submodule)
 -{
 -      struct ref_store *refs;
 -
 -      if (!submodule || !*submodule)
 -              return main_ref_store;
 -
 -      for (refs = submodule_ref_stores; refs; refs = refs->next) {
 -              if (!strcmp(submodule, refs->submodule))
 -                      return refs;
 -      }
 -
 -      return NULL;
 +      refs = be->init(submodule);
 +      register_ref_store(refs, submodule);
 +      return refs;
  }
  
  struct ref_store *get_ref_store(const char *submodule)
        return refs;
  }
  
 -void assert_main_repository(struct ref_store *refs, const char *caller)
 +void base_ref_store_init(struct ref_store *refs,
 +                       const struct ref_storage_be *be)
  {
 -      if (*refs->submodule)
 -              die("BUG: %s called for a submodule", caller);
 +      refs->be = be;
  }
  
  /* backend functions */
diff --combined strbuf.h
index cf8e4bf532a63cf5133dcdf011968e9ac8b24d39,4d225ffe51fc3ebc03bdc768bc53f85ad0a09bb8..80047b1bb7b826699deff3e8c2590a2a00a5c121
+++ b/strbuf.h
@@@ -109,7 -109,9 +109,7 @@@ extern void strbuf_attach(struct strbu
   */
  static inline void strbuf_swap(struct strbuf *a, struct strbuf *b)
  {
 -      struct strbuf tmp = *a;
 -      *a = *b;
 -      *b = tmp;
 +      SWAP(*a, *b);
  }
  
  
@@@ -441,20 -443,6 +441,20 @@@ extern int strbuf_getcwd(struct strbuf 
   */
  extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
  
 +/**
 + * Canonize `path` (make it absolute, resolve symlinks, remove extra
 + * slashes) and append it to `sb`.  Die with an informative error
 + * message if there is a problem.
 + *
 + * The directory part of `path` (i.e., everything up to the last
 + * dir_sep) must denote a valid, existing directory, but the last
 + * component need not exist.
 + *
 + * Callers that don't mind links should use the more lightweight
 + * strbuf_add_absolute_path() instead.
 + */
 +extern void strbuf_add_real_path(struct strbuf *sb, const char *path);
 +
  
  /**
   * Normalize in-place the path contained in the strbuf. See
@@@ -574,7 -562,26 +574,26 @@@ static inline void strbuf_complete_line
        strbuf_complete(sb, '\n');
  }
  
- extern int strbuf_branchname(struct strbuf *sb, const char *name);
+ /*
+  * Copy "name" to "sb", expanding any special @-marks as handled by
+  * interpret_branch_name(). The result is a non-qualified branch name
+  * (so "foo" or "origin/master" instead of "refs/heads/foo" or
+  * "refs/remotes/origin/master").
+  *
+  * Note that the resulting name may not be a syntactically valid refname.
+  *
+  * If "allowed" is non-zero, restrict the set of allowed expansions. See
+  * interpret_branch_name() for details.
+  */
+ extern void strbuf_branchname(struct strbuf *sb, const char *name,
+                             unsigned allowed);
+ /*
+  * Like strbuf_branchname() above, but confirm that the result is
+  * syntactically valid to be used as a local branch name in refs/heads/.
+  *
+  * The return value is "0" if the result is valid, and "-1" otherwise.
+  */
  extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
  
  extern void strbuf_addstr_urlencode(struct strbuf *, const char *,