Merge branch 'jk/interpret-branch-name' into maint
authorJunio C Hamano <gitster@pobox.com>
Tue, 28 Mar 2017 20:52:22 +0000 (13:52 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 28 Mar 2017 20:52:22 +0000 (13:52 -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 9d30f55b0b83cfc09294e2cb7cabdc9aa72e2508,0c924612ebd1ef4f84f82e6028bf04547f5f4b9d..babfd0f73063060eea595fccabefe55f9f631d26
@@@ -190,17 -190,20 +190,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);
  
@@@ -512,6 -515,15 +515,6 @@@ static void print_ref_list(struct ref_f
        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();
        ref_array_sort(sorting, &array);
  
        for (i = 0; i < array.nr; i++)
@@@ -636,7 -648,6 +639,7 @@@ 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;
  
        struct option 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_END(),
        };
  
  
        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;
 +              /*
 +               * 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);
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 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 d76d0896bb5758386752dbd6d7e2f1e53559b837,7aea88534d06de7fe973ac66b9e941b8d6fceea9..b3cf851b59ff80be9f36d27c350099b15f74844d
+++ 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);
@@@ -1102,13 -1096,9 +1102,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);
@@@ -1167,8 -1157,7 +1167,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);
@@@ -1182,19 -1171,6 +1182,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
@@@ -1316,7 -1292,37 +1316,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);
@@@ -1746,8 -1752,6 +1776,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 *);
@@@ -1816,11 -1820,8 +1846,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 b9188908b26b5824c46098e25b763c9aa92246c0,da847f6901ba50ba51f793b5b0462c0d268f4046..2d717742d603d1bd7f6c1644c72ed2e2fd5ecb2f
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -404,7 -404,7 +404,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;
@@@ -638,17 -638,12 +638,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)
@@@ -1034,10 -1029,10 +1034,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;
  
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 *,