Merge branch 'cc/starts-n-ends-with'
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 Dec 2013 19:47:35 +0000 (11:47 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Dec 2013 20:02:44 +0000 (12:02 -0800)
Remove a few duplicate implementations of prefix/suffix comparison
functions, and rename them to starts_with and ends_with.

* cc/starts-n-ends-with:
replace {pre,suf}fixcmp() with {starts,ends}_with()
strbuf: introduce starts_with() and ends_with()
builtin/remote: remove postfixcmp() and use suffixcmp() instead
environment: normalize use of prefixcmp() by removing " != 0"

25 files changed:
1  2 
builtin/branch.c
builtin/checkout.c
builtin/commit.c
builtin/fast-export.c
builtin/fetch.c
builtin/for-each-ref.c
builtin/name-rev.c
builtin/remote.c
builtin/rev-parse.c
builtin/show-branch.c
commit.c
config.c
diff.c
fetch-pack.c
http.c
log-tree.c
notes-utils.c
pathspec.c
remote-curl.c
remote.c
revision.c
send-pack.c
sha1_name.c
upload-pack.c
wt-status.c
diff --combined builtin/branch.c
index 636a16ea4e3af71aef80ba42639f0933c286f485,c5953e56154a989dfa218bdc3acc66ec371ffaef..b4d771673e7e9dccacbae8b29f4f1cd6a835151f
@@@ -81,13 -81,13 +81,13 @@@ static int parse_branch_color_slot(cons
  
  static int git_branch_config(const char *var, const char *value, void *cb)
  {
-       if (!prefixcmp(var, "column."))
+       if (starts_with(var, "column."))
                return git_column_config(var, value, "branch", &colopts);
        if (!strcmp(var, "color.branch")) {
                branch_use_color = git_config_colorbool(var, value);
                return 0;
        }
-       if (!prefixcmp(var, "color.branch.")) {
+       if (starts_with(var, "color.branch.")) {
                int slot = parse_branch_color_slot(var, 13);
                if (slot < 0)
                        return 0;
@@@ -502,7 -502,7 +502,7 @@@ static void add_verbose_info(struct str
        const char *sub = _(" **** invalid ref ****");
        struct commit *commit = item->commit;
  
 -      if (commit && !parse_commit(commit)) {
 +      if (!parse_commit(commit)) {
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
                sub = subject.buf;
        }
@@@ -868,7 -868,7 +868,7 @@@ int cmd_branch(int argc, const char **a
        if (!strcmp(head, "HEAD")) {
                detached = 1;
        } else {
-               if (prefixcmp(head, "refs/heads/"))
+               if (!starts_with(head, "refs/heads/"))
                        die(_("HEAD not found below refs/heads!"));
                head += 11;
        }
diff --combined builtin/checkout.c
index 904fd715f0f27af0ac3694209ba3ffe61f174fa8,7e939b05e348006d5eb81c2d3e8cbbc4be3804cb..5df3837e3102e2f7d432ec11bb4685535bfaa0ac
@@@ -380,8 -380,8 +380,8 @@@ static void show_local_changes(struct o
  static void describe_detached_head(const char *msg, struct commit *commit)
  {
        struct strbuf sb = STRBUF_INIT;
 -      parse_commit(commit);
 -      pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
 +      if (!parse_commit(commit))
 +              pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
        fprintf(stderr, "%s %s... %s\n", msg,
                find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
        strbuf_release(&sb);
@@@ -677,12 -677,12 +677,12 @@@ static int add_pending_uninteresting_re
  
  static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
  {
 -      parse_commit(commit);
        strbuf_addstr(sb, "  ");
        strbuf_addstr(sb,
                find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
        strbuf_addch(sb, ' ');
 -      pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
 +      if (!parse_commit(commit))
 +              pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
        strbuf_addch(sb, '\n');
  }
  
@@@ -781,7 -781,7 +781,7 @@@ static int switch_branches(const struc
        if (!(flag & REF_ISSYMREF))
                old.path = NULL;
  
-       if (old.path && !prefixcmp(old.path, "refs/heads/"))
+       if (old.path && starts_with(old.path, "refs/heads/"))
                old.name = old.path + strlen("refs/heads/");
  
        if (!new->name) {
                new->commit = old.commit;
                if (!new->commit)
                        die(_("You are on a branch yet to be born"));
 -              parse_commit(new->commit);
 +              parse_commit_or_die(new->commit);
        }
  
        ret = merge_working_tree(opts, &old, new, &writeout_error);
@@@ -816,7 -816,7 +816,7 @@@ static int git_checkout_config(const ch
                return 0;
        }
  
-       if (!prefixcmp(var, "submodule."))
+       if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
  
        return git_xmerge_config(var, value, NULL);
@@@ -995,7 -995,7 +995,7 @@@ static int parse_branchname_arg(int arg
                /* not a commit */
                *source_tree = parse_tree_indirect(rev);
        } else {
 -              parse_commit(new->commit);
 +              parse_commit_or_die(new->commit);
                *source_tree = new->commit->tree;
        }
  
@@@ -1151,9 -1151,9 +1151,9 @@@ int cmd_checkout(int argc, const char *
                const char *argv0 = argv[0];
                if (!argc || !strcmp(argv0, "--"))
                        die (_("--track needs a branch name"));
-               if (!prefixcmp(argv0, "refs/"))
+               if (starts_with(argv0, "refs/"))
                        argv0 += 5;
-               if (!prefixcmp(argv0, "remotes/"))
+               if (starts_with(argv0, "remotes/"))
                        argv0 += 8;
                argv0 = strchr(argv0, '/');
                if (!argv0 || !argv0[1])
diff --combined builtin/commit.c
index f4ff75d6842f86bfd86d9448ee0a6c941410c574,4f765a2b3e022a0906611633577ea58abfdb86db..3767478c6ddb02cf8a4a418f8911102aeaacfbe8
@@@ -733,7 -733,7 +733,7 @@@ static int prepare_to_commit(const cha
                                eol = nl - sb.buf;
                        else
                                eol = sb.len;
-                       if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) {
+                       if (starts_with(sb.buf + previous, "\nConflicts:\n")) {
                                ignore_footer = sb.len - previous;
                                break;
                        }
@@@ -904,7 -904,7 +904,7 @@@ static int rest_is_empty(struct strbuf 
                        eol = sb->len;
  
                if (strlen(sign_off_header) <= eol - i &&
-                   !prefixcmp(sb->buf + i, sign_off_header)) {
+                   starts_with(sb->buf + i, sign_off_header)) {
                        i = eol;
                        continue;
                }
@@@ -1183,7 -1183,7 +1183,7 @@@ static int git_status_config(const cha
  {
        struct wt_status *s = cb;
  
-       if (!prefixcmp(k, "column."))
+       if (starts_with(k, "column."))
                return git_column_config(k, v, "status", &s->colopts);
        if (!strcmp(k, "status.submodulesummary")) {
                int is_bool;
                s->display_comment_prefix = git_config_bool(k, v);
                return 0;
        }
-       if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
+       if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) {
                int slot = parse_status_slot(k, 13);
                if (slot < 0)
                        return 0;
@@@ -1338,7 -1338,7 +1338,7 @@@ static void print_summary(const char *p
        commit = lookup_commit(sha1);
        if (!commit)
                die(_("couldn't look up newly created commit"));
 -      if (!commit || parse_commit(commit))
 +      if (parse_commit(commit))
                die(_("could not parse newly created commit"));
  
        strbuf_addstr(&format, "format:%h] %s");
  
        head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
        printf("[%s%s ",
-               !prefixcmp(head, "refs/heads/") ?
+               starts_with(head, "refs/heads/") ?
                        head + 11 :
                        !strcmp(head, "HEAD") ?
                                _("detached HEAD") :
@@@ -1505,7 -1505,7 +1505,7 @@@ int cmd_commit(int argc, const char **a
        struct strbuf sb = STRBUF_INIT;
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
 -      char *nl, *p;
 +      char *nl;
        unsigned char sha1[20];
        struct ref_lock *ref_lock;
        struct commit_list *parents = NULL, **pptr = &parents;
                current_head = NULL;
        else {
                current_head = lookup_commit_or_die(sha1, "HEAD");
 -              if (!current_head || parse_commit(current_head))
 +              if (parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
        argc = parse_and_validate_options(argc, argv, builtin_commit_options,
        }
  
        /* Truncate the message just before the diff, if any. */
 -      if (verbose) {
 -              p = strstr(sb.buf, "\ndiff --git ");
 -              if (p != NULL)
 -                      strbuf_setlen(&sb, p - sb.buf + 1);
 -      }
 +      if (verbose)
 +              wt_status_truncate_message_at_cut_line(&sb);
  
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, cleanup_mode == CLEANUP_ALL);
diff --combined builtin/fast-export.c
index ea6305258d4d63e58f2453736cfc5aa38ec37282,ea55a245965bc8ad0e8e6d2241c7d8535d3b49a7..b8d8a3aaf9ee8294be9a78956a70b476d327ee05
@@@ -287,7 -287,7 +287,7 @@@ static void handle_commit(struct commi
  
        rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
  
 -      parse_commit(commit);
 +      parse_commit_or_die(commit);
        author = strstr(commit->buffer, "\nauthor ");
        if (!author)
                die ("Could not find author in commit %s",
        if (commit->parents &&
            get_object_mark(&commit->parents->item->object) != 0 &&
            !full_tree) {
 -              parse_commit(commit->parents->item);
 +              parse_commit_or_die(commit->parents->item);
                diff_tree_sha1(commit->parents->item->tree->object.sha1,
                               commit->tree->object.sha1, "", &rev->diffopt);
        }
@@@ -476,7 -476,7 +476,7 @@@ static void handle_tag(const char *name
                }
        }
  
-       if (!prefixcmp(name, "refs/tags/"))
+       if (starts_with(name, "refs/tags/"))
                name += 10;
        printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
               name, tagged_mark,
diff --combined builtin/fetch.c
index 3d978eb58ec5cc1e2629db10562319c666bc031e,4dd82504c764e96b3af5210c994b3f5277b3d8a5..e3ac84a0dd14612afdb1eca9f265767a701bc810
@@@ -160,156 -160,48 +160,156 @@@ static void add_merge_config(struct re
        }
  }
  
 +static int add_existing(const char *refname, const unsigned char *sha1,
 +                      int flag, void *cbdata)
 +{
 +      struct string_list *list = (struct string_list *)cbdata;
 +      struct string_list_item *item = string_list_insert(list, refname);
 +      item->util = xmalloc(20);
 +      hashcpy(item->util, sha1);
 +      return 0;
 +}
 +
 +static int will_fetch(struct ref **head, const unsigned char *sha1)
 +{
 +      struct ref *rm = *head;
 +      while (rm) {
 +              if (!hashcmp(rm->old_sha1, sha1))
 +                      return 1;
 +              rm = rm->next;
 +      }
 +      return 0;
 +}
 +
  static void find_non_local_tags(struct transport *transport,
                        struct ref **head,
 -                      struct ref ***tail);
 +                      struct ref ***tail)
 +{
 +      struct string_list existing_refs = STRING_LIST_INIT_DUP;
 +      struct string_list remote_refs = STRING_LIST_INIT_NODUP;
 +      const struct ref *ref;
 +      struct string_list_item *item = NULL;
 +
 +      for_each_ref(add_existing, &existing_refs);
 +      for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
-               if (prefixcmp(ref->name, "refs/tags/"))
++              if (!starts_with(ref->name, "refs/tags/"))
 +                      continue;
 +
 +              /*
 +               * The peeled ref always follows the matching base
 +               * ref, so if we see a peeled ref that we don't want
 +               * to fetch then we can mark the ref entry in the list
 +               * as one to ignore by setting util to NULL.
 +               */
-               if (!suffixcmp(ref->name, "^{}")) {
++              if (ends_with(ref->name, "^{}")) {
 +                      if (item && !has_sha1_file(ref->old_sha1) &&
 +                          !will_fetch(head, ref->old_sha1) &&
 +                          !has_sha1_file(item->util) &&
 +                          !will_fetch(head, item->util))
 +                              item->util = NULL;
 +                      item = NULL;
 +                      continue;
 +              }
 +
 +              /*
 +               * If item is non-NULL here, then we previously saw a
 +               * ref not followed by a peeled reference, so we need
 +               * to check if it is a lightweight tag that we want to
 +               * fetch.
 +               */
 +              if (item && !has_sha1_file(item->util) &&
 +                  !will_fetch(head, item->util))
 +                      item->util = NULL;
 +
 +              item = NULL;
 +
 +              /* skip duplicates and refs that we already have */
 +              if (string_list_has_string(&remote_refs, ref->name) ||
 +                  string_list_has_string(&existing_refs, ref->name))
 +                      continue;
 +
 +              item = string_list_insert(&remote_refs, ref->name);
 +              item->util = (void *)ref->old_sha1;
 +      }
 +      string_list_clear(&existing_refs, 1);
 +
 +      /*
 +       * We may have a final lightweight tag that needs to be
 +       * checked to see if it needs fetching.
 +       */
 +      if (item && !has_sha1_file(item->util) &&
 +          !will_fetch(head, item->util))
 +              item->util = NULL;
 +
 +      /*
 +       * For all the tags in the remote_refs string list,
 +       * add them to the list of refs to be fetched
 +       */
 +      for_each_string_list_item(item, &remote_refs) {
 +              /* Unless we have already decided to ignore this item... */
 +              if (item->util)
 +              {
 +                      struct ref *rm = alloc_ref(item->string);
 +                      rm->peer_ref = alloc_ref(item->string);
 +                      hashcpy(rm->old_sha1, item->util);
 +                      **tail = rm;
 +                      *tail = &rm->next;
 +              }
 +      }
 +
 +      string_list_clear(&remote_refs, 0);
 +}
  
  static struct ref *get_ref_map(struct transport *transport,
 -                             struct refspec *refs, int ref_count, int tags,
 -                             int *autotags)
 +                             struct refspec *refspecs, int refspec_count,
 +                             int tags, int *autotags)
  {
        int i;
        struct ref *rm;
        struct ref *ref_map = NULL;
        struct ref **tail = &ref_map;
  
 -      const struct ref *remote_refs = transport_get_remote_refs(transport);
 +      /* opportunistically-updated references: */
 +      struct ref *orefs = NULL, **oref_tail = &orefs;
  
 -      if (ref_count || tags == TAGS_SET) {
 -              struct ref **old_tail;
 +      const struct ref *remote_refs = transport_get_remote_refs(transport);
  
 -              for (i = 0; i < ref_count; i++) {
 -                      get_fetch_map(remote_refs, &refs[i], &tail, 0);
 -                      if (refs[i].dst && refs[i].dst[0])
 +      if (refspec_count) {
 +              for (i = 0; i < refspec_count; i++) {
 +                      get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
 +                      if (refspecs[i].dst && refspecs[i].dst[0])
                                *autotags = 1;
                }
 -              /* Merge everything on the command line, but not --tags */
 +              /* Merge everything on the command line (but not --tags) */
                for (rm = ref_map; rm; rm = rm->next)
                        rm->fetch_head_status = FETCH_HEAD_MERGE;
 -              if (tags == TAGS_SET)
 -                      get_fetch_map(remote_refs, tag_refspec, &tail, 0);
  
                /*
 -               * For any refs that we happen to be fetching via command-line
 -               * arguments, take the opportunity to update their configured
 -               * counterparts. However, we do not want to mention these
 -               * entries in FETCH_HEAD at all, as they would simply be
 -               * duplicates of existing entries.
 +               * For any refs that we happen to be fetching via
 +               * command-line arguments, the destination ref might
 +               * have been missing or have been different than the
 +               * remote-tracking ref that would be derived from the
 +               * configured refspec.  In these cases, we want to
 +               * take the opportunity to update their configured
 +               * remote-tracking reference.  However, we do not want
 +               * to mention these entries in FETCH_HEAD at all, as
 +               * they would simply be duplicates of existing
 +               * entries, so we set them FETCH_HEAD_IGNORE below.
 +               *
 +               * We compute these entries now, based only on the
 +               * refspecs specified on the command line.  But we add
 +               * them to the list following the refspecs resulting
 +               * from the tags option so that one of the latter,
 +               * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed
 +               * by ref_remove_duplicates() in favor of one of these
 +               * opportunistic entries with FETCH_HEAD_IGNORE.
                 */
 -              old_tail = tail;
                for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
                        get_fetch_map(ref_map, &transport->remote->fetch[i],
 -                                    &tail, 1);
 -              for (rm = *old_tail; rm; rm = rm->next)
 -                      rm->fetch_head_status = FETCH_HEAD_IGNORE;
 +                                    &oref_tail, 1);
 +
 +              if (tags == TAGS_SET)
 +                      get_fetch_map(remote_refs, tag_refspec, &tail, 0);
        } else {
                /* Use the defaults */
                struct remote *remote = transport->remote;
                        tail = &ref_map->next;
                }
        }
 -      if (tags == TAGS_DEFAULT && *autotags)
 +
 +      if (tags == TAGS_SET)
 +              /* also fetch all tags */
 +              get_fetch_map(remote_refs, tag_refspec, &tail, 0);
 +      else if (tags == TAGS_DEFAULT && *autotags)
                find_non_local_tags(transport, &ref_map, &tail);
 -      ref_remove_duplicates(ref_map);
  
 -      return ref_map;
 +      /* Now append any refs to be updated opportunistically: */
 +      *tail = orefs;
 +      for (rm = orefs; rm; rm = rm->next) {
 +              rm->fetch_head_status = FETCH_HEAD_IGNORE;
 +              tail = &rm->next;
 +      }
 +
 +      return ref_remove_duplicates(ref_map);
  }
  
  #define STORE_REF_ERROR_OTHER 1
@@@ -431,7 -313,7 +431,7 @@@ static int update_local_ref(struct ref 
        }
  
        if (!is_null_sha1(ref->old_sha1) &&
-           !prefixcmp(ref->name, "refs/tags/")) {
+           starts_with(ref->name, "refs/tags/")) {
                int r;
                r = s_update_ref("updating tag", ref, 0);
                strbuf_addf(display, "%c %-*s %-*s -> %s%s",
                 * more likely to follow a standard layout.
                 */
                const char *name = remote_ref ? remote_ref->name : "";
-               if (!prefixcmp(name, "refs/tags/")) {
+               if (starts_with(name, "refs/tags/")) {
                        msg = "storing tag";
                        what = _("[new tag]");
-               } else if (!prefixcmp(name, "refs/heads/")) {
+               } else if (starts_with(name, "refs/heads/")) {
                        msg = "storing head";
                        what = _("[new branch]");
                } else {
@@@ -589,15 -471,15 +589,15 @@@ static int store_updated_refs(const cha
                                kind = "";
                                what = "";
                        }
-                       else if (!prefixcmp(rm->name, "refs/heads/")) {
+                       else if (starts_with(rm->name, "refs/heads/")) {
                                kind = "branch";
                                what = rm->name + 11;
                        }
-                       else if (!prefixcmp(rm->name, "refs/tags/")) {
+                       else if (starts_with(rm->name, "refs/tags/")) {
                                kind = "tag";
                                what = rm->name + 10;
                        }
-                       else if (!prefixcmp(rm->name, "refs/remotes/")) {
+                       else if (starts_with(rm->name, "refs/remotes/")) {
                                kind = "remote-tracking branch";
                                what = rm->name + 13;
                        }
@@@ -730,6 -612,106 +730,6 @@@ static int prune_refs(struct refspec *r
        return result;
  }
  
 -static int add_existing(const char *refname, const unsigned char *sha1,
 -                      int flag, void *cbdata)
 -{
 -      struct string_list *list = (struct string_list *)cbdata;
 -      struct string_list_item *item = string_list_insert(list, refname);
 -      item->util = xmalloc(20);
 -      hashcpy(item->util, sha1);
 -      return 0;
 -}
 -
 -static int will_fetch(struct ref **head, const unsigned char *sha1)
 -{
 -      struct ref *rm = *head;
 -      while (rm) {
 -              if (!hashcmp(rm->old_sha1, sha1))
 -                      return 1;
 -              rm = rm->next;
 -      }
 -      return 0;
 -}
 -
 -static void find_non_local_tags(struct transport *transport,
 -                      struct ref **head,
 -                      struct ref ***tail)
 -{
 -      struct string_list existing_refs = STRING_LIST_INIT_DUP;
 -      struct string_list remote_refs = STRING_LIST_INIT_NODUP;
 -      const struct ref *ref;
 -      struct string_list_item *item = NULL;
 -
 -      for_each_ref(add_existing, &existing_refs);
 -      for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
 -              if (!starts_with(ref->name, "refs/tags/"))
 -                      continue;
 -
 -              /*
 -               * The peeled ref always follows the matching base
 -               * ref, so if we see a peeled ref that we don't want
 -               * to fetch then we can mark the ref entry in the list
 -               * as one to ignore by setting util to NULL.
 -               */
 -              if (ends_with(ref->name, "^{}")) {
 -                      if (item && !has_sha1_file(ref->old_sha1) &&
 -                          !will_fetch(head, ref->old_sha1) &&
 -                          !has_sha1_file(item->util) &&
 -                          !will_fetch(head, item->util))
 -                              item->util = NULL;
 -                      item = NULL;
 -                      continue;
 -              }
 -
 -              /*
 -               * If item is non-NULL here, then we previously saw a
 -               * ref not followed by a peeled reference, so we need
 -               * to check if it is a lightweight tag that we want to
 -               * fetch.
 -               */
 -              if (item && !has_sha1_file(item->util) &&
 -                  !will_fetch(head, item->util))
 -                      item->util = NULL;
 -
 -              item = NULL;
 -
 -              /* skip duplicates and refs that we already have */
 -              if (string_list_has_string(&remote_refs, ref->name) ||
 -                  string_list_has_string(&existing_refs, ref->name))
 -                      continue;
 -
 -              item = string_list_insert(&remote_refs, ref->name);
 -              item->util = (void *)ref->old_sha1;
 -      }
 -      string_list_clear(&existing_refs, 1);
 -
 -      /*
 -       * We may have a final lightweight tag that needs to be
 -       * checked to see if it needs fetching.
 -       */
 -      if (item && !has_sha1_file(item->util) &&
 -          !will_fetch(head, item->util))
 -              item->util = NULL;
 -
 -      /*
 -       * For all the tags in the remote_refs string list,
 -       * add them to the list of refs to be fetched
 -       */
 -      for_each_string_list_item(item, &remote_refs) {
 -              /* Unless we have already decided to ignore this item... */
 -              if (item->util)
 -              {
 -                      struct ref *rm = alloc_ref(item->string);
 -                      rm->peer_ref = alloc_ref(item->string);
 -                      hashcpy(rm->old_sha1, item->util);
 -                      **tail = rm;
 -                      *tail = &rm->next;
 -              }
 -      }
 -
 -      string_list_clear(&remote_refs, 0);
 -}
 -
  static void check_not_current_branch(struct ref *ref_map)
  {
        struct branch *current_branch = branch_get(NULL);
@@@ -849,16 -831,30 +849,16 @@@ static int do_fetch(struct transport *t
        }
        if (prune) {
                /*
 -               * If --tags was specified, pretend that the user gave us
 -               * the canonical tags refspec
 +               * We only prune based on refspecs specified
 +               * explicitly (via command line or configuration); we
 +               * don't care whether --tags was specified.
                 */
 -              if (tags == TAGS_SET) {
 -                      const char *tags_str = "refs/tags/*:refs/tags/*";
 -                      struct refspec *tags_refspec, *refspec;
 -
 -                      /* Copy the refspec and add the tags to it */
 -                      refspec = xcalloc(ref_count + 1, sizeof(struct refspec));
 -                      tags_refspec = parse_fetch_refspec(1, &tags_str);
 -                      memcpy(refspec, refs, ref_count * sizeof(struct refspec));
 -                      memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));
 -                      ref_count++;
 -
 -                      prune_refs(refspec, ref_count, ref_map);
 -
 -                      ref_count--;
 -                      /* The rest of the strings belong to fetch_one */
 -                      free_refspec(1, tags_refspec);
 -                      free(refspec);
 -              } else if (ref_count) {
 +              if (ref_count) {
                        prune_refs(refs, ref_count, ref_map);
                } else {
 -                      prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
 +                      prune_refs(transport->remote->fetch,
 +                                 transport->remote->fetch_refspec_nr,
 +                                 ref_map);
                }
        }
        free_refs(ref_map);
@@@ -896,7 -892,7 +896,7 @@@ static int get_remote_group(const char 
  {
        struct remote_group_data *g = priv;
  
-       if (!prefixcmp(key, "remotes.") &&
+       if (starts_with(key, "remotes.") &&
                        !strcmp(key + 8, g->name)) {
                /* split list by white space */
                int space = strcspn(value, " \t\n");
@@@ -934,8 -930,8 +934,8 @@@ static void add_options_to_argv(struct 
  {
        if (dry_run)
                argv_array_push(argv, "--dry-run");
 -      if (prune > 0)
 -              argv_array_push(argv, "--prune");
 +      if (prune != -1)
 +              argv_array_push(argv, prune ? "--prune" : "--no-prune");
        if (update_head_ok)
                argv_array_push(argv, "--update-head-ok");
        if (force)
diff --combined builtin/for-each-ref.c
index 875bd813d5ed6d09622fc3473fdb574e9714bf7f,883d383201b7a67b36875ffe4b74b190a6492ae2..6551e7b39b6039032852d6ec0d0dd8da0d7031dc
@@@ -9,7 -9,6 +9,7 @@@
  #include "quote.h"
  #include "parse-options.h"
  #include "remote.h"
 +#include "color.h"
  
  /* Quoting styles */
  #define QUOTE_NONE 0
@@@ -76,8 -75,6 +76,8 @@@ static struct 
        { "upstream" },
        { "symref" },
        { "flag" },
 +      { "HEAD" },
 +      { "color" },
  };
  
  /*
@@@ -93,7 -90,6 +93,7 @@@
  static const char **used_atom;
  static cmp_type *used_atom_type;
  static int used_atom_cnt, sort_atom_limit, need_tagged, need_symref;
 +static int need_color_reset_at_eol;
  
  /*
   * Used to parse format string and sort specifiers
@@@ -180,21 -176,13 +180,21 @@@ static const char *find_next(const cha
  static int verify_format(const char *format)
  {
        const char *cp, *sp;
 +      static const char color_reset[] = "color:reset";
 +
 +      need_color_reset_at_eol = 0;
        for (cp = format; *cp && (sp = find_next(cp)); ) {
                const char *ep = strchr(sp, ')');
 +              int at;
 +
                if (!ep)
                        return error("malformed format string %s", sp);
                /* sp points at "%(" and ep points at the closing ")" */
 -              parse_atom(sp + 2, ep);
 +              at = parse_atom(sp + 2, ep);
                cp = ep + 1;
 +
 +              if (!memcmp(used_atom[at], "color:", 6))
 +                      need_color_reset_at_eol = !!strcmp(used_atom[at], color_reset);
        }
        return 0;
  }
@@@ -453,7 -441,7 +453,7 @@@ static void grab_person(const char *who
                if (name[wholen] != 0 &&
                    strcmp(name + wholen, "name") &&
                    strcmp(name + wholen, "email") &&
-                   prefixcmp(name + wholen, "date"))
+                   !starts_with(name + wholen, "date"))
                        continue;
                if (!wholine)
                        wholine = find_wholine(who, wholen, buf, sz);
                        v->s = copy_name(wholine);
                else if (!strcmp(name + wholen, "email"))
                        v->s = copy_email(wholine);
-               else if (!prefixcmp(name + wholen, "date"))
+               else if (starts_with(name + wholen, "date"))
                        grab_date(wholine, v, name);
        }
  
                if (deref)
                        name++;
  
-               if (!prefixcmp(name, "creatordate"))
+               if (starts_with(name, "creatordate"))
                        grab_date(wholine, v, name);
                else if (!strcmp(name, "creator"))
                        v->s = copy_line(wholine);
@@@ -661,20 -649,20 +661,20 @@@ static void populate_value(struct refin
                int deref = 0;
                const char *refname;
                const char *formatp;
 +              struct branch *branch = NULL;
  
                if (*name == '*') {
                        deref = 1;
                        name++;
                }
  
-               if (!prefixcmp(name, "refname"))
+               if (starts_with(name, "refname"))
                        refname = ref->refname;
-               else if (!prefixcmp(name, "symref"))
+               else if (starts_with(name, "symref"))
                        refname = ref->symref ? ref->symref : "";
-               else if (!prefixcmp(name, "upstream")) {
+               else if (starts_with(name, "upstream")) {
 -                      struct branch *branch;
                        /* only local branches may have an upstream */
-                       if (prefixcmp(ref->refname, "refs/heads/"))
+                       if (!starts_with(ref->refname, "refs/heads/"))
                                continue;
                        branch = branch_get(ref->refname + 11);
  
                            !branch->merge[0]->dst)
                                continue;
                        refname = branch->merge[0]->dst;
-               } else if (!prefixcmp(name, "color:")) {
 -              }
 -              else if (!strcmp(name, "flag")) {
++              } else if (starts_with(name, "color:")) {
 +                      char color[COLOR_MAXLEN] = "";
 +
 +                      color_parse(name + 6, "--format", color);
 +                      v->s = xstrdup(color);
 +                      continue;
 +              } else if (!strcmp(name, "flag")) {
                        char buf[256], *cp = buf;
                        if (ref->flag & REF_ISSYMREF)
                                cp = copy_advance(cp, ",symref");
                                v->s = xstrdup(buf + 1);
                        }
                        continue;
 -              }
 -              else if (!deref && grab_objectname(name, ref->objectname, v))
 +              } else if (!deref && grab_objectname(name, ref->objectname, v)) {
                        continue;
 -              else
 +              } else if (!strcmp(name, "HEAD")) {
 +                      const char *head;
 +                      unsigned char sha1[20];
 +
 +                      head = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
 +                      if (!strcmp(ref->refname, head))
 +                              v->s = "*";
 +                      else
 +                              v->s = " ";
 +                      continue;
 +              } else
                        continue;
  
                formatp = strchr(name, ':');
 -              /* look for "short" refname format */
                if (formatp) {
 +                      int num_ours, num_theirs;
 +
                        formatp++;
                        if (!strcmp(formatp, "short"))
                                refname = shorten_unambiguous_ref(refname,
                                                      warn_ambiguous_refs);
 -                      else
 +                      else if (!strcmp(formatp, "track") &&
-                               !prefixcmp(name, "upstream")) {
++                               starts_with(name, "upstream")) {
 +                              char buf[40];
 +
 +                              stat_tracking_info(branch, &num_ours, &num_theirs);
 +                              if (!num_ours && !num_theirs)
 +                                      v->s = "";
 +                              else if (!num_ours) {
 +                                      sprintf(buf, "[behind %d]", num_theirs);
 +                                      v->s = xstrdup(buf);
 +                              } else if (!num_theirs) {
 +                                      sprintf(buf, "[ahead %d]", num_ours);
 +                                      v->s = xstrdup(buf);
 +                              } else {
 +                                      sprintf(buf, "[ahead %d, behind %d]",
 +                                              num_ours, num_theirs);
 +                                      v->s = xstrdup(buf);
 +                              }
 +                              continue;
 +                      } else if (!strcmp(formatp, "trackshort") &&
-                               !prefixcmp(name, "upstream")) {
++                                 starts_with(name, "upstream")) {
 +                              assert(branch);
 +                              stat_tracking_info(branch, &num_ours, &num_theirs);
 +                              if (!num_ours && !num_theirs)
 +                                      v->s = "=";
 +                              else if (!num_ours)
 +                                      v->s = "<";
 +                              else if (!num_theirs)
 +                                      v->s = ">";
 +                              else
 +                                      v->s = "<>";
 +                              continue;
 +                      } else
                                die("unknown %.*s format %s",
                                    (int)(formatp - name), name, formatp);
                }
@@@ -934,9 -875,11 +934,9 @@@ static void sort_refs(struct ref_sort *
        qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
  }
  
 -static void print_value(struct refinfo *ref, int atom, int quote_style)
 +static void print_value(struct atom_value *v, int quote_style)
  {
 -      struct atom_value *v;
        struct strbuf sb = STRBUF_INIT;
 -      get_value(ref, atom, &v);
        switch (quote_style) {
        case QUOTE_NONE:
                fputs(v->s, stdout);
@@@ -1003,26 -946,15 +1003,26 @@@ static void show_ref(struct refinfo *in
        const char *cp, *sp, *ep;
  
        for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
 +              struct atom_value *atomv;
 +
                ep = strchr(sp, ')');
                if (cp < sp)
                        emit(cp, sp);
 -              print_value(info, parse_atom(sp + 2, ep), quote_style);
 +              get_value(info, parse_atom(sp + 2, ep), &atomv);
 +              print_value(atomv, quote_style);
        }
        if (*cp) {
                sp = cp + strlen(cp);
                emit(cp, sp);
        }
 +      if (need_color_reset_at_eol) {
 +              struct atom_value resetv;
 +              char color[COLOR_MAXLEN] = "";
 +
 +              color_parse("reset", "--format", color);
 +              resetv.s = color;
 +              print_value(&resetv, quote_style);
 +      }
        putchar('\n');
  }
  
diff --combined builtin/name-rev.c
index 23daaa7d99b40e1bbd38a2743bc0d9ed0c38eda9,990390156c50f798b2153ff4e7baff92aa90b9f8..0b21d7e5b297ff3d2d1a73d8a74c8bc37e672ccb
@@@ -27,7 -27,8 +27,7 @@@ static void name_rev(struct commit *com
        struct commit_list *parents;
        int parent_number = 1;
  
 -      if (!commit->object.parsed)
 -              parse_commit(commit);
 +      parse_commit(commit);
  
        if (commit->date < cutoff)
                return;
@@@ -100,9 -101,9 +100,9 @@@ static const char *name_ref_abbrev(cons
  {
        if (shorten_unambiguous)
                refname = shorten_unambiguous_ref(refname, 0);
-       else if (!prefixcmp(refname, "refs/heads/"))
+       else if (starts_with(refname, "refs/heads/"))
                refname = refname + 11;
-       else if (!prefixcmp(refname, "refs/"))
+       else if (starts_with(refname, "refs/"))
                refname = refname + 5;
        return refname;
  }
@@@ -148,7 -149,7 +148,7 @@@ static int name_ref(const char *path, c
        int can_abbreviate_output = data->tags_only && data->name_only;
        int deref = 0;
  
-       if (data->tags_only && prefixcmp(path, "refs/tags/"))
+       if (data->tags_only && !starts_with(path, "refs/tags/"))
                return 0;
  
        if (data->ref_filter) {
diff --combined builtin/remote.c
index 119e9151ad7fb2a9bcc52d5673a7d51c8d24aacf,b3fd92f6d04a43574e074932c35f41c26f67d6cd..b3ab4cf8f6517c715845db7e17c67cad5f50d730
@@@ -6,7 -6,6 +6,7 @@@
  #include "strbuf.h"
  #include "run-command.h"
  #include "refs.h"
 +#include "argv-array.h"
  
  static const char * const builtin_remote_usage[] = {
        N_("git remote [-v | --verbose]"),
@@@ -78,14 -77,9 +78,6 @@@ static const char * const builtin_remot
  
  static int verbose;
  
- static inline int postfixcmp(const char *string, const char *postfix)
- {
-       int len1 = strlen(string), len2 = strlen(postfix);
-       if (len1 < len2)
-               return 1;
-       return strcmp(string + len1 - len2, postfix);
- }
 -static int show_all(void);
 -static int prune_remote(const char *remote, int dry_run);
--
  static int fetch_remote(const char *name)
  {
        const char *argv[] = { "fetch", name, NULL, NULL };
@@@ -267,7 -261,7 +259,7 @@@ static const char *abbrev_ref(const cha
  
  static int config_read_branches(const char *key, const char *value, void *cb)
  {
-       if (!prefixcmp(key, "branch.")) {
+       if (starts_with(key, "branch.")) {
                const char *orig_key = key;
                char *name;
                struct string_list_item *item;
                enum { REMOTE, MERGE, REBASE } type;
  
                key += 7;
-               if (!postfixcmp(key, ".remote")) {
+               if (ends_with(key, ".remote")) {
                        name = xstrndup(key, strlen(key) - 7);
                        type = REMOTE;
-               } else if (!postfixcmp(key, ".merge")) {
+               } else if (ends_with(key, ".merge")) {
                        name = xstrndup(key, strlen(key) - 6);
                        type = MERGE;
-               } else if (!postfixcmp(key, ".rebase")) {
+               } else if (ends_with(key, ".rebase")) {
                        name = xstrndup(key, strlen(key) - 7);
                        type = REBASE;
                } else
                                space = strchr(value, ' ');
                        }
                        string_list_append(&info->merge, xstrdup(value));
 -              } else
 -                      info->rebase = git_config_bool(orig_key, value);
 +              } else {
 +                      int v = git_config_maybe_bool(orig_key, value);
 +                      if (v >= 0)
 +                              info->rebase = v;
 +                      else if (!strcmp(value, "preserve"))
 +                              info->rebase = 1;
 +              }
        }
        return 0;
  }
@@@ -537,9 -526,9 +529,9 @@@ static int add_branch_for_removal(cons
        }
  
        /* don't delete non-remote-tracking refs */
-       if (prefixcmp(refname, "refs/remotes/")) {
+       if (!starts_with(refname, "refs/remotes/")) {
                /* advise user how to delete local branches */
-               if (!prefixcmp(refname, "refs/heads/"))
+               if (starts_with(refname, "refs/heads/"))
                        string_list_append(branches->skipped,
                                           abbrev_branch(refname));
                /* silently skip over other non-remote refs */
@@@ -574,7 -563,7 +566,7 @@@ static int read_remote_branches(const c
        const char *symref;
  
        strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
-       if (!prefixcmp(refname, buf.buf)) {
+       if (starts_with(refname, buf.buf)) {
                item = string_list_append(rename->remote_branches, xstrdup(refname));
                symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
                if (flag & REF_ISSYMREF)
@@@ -1087,64 -1076,6 +1079,64 @@@ static int show_push_info_item(struct s
        return 0;
  }
  
 +static int get_one_entry(struct remote *remote, void *priv)
 +{
 +      struct string_list *list = priv;
 +      struct strbuf url_buf = STRBUF_INIT;
 +      const char **url;
 +      int i, url_nr;
 +
 +      if (remote->url_nr > 0) {
 +              strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
 +              string_list_append(list, remote->name)->util =
 +                              strbuf_detach(&url_buf, NULL);
 +      } else
 +              string_list_append(list, remote->name)->util = NULL;
 +      if (remote->pushurl_nr) {
 +              url = remote->pushurl;
 +              url_nr = remote->pushurl_nr;
 +      } else {
 +              url = remote->url;
 +              url_nr = remote->url_nr;
 +      }
 +      for (i = 0; i < url_nr; i++)
 +      {
 +              strbuf_addf(&url_buf, "%s (push)", url[i]);
 +              string_list_append(list, remote->name)->util =
 +                              strbuf_detach(&url_buf, NULL);
 +      }
 +
 +      return 0;
 +}
 +
 +static int show_all(void)
 +{
 +      struct string_list list = STRING_LIST_INIT_NODUP;
 +      int result;
 +
 +      list.strdup_strings = 1;
 +      result = for_each_remote(get_one_entry, &list);
 +
 +      if (!result) {
 +              int i;
 +
 +              sort_string_list(&list);
 +              for (i = 0; i < list.nr; i++) {
 +                      struct string_list_item *item = list.items + i;
 +                      if (verbose)
 +                              printf("%s\t%s\n", item->string,
 +                                      item->util ? (const char *)item->util : "");
 +                      else {
 +                              if (i && !strcmp((item - 1)->string, item->string))
 +                                      continue;
 +                              printf("%s\n", item->string);
 +                      }
 +              }
 +      }
 +      string_list_clear(&list, 1);
 +      return result;
 +}
 +
  static int show(int argc, const char **argv)
  {
        int no_query = 0, result = 0, query_flag = 0;
@@@ -1307,6 -1238,26 +1299,6 @@@ static int set_head(int argc, const cha
        return result;
  }
  
 -static int prune(int argc, const char **argv)
 -{
 -      int dry_run = 0, result = 0;
 -      struct option options[] = {
 -              OPT__DRY_RUN(&dry_run, N_("dry run")),
 -              OPT_END()
 -      };
 -
 -      argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,
 -                           0);
 -
 -      if (argc < 1)
 -              usage_with_options(builtin_remote_prune_usage, options);
 -
 -      for (; argc; argc--, argv++)
 -              result |= prune_remote(*argv, dry_run);
 -
 -      return result;
 -}
 -
  static int prune_remote(const char *remote, int dry_run)
  {
        int result = 0, i;
        return result;
  }
  
 +static int prune(int argc, const char **argv)
 +{
 +      int dry_run = 0, result = 0;
 +      struct option options[] = {
 +              OPT__DRY_RUN(&dry_run, N_("dry run")),
 +              OPT_END()
 +      };
 +
 +      argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,
 +                           0);
 +
 +      if (argc < 1)
 +              usage_with_options(builtin_remote_prune_usage, options);
 +
 +      for (; argc; argc--, argv++)
 +              result |= prune_remote(*argv, dry_run);
 +
 +      return result;
 +}
 +
  static int get_remote_default(const char *key, const char *value, void *priv)
  {
        if (strcmp(key, "remotes.default") == 0) {
  
  static int update(int argc, const char **argv)
  {
 -      int i, prune = 0;
 +      int i, prune = -1;
        struct option options[] = {
                OPT_BOOL('p', "prune", &prune,
                         N_("prune remotes after fetching")),
                OPT_END()
        };
 -      const char **fetch_argv;
 -      int fetch_argc = 0;
 +      struct argv_array fetch_argv = ARGV_ARRAY_INIT;
        int default_defined = 0;
 -
 -      fetch_argv = xmalloc(sizeof(char *) * (argc+5));
 +      int retval;
  
        argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
                             PARSE_OPT_KEEP_ARGV0);
  
 -      fetch_argv[fetch_argc++] = "fetch";
 +      argv_array_push(&fetch_argv, "fetch");
  
 -      if (prune)
 -              fetch_argv[fetch_argc++] = "--prune";
 +      if (prune != -1)
 +              argv_array_push(&fetch_argv, prune ? "--prune" : "--no-prune");
        if (verbose)
 -              fetch_argv[fetch_argc++] = "-v";
 -      fetch_argv[fetch_argc++] = "--multiple";
 +              argv_array_push(&fetch_argv, "-v");
 +      argv_array_push(&fetch_argv, "--multiple");
        if (argc < 2)
 -              fetch_argv[fetch_argc++] = "default";
 +              argv_array_push(&fetch_argv, "default");
        for (i = 1; i < argc; i++)
 -              fetch_argv[fetch_argc++] = argv[i];
 +              argv_array_push(&fetch_argv, argv[i]);
  
 -      if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) {
 +      if (strcmp(fetch_argv.argv[fetch_argv.argc-1], "default") == 0) {
                git_config(get_remote_default, &default_defined);
 -              if (!default_defined)
 -                      fetch_argv[fetch_argc-1] = "--all";
 +              if (!default_defined) {
 +                      argv_array_pop(&fetch_argv);
 +                      argv_array_push(&fetch_argv, "--all");
 +              }
        }
  
 -      fetch_argv[fetch_argc] = NULL;
 -
 -      return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
 +      retval = run_command_v_opt(fetch_argv.argv, RUN_GIT_CMD);
 +      argv_array_clear(&fetch_argv);
 +      return retval;
  }
  
  static int remove_all_fetch_refspecs(const char *remote, const char *key)
@@@ -1566,6 -1497,64 +1558,6 @@@ static int set_url(int argc, const cha
        return 0;
  }
  
 -static int get_one_entry(struct remote *remote, void *priv)
 -{
 -      struct string_list *list = priv;
 -      struct strbuf url_buf = STRBUF_INIT;
 -      const char **url;
 -      int i, url_nr;
 -
 -      if (remote->url_nr > 0) {
 -              strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
 -              string_list_append(list, remote->name)->util =
 -                              strbuf_detach(&url_buf, NULL);
 -      } else
 -              string_list_append(list, remote->name)->util = NULL;
 -      if (remote->pushurl_nr) {
 -              url = remote->pushurl;
 -              url_nr = remote->pushurl_nr;
 -      } else {
 -              url = remote->url;
 -              url_nr = remote->url_nr;
 -      }
 -      for (i = 0; i < url_nr; i++)
 -      {
 -              strbuf_addf(&url_buf, "%s (push)", url[i]);
 -              string_list_append(list, remote->name)->util =
 -                              strbuf_detach(&url_buf, NULL);
 -      }
 -
 -      return 0;
 -}
 -
 -static int show_all(void)
 -{
 -      struct string_list list = STRING_LIST_INIT_NODUP;
 -      int result;
 -
 -      list.strdup_strings = 1;
 -      result = for_each_remote(get_one_entry, &list);
 -
 -      if (!result) {
 -              int i;
 -
 -              sort_string_list(&list);
 -              for (i = 0; i < list.nr; i++) {
 -                      struct string_list_item *item = list.items + i;
 -                      if (verbose)
 -                              printf("%s\t%s\n", item->string,
 -                                      item->util ? (const char *)item->util : "");
 -                      else {
 -                              if (i && !strcmp((item - 1)->string, item->string))
 -                                      continue;
 -                              printf("%s\n", item->string);
 -                      }
 -              }
 -      }
 -      string_list_clear(&list, 1);
 -      return result;
 -}
 -
  int cmd_remote(int argc, const char **argv, const char *prefix)
  {
        struct option options[] = {
diff --combined builtin/rev-parse.c
index 1d9ecafd417cf763d0c33438b74cec28c463ed74,6b894027320648cc2efd9ea9b9d7e32963892bbd..6e802fdeb8bce0f7fdce925f47fc239462f6a459
@@@ -9,8 -9,6 +9,8 @@@
  #include "quote.h"
  #include "builtin.h"
  #include "parse-options.h"
 +#include "diff.h"
 +#include "revision.h"
  
  #define DO_REVS               1
  #define DO_NOREV      2
@@@ -32,9 -30,6 +32,9 @@@ static int abbrev_ref
  static int abbrev_ref_strict;
  static int output_sq;
  
 +static int stuck_long;
 +static struct string_list *ref_excludes;
 +
  /*
   * Some arguments are relevant "revision" arguments,
   * others are about output format or other details.
@@@ -190,8 -185,6 +190,8 @@@ static int show_default(void
  
  static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
  {
 +      if (ref_excluded(ref_excludes, refname))
 +              return 0;
        show_rev(NORMAL, sha1, refname);
        return 0;
  }
@@@ -327,15 -320,12 +327,15 @@@ static int parseopt_dump(const struct o
        struct strbuf *parsed = o->value;
        if (unset)
                strbuf_addf(parsed, " --no-%s", o->long_name);
 -      else if (o->short_name)
 +      else if (o->short_name && (o->long_name == NULL || !stuck_long))
                strbuf_addf(parsed, " -%c", o->short_name);
        else
                strbuf_addf(parsed, " --%s", o->long_name);
        if (arg) {
 -              strbuf_addch(parsed, ' ');
 +              if (!stuck_long)
 +                      strbuf_addch(parsed, ' ');
 +              else if (o->long_name)
 +                      strbuf_addch(parsed, '=');
                sq_quote_buf(parsed, arg);
        }
        return 0;
@@@ -361,8 -351,6 +361,8 @@@ static int cmd_parseopt(int argc, cons
                OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option,
                                        N_("stop parsing after the "
                                           "first non-option argument")),
 +              OPT_BOOL(0, "stuck-long", &stuck_long,
 +                                      N_("output in stuck long form")),
                OPT_END(),
        };
  
@@@ -520,7 -508,7 +520,7 @@@ int cmd_rev_parse(int argc, const char 
                        }
                        continue;
                }
-               if (!prefixcmp(arg, "-n")) {
+               if (starts_with(arg, "-n")) {
                        if ((filter & DO_FLAGS) && (filter & DO_REVS))
                                show(arg);
                        continue;
                                continue;
                        }
                        if (!strcmp(arg, "--short") ||
-                           !prefixcmp(arg, "--short=")) {
+                           starts_with(arg, "--short=")) {
                                filter &= ~(DO_FLAGS|DO_NOREV);
                                verify = 1;
                                abbrev = DEFAULT_ABBREV;
                                symbolic = SHOW_SYMBOLIC_FULL;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--abbrev-ref") &&
+                       if (starts_with(arg, "--abbrev-ref") &&
                            (!arg[12] || arg[12] == '=')) {
                                abbrev_ref = 1;
                                abbrev_ref_strict = warn_ambiguous_refs;
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--disambiguate=")) {
+                       if (starts_with(arg, "--disambiguate=")) {
                                for_each_abbrev(arg + 15, show_abbrev, NULL);
                                continue;
                        }
                                for_each_ref_in("refs/bisect/good", anti_reference, NULL);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--branches=")) {
+                       if (starts_with(arg, "--branches=")) {
                                for_each_glob_ref_in(show_reference, arg + 11,
                                        "refs/heads/", NULL);
 +                              clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
                        if (!strcmp(arg, "--branches")) {
                                for_each_branch_ref(show_reference, NULL);
 +                              clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--tags=")) {
+                       if (starts_with(arg, "--tags=")) {
                                for_each_glob_ref_in(show_reference, arg + 7,
                                        "refs/tags/", NULL);
 +                              clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
                        if (!strcmp(arg, "--tags")) {
                                for_each_tag_ref(show_reference, NULL);
 +                              clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--glob=")) {
+                       if (starts_with(arg, "--glob=")) {
                                for_each_glob_ref(show_reference, arg + 7, NULL);
 +                              clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--remotes=")) {
+                       if (starts_with(arg, "--remotes=")) {
                                for_each_glob_ref_in(show_reference, arg + 10,
                                        "refs/remotes/", NULL);
 +                              clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
                        if (!strcmp(arg, "--remotes")) {
                                for_each_remote_ref(show_reference, NULL);
-                       if (!prefixcmp(arg, "--exclude=")) {
 +                              clear_ref_exclusion(&ref_excludes);
 +                              continue;
 +                      }
++                      if (starts_with(arg, "--exclude=")) {
 +                              add_ref_exclusion(&ref_excludes, arg + 10);
                                continue;
                        }
                        if (!strcmp(arg, "--local-env-vars")) {
                                                : "false");
                                continue;
                        }
-                       if (!prefixcmp(arg, "--since=")) {
+                       if (starts_with(arg, "--since=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--after=")) {
+                       if (starts_with(arg, "--after=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--before=")) {
+                       if (starts_with(arg, "--before=")) {
                                show_datestring("--min-age=", arg+9);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--until=")) {
+                       if (starts_with(arg, "--until=")) {
                                show_datestring("--min-age=", arg+8);
                                continue;
                        }
diff --combined builtin/show-branch.c
index 46902c3de40b05b19ff1b5b0127b9f1cd11f6cde,f20c274c6fc7ef9cf6427a371f53f4667973d017..d9217ce1e1a957462284224c08786db77f5a8f15
@@@ -227,7 -227,8 +227,7 @@@ static void join_revs(struct commit_lis
                        parents = parents->next;
                        if ((this_flag & flags) == flags)
                                continue;
 -                      if (!p->object.parsed)
 -                              parse_commit(p);
 +                      parse_commit(p);
                        if (mark_seen(p, seen_p) && !still_interesting)
                                extra--;
                        p->object.flags |= flags;
@@@ -284,7 -285,7 +284,7 @@@ static void show_one_commit(struct comm
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
                pretty_str = pretty.buf;
        }
-       if (!prefixcmp(pretty_str, "[PATCH] "))
+       if (starts_with(pretty_str, "[PATCH] "))
                pretty_str += 8;
  
        if (!no_name) {
@@@ -395,7 -396,7 +395,7 @@@ static int append_head_ref(const char *
  {
        unsigned char tmp[20];
        int ofs = 11;
-       if (prefixcmp(refname, "refs/heads/"))
+       if (!starts_with(refname, "refs/heads/"))
                return 0;
        /* If both heads/foo and tags/foo exists, get_sha1 would
         * get confused.
@@@ -409,7 -410,7 +409,7 @@@ static int append_remote_ref(const cha
  {
        unsigned char tmp[20];
        int ofs = 13;
-       if (prefixcmp(refname, "refs/remotes/"))
+       if (!starts_with(refname, "refs/remotes/"))
                return 0;
        /* If both heads/foo and tags/foo exists, get_sha1 would
         * get confused.
  
  static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
  {
-       if (prefixcmp(refname, "refs/tags/"))
+       if (!starts_with(refname, "refs/tags/"))
                return 0;
        return append_ref(refname + 5, sha1, 0);
  }
@@@ -452,9 -453,9 +452,9 @@@ static int append_matching_ref(const ch
                return 0;
        if (fnmatch(match_ref_pattern, tail, 0))
                return 0;
-       if (!prefixcmp(refname, "refs/heads/"))
+       if (starts_with(refname, "refs/heads/"))
                return append_head_ref(refname, sha1, flag, cb_data);
-       if (!prefixcmp(refname, "refs/tags/"))
+       if (starts_with(refname, "refs/tags/"))
                return append_tag_ref(refname, sha1, flag, cb_data);
        return append_ref(refname, sha1, 0);
  }
@@@ -479,11 -480,11 +479,11 @@@ static int rev_is_head(char *head, int 
        if ((!head[0]) ||
            (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
                return 0;
-       if (!prefixcmp(head, "refs/heads/"))
+       if (starts_with(head, "refs/heads/"))
                head += 11;
-       if (!prefixcmp(name, "refs/heads/"))
+       if (starts_with(name, "refs/heads/"))
                name += 11;
-       else if (!prefixcmp(name, "heads/"))
+       else if (starts_with(name, "heads/"))
                name += 6;
        return !strcmp(head, name);
  }
@@@ -812,7 -813,7 +812,7 @@@ int cmd_show_branch(int ac, const char 
                                has_head++;
                }
                if (!has_head) {
-                       int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0;
+                       int offset = starts_with(head, "refs/heads/") ? 11 : 0;
                        append_one_rev(head + offset);
                }
        }
diff --combined commit.c
index 11509ffc471c548a1367068b3d7c2f15d19361f4,2a9bf59124894a2fc968fba43d0d802a5e8dc4a9..5df1df73512ebb90f558ce6f2f7e899e75f03ff8
+++ b/commit.c
@@@ -79,7 -79,7 +79,7 @@@ struct commit *lookup_commit_reference_
        if (get_sha1_committish(name, sha1))
                return NULL;
        commit = lookup_commit_reference(sha1);
 -      if (!commit || parse_commit(commit))
 +      if (parse_commit(commit))
                return NULL;
        return commit;
  }
@@@ -341,13 -341,6 +341,13 @@@ int parse_commit(struct commit *item
        return ret;
  }
  
 +void parse_commit_or_die(struct commit *item)
 +{
 +      if (parse_commit(item))
 +              die("unable to parse commit %s",
 +                  item ? sha1_to_hex(item->object.sha1) : "(null)");
 +}
 +
  int find_commit_subject(const char *commit_buffer, const char **subject)
  {
        const char *eol;
@@@ -566,7 -559,7 +566,7 @@@ static void record_author_date(struct a
             buf;
             buf = line_end + 1) {
                line_end = strchrnul(buf, '\n');
-               if (prefixcmp(buf, "author ")) {
+               if (!starts_with(buf, "author ")) {
                        if (!line_end[0] || line_end[1] == '\n')
                                return; /* end of header */
                        continue;
@@@ -1113,7 -1106,7 +1113,7 @@@ int parse_signed_commit(const unsigned 
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
                        sig = line + 1;
-               else if (!prefixcmp(line, gpg_sig_header) &&
+               else if (starts_with(line, gpg_sig_header) &&
                         line[gpg_sig_header_len] == ' ')
                        sig = line + gpg_sig_header_len + 1;
                if (sig) {
@@@ -1193,7 -1186,7 +1193,7 @@@ static void parse_gpg_output(struct sig
        for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
                const char *found, *next;
  
-               if (!prefixcmp(buf, sigcheck_gpg_status[i].check + 1)) {
+               if (starts_with(buf, sigcheck_gpg_status[i].check + 1)) {
                        /* At the very beginning of the buffer */
                        found = buf + strlen(sigcheck_gpg_status[i].check + 1);
                } else {
diff --combined config.c
index a2c22ab43c912ad1a509c07bd929f34221a0bf99,7b62f3386811fbf5db608da422ca85adbff3e95b..d969a5aefc2bca92938d3fd7f6a507e884ce2b7f
+++ b/config.c
@@@ -969,25 -969,25 +969,25 @@@ static int git_default_mailmap_config(c
  
  int git_default_config(const char *var, const char *value, void *dummy)
  {
-       if (!prefixcmp(var, "core."))
+       if (starts_with(var, "core."))
                return git_default_core_config(var, value);
  
-       if (!prefixcmp(var, "user."))
+       if (starts_with(var, "user."))
                return git_ident_config(var, value, dummy);
  
-       if (!prefixcmp(var, "i18n."))
+       if (starts_with(var, "i18n."))
                return git_default_i18n_config(var, value);
  
-       if (!prefixcmp(var, "branch."))
+       if (starts_with(var, "branch."))
                return git_default_branch_config(var, value);
  
-       if (!prefixcmp(var, "push."))
+       if (starts_with(var, "push."))
                return git_default_push_config(var, value);
  
-       if (!prefixcmp(var, "mailmap."))
+       if (starts_with(var, "mailmap."))
                return git_default_mailmap_config(var, value);
  
-       if (!prefixcmp(var, "advice."))
+       if (starts_with(var, "advice."))
                return git_default_advice_config(var, value);
  
        if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
@@@ -1210,14 -1210,15 +1210,14 @@@ int git_config(config_fn_t fn, void *da
   * Find all the stuff for git_config_set() below.
   */
  
 -#define MAX_MATCHES 512
 -
  static struct {
        int baselen;
        char *key;
        int do_not_match;
        regex_t *value_regex;
        int multi_replace;
 -      size_t offset[MAX_MATCHES];
 +      size_t *offset;
 +      unsigned int offset_alloc;
        enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
        int seen;
  } store;
@@@ -1240,11 -1241,11 +1240,11 @@@ static int store_aux(const char *key, c
                if (matches(key, value)) {
                        if (store.seen == 1 && store.multi_replace == 0) {
                                warning("%s has multiple values", key);
 -                      } else if (store.seen >= MAX_MATCHES) {
 -                              error("too many matches for %s", key);
 -                              return 1;
                        }
  
 +                      ALLOC_GROW(store.offset, store.seen + 1,
 +                                 store.offset_alloc);
 +
                        store.offset[store.seen] = cf->do_ftell(cf);
                        store.seen++;
                }
                 * Do not increment matches: this is no match, but we
                 * just made sure we are in the desired section.
                 */
 +              ALLOC_GROW(store.offset, store.seen + 1,
 +                         store.offset_alloc);
                store.offset[store.seen] = cf->do_ftell(cf);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
 +                      ALLOC_GROW(store.offset, store.seen + 1,
 +                                 store.offset_alloc);
                        store.offset[store.seen] = cf->do_ftell(cf);
                        store.state = KEY_SEEN;
                        store.seen++;
                        if (strrchr(key, '.') - key == store.baselen &&
                              !strncmp(key, store.key, store.baselen)) {
                                        store.state = SECTION_SEEN;
 +                                      ALLOC_GROW(store.offset,
 +                                                 store.seen + 1,
 +                                                 store.offset_alloc);
                                        store.offset[store.seen] = cf->do_ftell(cf);
                        }
                }
@@@ -1589,7 -1583,6 +1589,7 @@@ int git_config_set_multivar_in_file(con
                        }
                }
  
 +              ALLOC_GROW(store.offset, 1, store.offset_alloc);
                store.offset[0] = 0;
                store.state = START;
                store.seen = 0;
@@@ -1879,7 -1872,7 +1879,7 @@@ int parse_config_key(const char *var
        const char *dot;
  
        /* Does it start with "section." ? */
-       if (prefixcmp(var, section) || var[section_len] != '.')
+       if (!starts_with(var, section) || var[section_len] != '.')
                return -1;
  
        /*
diff --combined diff.c
index 3950e01910674c15d8af2c0d5d114402f43b1a9c,d53029b6275c4299db59d489f17d887e12bfcaae..b79432b1ec97909e20eb76fceb5530b2f7be647f
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -235,7 -235,7 +235,7 @@@ int git_diff_basic_config(const char *v
        if (userdiff_config(var, value) < 0)
                return -1;
  
-       if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
+       if (starts_with(var, "diff.color.") || starts_with(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
                if (slot < 0)
                        return 0;
                return 0;
        }
  
-       if (!prefixcmp(var, "submodule."))
+       if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
  
        return git_default_config(var, value, cb);
@@@ -1215,7 -1215,7 +1215,7 @@@ static void fn_out_consume(void *priv, 
                        diff_words_append(line, len,
                                          &ecbdata->diff_words->plus);
                        return;
-               } else if (!prefixcmp(line, "\\ ")) {
+               } else if (starts_with(line, "\\ ")) {
                        /*
                         * Eat the "no newline at eof" marker as if we
                         * saw a "+" or "-" line with nothing on it,
@@@ -2387,9 -2387,9 +2387,9 @@@ static void builtin_diff(const char *na
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
                if (!diffopts)
                        ;
-               else if (!prefixcmp(diffopts, "--unified="))
+               else if (starts_with(diffopts, "--unified="))
                        xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
-               else if (!prefixcmp(diffopts, "-u"))
+               else if (starts_with(diffopts, "-u"))
                        xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
                if (o->word_diff)
                        init_diff_words_data(&ecbdata, o, one, two);
@@@ -3391,10 -3391,10 +3391,10 @@@ int parse_long_opt(const char *opt, con
        if (arg[0] != '-' || arg[1] != '-')
                return 0;
        arg += strlen("--");
-       if (prefixcmp(arg, opt))
+       if (!starts_with(arg, opt))
                return 0;
        arg += strlen(opt);
 -      if (*arg == '=') { /* sticked form: --option=value */
 +      if (*arg == '=') { /* stuck form: --option=value */
                *optarg = arg + 1;
                return 1;
        }
@@@ -3422,7 -3422,7 +3422,7 @@@ static int stat_opt(struct diff_option
  
        switch (*arg) {
        case '-':
-               if (!prefixcmp(arg, "-width")) {
+               if (starts_with(arg, "-width")) {
                        arg += strlen("-width");
                        if (*arg == '=')
                                width = strtoul(arg + 1, &end, 10);
                                width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (!prefixcmp(arg, "-name-width")) {
+               } else if (starts_with(arg, "-name-width")) {
                        arg += strlen("-name-width");
                        if (*arg == '=')
                                name_width = strtoul(arg + 1, &end, 10);
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (!prefixcmp(arg, "-graph-width")) {
+               } else if (starts_with(arg, "-graph-width")) {
                        arg += strlen("-graph-width");
                        if (*arg == '=')
                                graph_width = strtoul(arg + 1, &end, 10);
                                graph_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (!prefixcmp(arg, "-count")) {
+               } else if (starts_with(arg, "-count")) {
                        arg += strlen("-count");
                        if (*arg == '=')
                                count = strtoul(arg + 1, &end, 10);
@@@ -3614,15 -3614,15 +3614,15 @@@ int diff_opt_parse(struct diff_options 
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
        else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
                return parse_dirstat_opt(options, "");
-       else if (!prefixcmp(arg, "-X"))
+       else if (starts_with(arg, "-X"))
                return parse_dirstat_opt(options, arg + 2);
-       else if (!prefixcmp(arg, "--dirstat="))
+       else if (starts_with(arg, "--dirstat="))
                return parse_dirstat_opt(options, arg + 10);
        else if (!strcmp(arg, "--cumulative"))
                return parse_dirstat_opt(options, "cumulative");
        else if (!strcmp(arg, "--dirstat-by-file"))
                return parse_dirstat_opt(options, "files");
-       else if (!prefixcmp(arg, "--dirstat-by-file=")) {
+       else if (starts_with(arg, "--dirstat-by-file=")) {
                parse_dirstat_opt(options, "files");
                return parse_dirstat_opt(options, arg + 18);
        }
                options->output_format |= DIFF_FORMAT_NAME_STATUS;
        else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch"))
                options->output_format |= DIFF_FORMAT_NO_OUTPUT;
-       else if (!prefixcmp(arg, "--stat"))
+       else if (starts_with(arg, "--stat"))
                /* --stat, --stat-width, --stat-name-width, or --stat-count */
                return stat_opt(options, av);
  
        /* renames options */
-       else if (!prefixcmp(arg, "-B") || !prefixcmp(arg, "--break-rewrites=") ||
+       else if (starts_with(arg, "-B") || starts_with(arg, "--break-rewrites=") ||
                 !strcmp(arg, "--break-rewrites")) {
                if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
                        return error("invalid argument to -B: %s", arg+2);
        }
-       else if (!prefixcmp(arg, "-M") || !prefixcmp(arg, "--find-renames=") ||
+       else if (starts_with(arg, "-M") || starts_with(arg, "--find-renames=") ||
                 !strcmp(arg, "--find-renames")) {
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
                        return error("invalid argument to -M: %s", arg+2);
        else if (!strcmp(arg, "-D") || !strcmp(arg, "--irreversible-delete")) {
                options->irreversible_delete = 1;
        }
-       else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--find-copies=") ||
+       else if (starts_with(arg, "-C") || starts_with(arg, "--find-copies=") ||
                 !strcmp(arg, "--find-copies")) {
                if (options->detect_rename == DIFF_DETECT_COPY)
                        DIFF_OPT_SET(options, FIND_COPIES_HARDER);
                DIFF_OPT_CLR(options, RENAME_EMPTY);
        else if (!strcmp(arg, "--relative"))
                DIFF_OPT_SET(options, RELATIVE_NAME);
-       else if (!prefixcmp(arg, "--relative=")) {
+       else if (starts_with(arg, "--relative=")) {
                DIFF_OPT_SET(options, RELATIVE_NAME);
                options->prefix = arg + 11;
        }
                DIFF_OPT_CLR(options, FOLLOW_RENAMES);
        else if (!strcmp(arg, "--color"))
                options->use_color = 1;
-       else if (!prefixcmp(arg, "--color=")) {
+       else if (starts_with(arg, "--color=")) {
                int value = git_config_colorbool(NULL, arg+8);
                if (value < 0)
                        return error("option `color' expects \"always\", \"auto\", or \"never\"");
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
        }
-       else if (!prefixcmp(arg, "--color-words=")) {
+       else if (starts_with(arg, "--color-words=")) {
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
                options->word_regex = arg + 14;
                if (options->word_diff == DIFF_WORDS_NONE)
                        options->word_diff = DIFF_WORDS_PLAIN;
        }
-       else if (!prefixcmp(arg, "--word-diff=")) {
+       else if (starts_with(arg, "--word-diff=")) {
                const char *type = arg + 12;
                if (!strcmp(type, "plain"))
                        options->word_diff = DIFF_WORDS_PLAIN;
        else if (!strcmp(arg, "--ignore-submodules")) {
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, "all");
-       } else if (!prefixcmp(arg, "--ignore-submodules=")) {
+       } else if (starts_with(arg, "--ignore-submodules=")) {
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, arg + 20);
        } else if (!strcmp(arg, "--submodule"))
                DIFF_OPT_SET(options, SUBMODULE_LOG);
-       else if (!prefixcmp(arg, "--submodule="))
+       else if (starts_with(arg, "--submodule="))
                return parse_submodule_opt(options, arg + 12);
  
        /* misc options */
        }
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
-       else if (!prefixcmp(arg, "--abbrev=")) {
+       else if (starts_with(arg, "--abbrev=")) {
                options->abbrev = strtoul(arg + 9, NULL, 10);
                if (options->abbrev < MINIMUM_ABBREV)
                        options->abbrev = MINIMUM_ABBREV;
@@@ -3907,15 -3907,15 +3907,15 @@@ static int diff_scoreopt_parse(const ch
        cmd = *opt++;
        if (cmd == '-') {
                /* convert the long-form arguments into short-form versions */
-               if (!prefixcmp(opt, "break-rewrites")) {
+               if (starts_with(opt, "break-rewrites")) {
                        opt += strlen("break-rewrites");
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'B';
-               } else if (!prefixcmp(opt, "find-copies")) {
+               } else if (starts_with(opt, "find-copies")) {
                        opt += strlen("find-copies");
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'C';
-               } else if (!prefixcmp(opt, "find-renames")) {
+               } else if (starts_with(opt, "find-renames")) {
                        opt += strlen("find-renames");
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'M';
@@@ -4325,7 -4325,7 +4325,7 @@@ static void patch_id_consume(void *priv
        int new_len;
  
        /* Ignore line numbers when computing the SHA1 of the patch */
-       if (!prefixcmp(line, "@@ -"))
+       if (starts_with(line, "@@ -"))
                return;
  
        new_len = remove_space(line, len);
diff --combined fetch-pack.c
index 5a1200f1a06f0ba3a9366eb9221d982d423d8da0,36be5b2b66bd2bcb28c6dcda5684240727b3e0ef..760ed16e79a7a46eecfb4c9efecaee7feba0f121
@@@ -47,8 -47,9 +47,8 @@@ static void rev_list_push(struct commi
        if (!(commit->object.flags & mark)) {
                commit->object.flags |= mark;
  
 -              if (!(commit->object.parsed))
 -                      if (parse_commit(commit))
 -                              return;
 +              if (parse_commit(commit))
 +                      return;
  
                prio_queue_put(&rev_list, commit);
  
@@@ -127,7 -128,8 +127,7 @@@ static const unsigned char *get_rev(voi
                        return NULL;
  
                commit = prio_queue_get(&rev_list);
 -              if (!commit->object.parsed)
 -                      parse_commit(commit);
 +              parse_commit(commit);
                parents = commit->parents;
  
                commit->object.flags |= POPPED;
@@@ -174,9 -176,9 +174,9 @@@ static void consume_shallow_list(struc
                 */
                char *line;
                while ((line = packet_read_line(fd, NULL))) {
-                       if (!prefixcmp(line, "shallow "))
+                       if (starts_with(line, "shallow "))
                                continue;
-                       if (!prefixcmp(line, "unshallow "))
+                       if (starts_with(line, "unshallow "))
                                continue;
                        die("git fetch-pack: expected shallow list");
                }
@@@ -192,7 -194,7 +192,7 @@@ static enum ack_type get_ack(int fd, un
                die("git fetch-pack: expected ACK/NAK, got EOF");
        if (!strcmp(line, "NAK"))
                return NAK;
-       if (!prefixcmp(line, "ACK ")) {
+       if (starts_with(line, "ACK ")) {
                if (!get_sha1_hex(line+4, result_sha1)) {
                        if (len < 45)
                                return ACK;
@@@ -321,13 -323,13 +321,13 @@@ static int find_common(struct fetch_pac
  
                send_request(args, fd[1], &req_buf);
                while ((line = packet_read_line(fd[0], NULL))) {
-                       if (!prefixcmp(line, "shallow ")) {
+                       if (starts_with(line, "shallow ")) {
                                if (get_sha1_hex(line + 8, sha1))
                                        die("invalid shallow line: %s", line);
                                register_shallow(sha1);
                                continue;
                        }
-                       if (!prefixcmp(line, "unshallow ")) {
+                       if (starts_with(line, "unshallow ")) {
                                if (get_sha1_hex(line + 10, sha1))
                                        die("invalid unshallow line: %s", line);
                                if (!lookup_object(sha1))
@@@ -521,7 -523,7 +521,7 @@@ static void filter_refs(struct fetch_pa
                }
  
                if (!keep && args->fetch_all &&
-                   (!args->depth || prefixcmp(ref->name, "refs/tags/")))
+                   (!args->depth || !starts_with(ref->name, "refs/tags/")))
                        keep = 1;
  
                if (keep) {
diff --combined http.c
index ccb813b86fb47dac7dcb3a1eb86faaa9e8ecc923,e6287b19dea6f1c53c3ba654a14bef25bd45c85e..70eaa26e88cbcfa62e7d5e967b4e519432319210
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -460,7 -460,7 +460,7 @@@ void http_init(struct remote *remote, c
                credential_from_url(&http_auth, url);
                if (!ssl_cert_password_required &&
                    getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
-                   !prefixcmp(url, "https://"))
+                   starts_with(url, "https://"))
                        ssl_cert_password_required = 1;
        }
  
@@@ -761,12 -761,6 +761,12 @@@ void finish_active_slot(struct active_r
        if (slot->results != NULL) {
                slot->results->curl_result = slot->curl_result;
                slot->results->http_code = slot->http_code;
 +#if LIBCURL_VERSION_NUM >= 0x070a08
 +              curl_easy_getinfo(slot->curl, CURLINFO_HTTPAUTH_AVAIL,
 +                                &slot->results->auth_avail);
 +#else
 +              slot->results->auth_avail = 0;
 +#endif
        }
  
        /* Run callback if appropriate */
@@@ -1000,7 -994,7 +1000,7 @@@ static int update_url_from_redirect(str
        if (!strcmp(asked, got->buf))
                return 0;
  
-       if (prefixcmp(asked, base->buf))
+       if (!starts_with(asked, base->buf))
                die("BUG: update_url_from_redirect: %s is not a superset of %s",
                    asked, base->buf);
  
@@@ -1106,7 -1100,7 +1106,7 @@@ int http_fetch_ref(const char *base, st
                strbuf_rtrim(&buffer);
                if (buffer.len == 40)
                        ret = get_sha1_hex(buffer.buf, ref->old_sha1);
-               else if (!prefixcmp(buffer.buf, "ref: ")) {
+               else if (starts_with(buffer.buf, "ref: ")) {
                        ref->symref = xstrdup(buffer.buf + 5);
                        ret = 0;
                }
@@@ -1207,8 -1201,8 +1207,8 @@@ int http_get_info_packs(const char *bas
                case 'P':
                        i++;
                        if (i + 52 <= buf.len &&
-                           !prefixcmp(data + i, " pack-") &&
-                           !prefixcmp(data + i + 46, ".pack\n")) {
+                           starts_with(data + i, " pack-") &&
+                           starts_with(data + i + 46, ".pack\n")) {
                                get_sha1_hex(data + i + 6, sha1);
                                fetch_and_setup_pack_index(packs_head, sha1,
                                                      base_url);
diff --combined log-tree.c
index e958d0748f11d82995f63d06b7e6ed05b2e7e4f1,40ef3ea6745af886b9143649e17798464f6f86bd..642faffdb7d149f162c8716eb13dd878d16baa4b
@@@ -98,7 -98,7 +98,7 @@@ static int add_ref_decoration(const cha
        struct object *obj;
        enum decoration_type type = DECORATION_NONE;
  
-       if (!prefixcmp(refname, "refs/replace/")) {
+       if (starts_with(refname, "refs/replace/")) {
                unsigned char original_sha1[20];
                if (!read_replace_refs)
                        return 0;
        if (!obj)
                return 0;
  
-       if (!prefixcmp(refname, "refs/heads/"))
+       if (starts_with(refname, "refs/heads/"))
                type = DECORATION_REF_LOCAL;
-       else if (!prefixcmp(refname, "refs/remotes/"))
+       else if (starts_with(refname, "refs/remotes/"))
                type = DECORATION_REF_REMOTE;
-       else if (!prefixcmp(refname, "refs/tags/"))
+       else if (starts_with(refname, "refs/tags/"))
                type = DECORATION_REF_TAG;
        else if (!strcmp(refname, "refs/stash"))
                type = DECORATION_REF_STASH;
@@@ -734,7 -734,7 +734,7 @@@ static int log_tree_diff(struct rev_inf
        if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS))
                return 0;
  
 -      parse_commit(commit);
 +      parse_commit_or_die(commit);
        sha1 = commit->tree->object.sha1;
  
        /* Root commit? */
                         * parent, showing summary diff of the others
                         * we merged _in_.
                         */
 -                      parse_commit(parents->item);
 +                      parse_commit_or_die(parents->item);
                        diff_tree_sha1(parents->item->tree->object.sha1,
                                       sha1, "", &opt->diffopt);
                        log_tree_diff_flush(opt);
        for (;;) {
                struct commit *parent = parents->item;
  
 -              parse_commit(parent);
 +              parse_commit_or_die(parent);
                diff_tree_sha1(parent->tree->object.sha1,
                               sha1, "", &opt->diffopt);
                log_tree_diff_flush(opt);
diff --combined notes-utils.c
index 7bb3473dbe44c1f8325ee6891620eec67163dfcd,db4390081c8d49af959b35c45b4981f70c98f3db..2975dcd581024cd483953b47636466b79c95bafe
@@@ -18,7 -18,7 +18,7 @@@ void create_notes_commit(struct notes_t
                unsigned char parent_sha1[20];
                if (!read_ref(t->ref, parent_sha1)) {
                        struct commit *parent = lookup_commit(parent_sha1);
 -                      if (!parent || parse_commit(parent))
 +                      if (parse_commit(parent))
                                die("Failed to find/parse commit %s", t->ref);
                        commit_list_insert(parent, &parents);
                }
@@@ -70,7 -70,7 +70,7 @@@ static combine_notes_fn parse_combine_n
  static int notes_rewrite_config(const char *k, const char *v, void *cb)
  {
        struct notes_rewrite_cfg *c = cb;
-       if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
+       if (starts_with(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
                c->enabled = git_config_bool(k, v);
                return 0;
        } else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
@@@ -85,7 -85,7 +85,7 @@@
        } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
                /* note that a refs/ prefix is implied in the
                 * underlying for_each_glob_ref */
-               if (!prefixcmp(v, "refs/notes/"))
+               if (starts_with(v, "refs/notes/"))
                        string_list_add_refs_by_glob(c->refs, v);
                else
                        warning(_("Refusing to rewrite notes in %s"
diff --combined pathspec.c
index 87b3b82f1fd9baee7758159e5e3119b6da0655e3,4a7893e0724bde995a64457a239381783edd945a..52d38a4b4fb6cbb4928bf3084b773651f14d7e72
@@@ -154,7 -154,7 +154,7 @@@ static unsigned prefix_pathspec(struct 
                                        magic |= pathspec_magic[i].bit;
                                        break;
                                }
-                               if (!prefixcmp(copyfrom, "prefix:")) {
+                               if (starts_with(copyfrom, "prefix:")) {
                                        char *endptr;
                                        pathspec_prefix = strtol(copyfrom + 7,
                                                                 &endptr, 10);
        magic |= short_magic;
        *p_short_magic = short_magic;
  
 -      /* --noglob-pathspec adds :(literal) _unless_ :(glob) is specifed */
 +      /* --noglob-pathspec adds :(literal) _unless_ :(glob) is specified */
        if (noglob_global && !(magic & PATHSPEC_GLOB))
                global_magic |= PATHSPEC_LITERAL;
  
 -      /* --glob-pathspec is overriden by :(literal) */
 +      /* --glob-pathspec is overridden by :(literal) */
        if ((global_magic & PATHSPEC_GLOB) && (magic & PATHSPEC_LITERAL))
                global_magic &= ~PATHSPEC_GLOB;
  
diff --combined remote-curl.c
index 91b07a41456fe5cdb2157a648353d982b88f7e9a,65c403d1d54b1cdf38ec40bcf8354c17ae01b4b8..e38c4b026f2ad63181c765cd38ceb72ef30c4ee8
@@@ -217,7 -217,7 +217,7 @@@ static struct discovery* discover_refs(
        free_discovery(last);
  
        strbuf_addf(&refs_url, "%sinfo/refs", url.buf);
-       if ((!prefixcmp(url.buf, "http://") || !prefixcmp(url.buf, "https://")) &&
+       if ((starts_with(url.buf, "http://") || starts_with(url.buf, "https://")) &&
             git_env_bool("GIT_SMART_HTTP", 1)) {
                maybe_smart = 1;
                if (!strchr(url.buf, '?'))
@@@ -394,29 -394,25 +394,29 @@@ static size_t rpc_in(char *ptr, size_t 
        return size;
  }
  
 -static int run_slot(struct active_request_slot *slot)
 +static int run_slot(struct active_request_slot *slot,
 +                  struct slot_results *results)
  {
        int err;
 -      struct slot_results results;
 +      struct slot_results results_buf;
  
 -      slot->results = &results;
 +      if (!results)
 +              results = &results_buf;
 +
 +      slot->results = results;
        slot->curl_result = curl_easy_perform(slot->curl);
        finish_active_slot(slot);
  
 -      err = handle_curl_result(&results);
 +      err = handle_curl_result(results);
        if (err != HTTP_OK && err != HTTP_REAUTH) {
                error("RPC failed; result=%d, HTTP code = %ld",
 -                    results.curl_result, results.http_code);
 +                    results->curl_result, results->http_code);
        }
  
        return err;
  }
  
 -static int probe_rpc(struct rpc_state *rpc)
 +static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
  {
        struct active_request_slot *slot;
        struct curl_slist *headers = NULL;
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
        curl_easy_setopt(slot->curl, CURLOPT_FILE, &buf);
  
 -      err = run_slot(slot);
 +      err = run_slot(slot, results);
  
        curl_slist_free_all(headers);
        strbuf_release(&buf);
@@@ -453,7 -449,6 +453,7 @@@ static int post_rpc(struct rpc_state *r
        char *gzip_body = NULL;
        size_t gzip_size = 0;
        int err, large_request = 0;
 +      int needs_100_continue = 0;
  
        /* Try to load the entire request, if we can fit it into the
         * allocated buffer space we can use HTTP/1.0 and avoid the
        }
  
        if (large_request) {
 +              struct slot_results results;
 +
                do {
 -                      err = probe_rpc(rpc);
 +                      err = probe_rpc(rpc, &results);
                        if (err == HTTP_REAUTH)
                                credential_fill(&http_auth);
                } while (err == HTTP_REAUTH);
                if (err != HTTP_OK)
                        return -1;
 +
 +              if (results.auth_avail & CURLAUTH_GSSNEGOTIATE)
 +                      needs_100_continue = 1;
        }
  
        headers = curl_slist_append(headers, rpc->hdr_content_type);
        headers = curl_slist_append(headers, rpc->hdr_accept);
 -      headers = curl_slist_append(headers, "Expect:");
 +      headers = curl_slist_append(headers, needs_100_continue ?
 +              "Expect: 100-continue" : "Expect:");
  
  retry:
        slot = get_active_slot();
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
        curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
  
 -      err = run_slot(slot);
 +      err = run_slot(slot, NULL);
        if (err == HTTP_REAUTH && !large_request) {
                credential_fill(&http_auth);
                goto retry;
@@@ -766,7 -755,7 +766,7 @@@ static void parse_fetch(struct strbuf *
        int alloc_heads = 0, nr_heads = 0;
  
        do {
-               if (!prefixcmp(buf->buf, "fetch ")) {
+               if (starts_with(buf->buf, "fetch ")) {
                        char *p = buf->buf + strlen("fetch ");
                        char *name;
                        struct ref *ref;
@@@ -889,7 -878,7 +889,7 @@@ static void parse_push(struct strbuf *b
        int alloc_spec = 0, nr_spec = 0, i, ret;
  
        do {
-               if (!prefixcmp(buf->buf, "push ")) {
+               if (starts_with(buf->buf, "push ")) {
                        ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
                        specs[nr_spec++] = xstrdup(buf->buf + 5);
                }
@@@ -952,19 -941,19 +952,19 @@@ int main(int argc, const char **argv
                }
                if (buf.len == 0)
                        break;
-               if (!prefixcmp(buf.buf, "fetch ")) {
+               if (starts_with(buf.buf, "fetch ")) {
                        if (nongit)
                                die("Fetch attempted without a local repo");
                        parse_fetch(&buf);
  
-               } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
+               } else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
                        int for_push = !!strstr(buf.buf + 4, "for-push");
                        output_refs(get_refs(for_push));
  
-               } else if (!prefixcmp(buf.buf, "push ")) {
+               } else if (starts_with(buf.buf, "push ")) {
                        parse_push(&buf);
  
-               } else if (!prefixcmp(buf.buf, "option ")) {
+               } else if (starts_with(buf.buf, "option ")) {
                        char *name = buf.buf + strlen("option ");
                        char *value = strchr(name, ' ');
                        int result;
diff --combined remote.c
index 15e6e5efcf1643b084b697a7daf563d901a15c02,01aa4b4b698184ca5bd7ebdada901679166be07f..709a9cbd26fea972a6781581a0c345c8e95a7f6d
+++ b/remote.c
@@@ -76,7 -76,7 +76,7 @@@ static const char *alias_url(const cha
                if (!r->rewrite[i])
                        continue;
                for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
-                       if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) &&
+                       if (starts_with(url, r->rewrite[i]->instead_of[j].s) &&
                            (!longest ||
                             longest->len < r->rewrite[i]->instead_of[j].len)) {
                                longest = &(r->rewrite[i]->instead_of[j]);
@@@ -239,13 -239,13 +239,13 @@@ static void read_remotes_file(struct re
                int value_list;
                char *s, *p;
  
-               if (!prefixcmp(buffer, "URL:")) {
+               if (starts_with(buffer, "URL:")) {
                        value_list = 0;
                        s = buffer + 4;
-               } else if (!prefixcmp(buffer, "Push:")) {
+               } else if (starts_with(buffer, "Push:")) {
                        value_list = 1;
                        s = buffer + 5;
-               } else if (!prefixcmp(buffer, "Pull:")) {
+               } else if (starts_with(buffer, "Pull:")) {
                        value_list = 2;
                        s = buffer + 5;
                } else
@@@ -337,7 -337,7 +337,7 @@@ static int handle_config(const char *ke
        const char *subkey;
        struct remote *remote;
        struct branch *branch;
-       if (!prefixcmp(key, "branch.")) {
+       if (starts_with(key, "branch.")) {
                name = key + 7;
                subkey = strrchr(name, '.');
                if (!subkey)
                }
                return 0;
        }
-       if (!prefixcmp(key, "url.")) {
+       if (starts_with(key, "url.")) {
                struct rewrite *rewrite;
                name = key + 4;
                subkey = strrchr(name, '.');
                }
        }
  
-       if (prefixcmp(key,  "remote."))
+       if (!starts_with(key,  "remote."))
                return 0;
        name = key + 7;
  
@@@ -487,7 -487,7 +487,7 @@@ static void read_config(void
        current_branch = NULL;
        head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
        if (head_ref && (flag & REF_ISSYMREF) &&
-           !prefixcmp(head_ref, "refs/heads/")) {
+           starts_with(head_ref, "refs/heads/")) {
                current_branch =
                        make_branch(head_ref + strlen("refs/heads/"), 0);
        }
@@@ -745,66 -745,35 +745,66 @@@ int for_each_remote(each_remote_fn fn, 
        return result;
  }
  
 -void ref_remove_duplicates(struct ref *ref_map)
 +static void handle_duplicate(struct ref *ref1, struct ref *ref2)
 +{
 +      if (strcmp(ref1->name, ref2->name)) {
 +              if (ref1->fetch_head_status != FETCH_HEAD_IGNORE &&
 +                  ref2->fetch_head_status != FETCH_HEAD_IGNORE) {
 +                      die(_("Cannot fetch both %s and %s to %s"),
 +                          ref1->name, ref2->name, ref2->peer_ref->name);
 +              } else if (ref1->fetch_head_status != FETCH_HEAD_IGNORE &&
 +                         ref2->fetch_head_status == FETCH_HEAD_IGNORE) {
 +                      warning(_("%s usually tracks %s, not %s"),
 +                              ref2->peer_ref->name, ref2->name, ref1->name);
 +              } else if (ref1->fetch_head_status == FETCH_HEAD_IGNORE &&
 +                         ref2->fetch_head_status == FETCH_HEAD_IGNORE) {
 +                      die(_("%s tracks both %s and %s"),
 +                          ref2->peer_ref->name, ref1->name, ref2->name);
 +              } else {
 +                      /*
 +                       * This last possibility doesn't occur because
 +                       * FETCH_HEAD_IGNORE entries always appear at
 +                       * the end of the list.
 +                       */
 +                      die(_("Internal error"));
 +              }
 +      }
 +      free(ref2->peer_ref);
 +      free(ref2);
 +}
 +
 +struct ref *ref_remove_duplicates(struct ref *ref_map)
  {
        struct string_list refs = STRING_LIST_INIT_NODUP;
 -      struct string_list_item *item = NULL;
 -      struct ref *prev = NULL, *next = NULL;
 -      for (; ref_map; prev = ref_map, ref_map = next) {
 -              next = ref_map->next;
 -              if (!ref_map->peer_ref)
 -                      continue;
 +      struct ref *retval = NULL;
 +      struct ref **p = &retval;
  
 -              item = string_list_lookup(&refs, ref_map->peer_ref->name);
 -              if (item) {
 -                      if (strcmp(((struct ref *)item->util)->name,
 -                                 ref_map->name))
 -                              die("%s tracks both %s and %s",
 -                                  ref_map->peer_ref->name,
 -                                  ((struct ref *)item->util)->name,
 -                                  ref_map->name);
 -                      prev->next = ref_map->next;
 -                      free(ref_map->peer_ref);
 -                      free(ref_map);
 -                      ref_map = prev; /* skip this; we freed it */
 -                      continue;
 -              }
 +      while (ref_map) {
 +              struct ref *ref = ref_map;
 +
 +              ref_map = ref_map->next;
 +              ref->next = NULL;
  
 -              item = string_list_insert(&refs, ref_map->peer_ref->name);
 -              item->util = ref_map;
 +              if (!ref->peer_ref) {
 +                      *p = ref;
 +                      p = &ref->next;
 +              } else {
 +                      struct string_list_item *item =
 +                              string_list_insert(&refs, ref->peer_ref->name);
 +
 +                      if (item->util) {
 +                              /* Entry already existed */
 +                              handle_duplicate((struct ref *)item->util, ref);
 +                      } else {
 +                              *p = ref;
 +                              p = &ref->next;
 +                              item->util = ref;
 +                      }
 +              }
        }
 +
        string_list_clear(&refs, 0);
 +      return retval;
  }
  
  int remote_has_url(struct remote *remote, const char *url)
@@@ -856,8 -825,6 +856,8 @@@ static int query_refspecs(struct refspe
  {
        int i;
        int find_src = !query->src;
 +      const char *needle = find_src ? query->dst : query->src;
 +      char **result = find_src ? &query->src : &query->dst;
  
        if (find_src && !query->dst)
                return error("query_refspecs: need either src or dst");
                struct refspec *refspec = &refs[i];
                const char *key = find_src ? refspec->dst : refspec->src;
                const char *value = find_src ? refspec->src : refspec->dst;
 -              const char *needle = find_src ? query->dst : query->src;
 -              char **result = find_src ? &query->src : &query->dst;
  
                if (!refspec->dst)
                        continue;
@@@ -1013,8 -982,8 +1013,8 @@@ static int count_refspec_match(const ch
                 */
                if (namelen != patlen &&
                    patlen != namelen - 5 &&
-                   prefixcmp(name, "refs/heads/") &&
-                   prefixcmp(name, "refs/tags/")) {
+                   !starts_with(name, "refs/heads/") &&
+                   !starts_with(name, "refs/tags/")) {
                        /* We want to catch the case where only weak
                         * matches are found and there are multiple
                         * matches, and where more than one strong
@@@ -1085,9 -1054,9 +1085,9 @@@ static char *guess_ref(const char *name
        if (!r)
                return NULL;
  
-       if (!prefixcmp(r, "refs/heads/"))
+       if (starts_with(r, "refs/heads/"))
                strbuf_addstr(&buf, "refs/heads/");
-       else if (!prefixcmp(r, "refs/tags/"))
+       else if (starts_with(r, "refs/tags/"))
                strbuf_addstr(&buf, "refs/tags/");
        else
                return NULL;
@@@ -1135,7 -1104,7 +1135,7 @@@ static int match_explicit(struct ref *s
                dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
                if (!dst_value ||
                    ((flag & REF_ISSYMREF) &&
-                    prefixcmp(dst_value, "refs/heads/")))
+                    !starts_with(dst_value, "refs/heads/")))
                        die("%s cannot be resolved to branch.",
                            matched_src->name);
        }
@@@ -1224,7 -1193,7 +1224,7 @@@ static char *get_ref_match(const struc
                 * including refs outside refs/heads/ hierarchy, but
                 * that does not make much sense these days.
                 */
-               if (!send_mirror && prefixcmp(ref->name, "refs/heads/"))
+               if (!send_mirror && !starts_with(ref->name, "refs/heads/"))
                        return NULL;
                name = xstrdup(ref->name);
        }
@@@ -1279,7 -1248,7 +1279,7 @@@ static void add_missing_tags(struct re
                        add_to_tips(&sent_tips, ref->peer_ref->new_sha1);
                else
                        add_to_tips(&sent_tips, ref->old_sha1);
-               if (!prefixcmp(ref->name, "refs/tags/"))
+               if (starts_with(ref->name, "refs/tags/"))
                        string_list_append(&dst_tag, ref->name);
        }
        clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK);
  
        /* Collect tags they do not have. */
        for (ref = src; ref; ref = ref->next) {
-               if (prefixcmp(ref->name, "refs/tags/"))
+               if (!starts_with(ref->name, "refs/tags/"))
                        continue; /* not a tag */
                if (string_list_has_string(&dst_tag, ref->name))
                        continue; /* they already have it */
@@@ -1512,7 -1481,7 +1512,7 @@@ void set_ref_status_for_push(struct re
                 */
  
                else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
-                       if (!prefixcmp(ref->name, "refs/tags/"))
+                       if (starts_with(ref->name, "refs/tags/"))
                                reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
                        else if (!has_sha1_file(ref->old_sha1))
                                reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
@@@ -1584,13 -1553,6 +1584,13 @@@ static int ignore_symref_update(const c
        return (flag & REF_ISSYMREF);
  }
  
 +/*
 + * Create and return a list of (struct ref) consisting of copies of
 + * each remote_ref that matches refspec.  refspec must be a pattern.
 + * Fill in the copies' peer_ref to describe the local tracking refs to
 + * which they map.  Omit any references that would map to an existing
 + * local symbolic ref.
 + */
  static struct ref *get_expanded_map(const struct ref *remote_refs,
                                    const struct refspec *refspec)
  {
        struct ref *ret = NULL;
        struct ref **tail = &ret;
  
 -      char *expn_name;
 -
        for (ref = remote_refs; ref; ref = ref->next) {
 +              char *expn_name = NULL;
 +
                if (strchr(ref->name, '^'))
                        continue; /* a dereference item */
                if (match_name_with_pattern(refspec->src, ref->name,
                        struct ref *cpy = copy_ref(ref);
  
                        cpy->peer_ref = alloc_ref(expn_name);
 -                      free(expn_name);
                        if (refspec->force)
                                cpy->peer_ref->force = 1;
                        *tail = cpy;
                        tail = &cpy->next;
                }
 +              free(expn_name);
        }
  
        return ret;
@@@ -1645,12 -1607,12 +1645,12 @@@ static struct ref *get_local_ref(const 
        if (!name || name[0] == '\0')
                return NULL;
  
-       if (!prefixcmp(name, "refs/"))
+       if (starts_with(name, "refs/"))
                return alloc_ref(name);
  
-       if (!prefixcmp(name, "heads/") ||
-           !prefixcmp(name, "tags/") ||
-           !prefixcmp(name, "remotes/"))
+       if (starts_with(name, "heads/") ||
+           starts_with(name, "tags/") ||
+           starts_with(name, "remotes/"))
                return alloc_ref_with_prefix("refs/", 5, name);
  
        return alloc_ref_with_prefix("refs/heads/", 11, name);
@@@ -1685,7 -1647,7 +1685,7 @@@ int get_fetch_map(const struct ref *rem
  
        for (rmp = &ref_map; *rmp; ) {
                if ((*rmp)->peer_ref) {
-                       if (prefixcmp((*rmp)->peer_ref->name, "refs/") ||
+                       if (!starts_with((*rmp)->peer_ref->name, "refs/") ||
                            check_refname_format((*rmp)->peer_ref->name, 0)) {
                                struct ref *ignore = *rmp;
                                error("* Ignoring funny ref '%s' locally",
@@@ -1969,7 -1931,7 +1969,7 @@@ struct ref *guess_remote_head(const str
        /* Look for another ref that points there */
        for (r = refs; r; r = r->next) {
                if (r != head &&
-                   !prefixcmp(r->name, "refs/heads/") &&
+                   starts_with(r->name, "refs/heads/") &&
                    !hashcmp(r->old_sha1, head->old_sha1)) {
                        *tail = copy_ref(r);
                        tail = &((*tail)->next);
diff --combined revision.c
index 05d2d7763a3c9a2bd555cb19ef9af01c5767adb8,10854b07071db090c7422dfb16e60dd87d305c0e..a68fde6e959c3f305fa7dba285783bd4300b8c0f
@@@ -1180,28 -1180,11 +1180,28 @@@ struct all_refs_cb 
        const char *name_for_errormsg;
  };
  
 +int ref_excluded(struct string_list *ref_excludes, const char *path)
 +{
 +      struct string_list_item *item;
 +
 +      if (!ref_excludes)
 +              return 0;
 +      for_each_string_list_item(item, ref_excludes) {
 +              if (!fnmatch(item->string, path, 0))
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
  static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
  {
        struct all_refs_cb *cb = cb_data;
 -      struct object *object = get_reference(cb->all_revs, path, sha1,
 -                                            cb->all_flags);
 +      struct object *object;
 +
 +      if (ref_excluded(cb->all_revs->ref_excludes, path))
 +          return 0;
 +
 +      object = get_reference(cb->all_revs, path, sha1, cb->all_flags);
        add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
        add_pending_sha1(cb->all_revs, path, sha1, cb->all_flags);
        return 0;
@@@ -1214,24 -1197,6 +1214,24 @@@ static void init_all_refs_cb(struct all
        cb->all_flags = flags;
  }
  
 +void clear_ref_exclusion(struct string_list **ref_excludes_p)
 +{
 +      if (*ref_excludes_p) {
 +              string_list_clear(*ref_excludes_p, 0);
 +              free(*ref_excludes_p);
 +      }
 +      *ref_excludes_p = NULL;
 +}
 +
 +void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
 +{
 +      if (!*ref_excludes_p) {
 +              *ref_excludes_p = xcalloc(1, sizeof(**ref_excludes_p));
 +              (*ref_excludes_p)->strdup_strings = 1;
 +      }
 +      string_list_append(*ref_excludes_p, exclude);
 +}
 +
  static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
                int (*for_each)(const char *, each_ref_fn, void *))
  {
@@@ -1627,9 -1592,9 +1627,9 @@@ static int handle_revision_opt(struct r
            !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
            !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
-           !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") ||
-           !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") ||
-           !prefixcmp(arg, "--remotes=") || !prefixcmp(arg, "--no-walk="))
+           !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
+           starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
+           starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
        {
                unkv[(*unkc)++] = arg;
                return 1;
                revs->max_count = atoi(argv[1]);
                revs->no_walk = 0;
                return 2;
-       } else if (!prefixcmp(arg, "-n")) {
+       } else if (starts_with(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
                revs->no_walk = 0;
        } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
        } else if (!strcmp(arg, "--author-date-order")) {
                revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
                revs->topo_order = 1;
-       } else if (!prefixcmp(arg, "--early-output")) {
+       } else if (starts_with(arg, "--early-output")) {
                int count = 100;
                switch (arg[14]) {
                case '=':
                revs->min_parents = 2;
        } else if (!strcmp(arg, "--no-merges")) {
                revs->max_parents = 1;
-       } else if (!prefixcmp(arg, "--min-parents=")) {
+       } else if (starts_with(arg, "--min-parents=")) {
                revs->min_parents = atoi(arg+14);
-       } else if (!prefixcmp(arg, "--no-min-parents")) {
+       } else if (starts_with(arg, "--no-min-parents")) {
                revs->min_parents = 0;
-       } else if (!prefixcmp(arg, "--max-parents=")) {
+       } else if (starts_with(arg, "--max-parents=")) {
                revs->max_parents = atoi(arg+14);
-       } else if (!prefixcmp(arg, "--no-max-parents")) {
+       } else if (starts_with(arg, "--no-max-parents")) {
                revs->max_parents = -1;
        } else if (!strcmp(arg, "--boundary")) {
                revs->boundary = 1;
                revs->verify_objects = 1;
        } else if (!strcmp(arg, "--unpacked")) {
                revs->unpacked = 1;
-       } else if (!prefixcmp(arg, "--unpacked=")) {
+       } else if (starts_with(arg, "--unpacked=")) {
                die("--unpacked=<packfile> no longer supported.");
        } else if (!strcmp(arg, "-r")) {
                revs->diff = 1;
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
-       } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+       } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) {
                /*
                 * Detached form ("--pretty X" as opposed to "--pretty=X")
                 * not allowed, since the argument is optional.
                revs->notes_opt.use_default_notes = 1;
        } else if (!strcmp(arg, "--show-signature")) {
                revs->show_signature = 1;
-       } else if (!prefixcmp(arg, "--show-notes=") ||
-                  !prefixcmp(arg, "--notes=")) {
+       } else if (starts_with(arg, "--show-notes=") ||
+                  starts_with(arg, "--notes=")) {
                struct strbuf buf = STRBUF_INIT;
                revs->show_notes = 1;
                revs->show_notes_given = 1;
-               if (!prefixcmp(arg, "--show-notes")) {
+               if (starts_with(arg, "--show-notes")) {
                        if (revs->notes_opt.use_default_notes < 0)
                                revs->notes_opt.use_default_notes = 1;
                        strbuf_addstr(&buf, arg+13);
                revs->abbrev = 0;
        } else if (!strcmp(arg, "--abbrev")) {
                revs->abbrev = DEFAULT_ABBREV;
-       } else if (!prefixcmp(arg, "--abbrev=")) {
+       } else if (starts_with(arg, "--abbrev=")) {
                revs->abbrev = strtoul(arg + 9, NULL, 10);
                if (revs->abbrev < MINIMUM_ABBREV)
                        revs->abbrev = MINIMUM_ABBREV;
@@@ -2004,51 -1969,40 +2004,51 @@@ static int handle_revision_pseudo_opt(c
        if (!strcmp(arg, "--all")) {
                handle_refs(submodule, revs, *flags, for_each_ref_submodule);
                handle_refs(submodule, revs, *flags, head_ref_submodule);
 +              clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--branches")) {
                handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
 +              clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--bisect")) {
                handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
                handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
                revs->bisect = 1;
        } else if (!strcmp(arg, "--tags")) {
                handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
 +              clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--remotes")) {
                handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);
 +              clear_ref_exclusion(&revs->ref_excludes);
        } else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref(handle_one_ref, optarg, &cb);
 +              clear_ref_exclusion(&revs->ref_excludes);
 +              return argcount;
 +      } else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
 +              add_ref_exclusion(&revs->ref_excludes, optarg);
                return argcount;
-       } else if (!prefixcmp(arg, "--branches=")) {
+       } else if (starts_with(arg, "--branches=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
-       } else if (!prefixcmp(arg, "--tags=")) {
 +              clear_ref_exclusion(&revs->ref_excludes);
+       } else if (starts_with(arg, "--tags=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
-       } else if (!prefixcmp(arg, "--remotes=")) {
 +              clear_ref_exclusion(&revs->ref_excludes);
+       } else if (starts_with(arg, "--remotes=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
 +              clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--reflog")) {
                handle_reflog(revs, *flags);
        } else if (!strcmp(arg, "--not")) {
                *flags ^= UNINTERESTING | BOTTOM;
        } else if (!strcmp(arg, "--no-walk")) {
                revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
-       } else if (!prefixcmp(arg, "--no-walk=")) {
+       } else if (starts_with(arg, "--no-walk=")) {
                /*
                 * Detached form ("--no-walk X" as opposed to "--no-walk=X")
                 * not allowed, since the argument is optional.
diff --combined send-pack.c
index 9ee8cf50a830ed85f134825dcf1d41287a21f046,142f211b412dc79254c1951d9e698decdf7c1508..ac14a4d090cce0fa282447641097085a8428c05c
@@@ -109,7 -109,7 +109,7 @@@ static int receive_status(int in, struc
        struct ref *hint;
        int ret = 0;
        char *line = packet_read_line(in, NULL);
-       if (prefixcmp(line, "unpack "))
+       if (!starts_with(line, "unpack "))
                return error("did not receive remote status");
        if (strcmp(line, "unpack ok")) {
                error("unpack failed: %s", line + 7);
                line = packet_read_line(in, NULL);
                if (!line)
                        break;
-               if (prefixcmp(line, "ok ") && prefixcmp(line, "ng ")) {
+               if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) {
                        error("invalid ref status from remote: %s", line);
                        ret = -1;
                        break;
@@@ -206,8 -206,6 +206,8 @@@ int send_pack(struct send_pack_args *ar
                quiet_supported = 1;
        if (server_supports("agent"))
                agent_supported = 1;
 +      if (server_supports("no-thin"))
 +              args->use_thin_pack = 0;
  
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
diff --combined sha1_name.c
index 2f37488b96c0a8485e90d87befa997d75ba4b7b1,047477af38337290a0fdbbe5ba53d63474b48c71..b1873d8113bdeb0ff8087319c99833036aaffe1b
@@@ -546,7 -546,7 +546,7 @@@ static int get_sha1_basic(const char *s
                if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (!len) {
-                               if (!prefixcmp(real_ref, "refs/heads/")) {
+                               if (starts_with(real_ref, "refs/heads/")) {
                                        str = real_ref + 11;
                                        len = strlen(real_ref + 11);
                                } else {
@@@ -581,6 -581,8 +581,6 @@@ static int get_parent(const char *name
        if (ret)
                return ret;
        commit = lookup_commit_reference(sha1);
 -      if (!commit)
 -              return -1;
        if (parse_commit(commit))
                return -1;
        if (!idx) {
@@@ -674,15 -676,15 +674,15 @@@ static int peel_onion(const char *name
                return -1;
  
        sp++; /* beginning of type name, or closing brace for empty */
-       if (!prefixcmp(sp, "commit}"))
+       if (starts_with(sp, "commit}"))
                expected_type = OBJ_COMMIT;
-       else if (!prefixcmp(sp, "tag}"))
+       else if (starts_with(sp, "tag}"))
                expected_type = OBJ_TAG;
-       else if (!prefixcmp(sp, "tree}"))
+       else if (starts_with(sp, "tree}"))
                expected_type = OBJ_TREE;
-       else if (!prefixcmp(sp, "blob}"))
+       else if (starts_with(sp, "blob}"))
                expected_type = OBJ_BLOB;
-       else if (!prefixcmp(sp, "object}"))
+       else if (starts_with(sp, "object}"))
                expected_type = OBJ_ANY;
        else if (sp[0] == '}')
                expected_type = OBJ_NONE;
@@@ -909,7 -911,7 +909,7 @@@ static int grab_nth_branch_switch(unsig
        const char *match = NULL, *target = NULL;
        size_t len;
  
-       if (!prefixcmp(message, "checkout: moving from ")) {
+       if (starts_with(message, "checkout: moving from ")) {
                match = message + strlen("checkout: moving from ");
                target = strstr(match, " to ");
        }
@@@ -1302,7 -1304,7 +1302,7 @@@ static void diagnose_invalid_index_path
  
  static char *resolve_relative_path(const char *rel)
  {
-       if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
+       if (!starts_with(rel, "./") && !starts_with(rel, "../"))
                return NULL;
  
        if (!startup_info)
diff --combined upload-pack.c
index d30f339e707fe3263b9c186e54b6b3317afc986c,dafd0e94d194593c25d29430f02bc49b4f4d81a6..ec56cdbce9fe0bf87aeafb3567e09cbf546e7218
@@@ -394,7 -394,7 +394,7 @@@ static int get_common_commits(void
                        got_other = 0;
                        continue;
                }
-               if (!prefixcmp(line, "have ")) {
+               if (starts_with(line, "have ")) {
                        switch (got_sha1(line+5, sha1)) {
                        case -1: /* they have what we do not */
                                got_other = 1;
@@@ -540,7 -540,7 +540,7 @@@ static void receive_needs(void
                if (!line)
                        break;
  
-               if (!prefixcmp(line, "shallow ")) {
+               if (starts_with(line, "shallow ")) {
                        unsigned char sha1[20];
                        struct object *object;
                        if (get_sha1_hex(line + 8, sha1))
                        }
                        continue;
                }
-               if (!prefixcmp(line, "deepen ")) {
+               if (starts_with(line, "deepen ")) {
                        char *end;
                        depth = strtol(line + 7, &end, 0);
                        if (end == line + 7 || depth <= 0)
                                die("Invalid deepen: %s", line);
                        continue;
                }
-               if (prefixcmp(line, "want ") ||
+               if (!starts_with(line, "want ") ||
                    get_sha1_hex(line+5, sha1_buf))
                        die("git upload-pack: protocol error, "
                            "expected to get sha, not '%s'", line);
                                /* make sure the real parents are parsed */
                                unregister_shallow(object->sha1);
                                object->parsed = 0;
 -                              if (parse_commit((struct commit *)object))
 -                                      die("invalid commit");
 +                              parse_commit_or_die((struct commit *)object);
                                parents = ((struct commit *)object)->parents;
                                while (parents) {
                                        add_object_array(&parents->item->object,
@@@ -814,7 -815,7 +814,7 @@@ int main(int argc, char **argv
                        strict = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--timeout=")) {
+               if (starts_with(arg, "--timeout=")) {
                        timeout = atoi(arg+10);
                        daemon_mode = 1;
                        continue;
diff --combined wt-status.c
index cd7d706a6344fd28b264db4b5c1ea02a2677512e,f3c008870a87fcd61fe412b6b82a6bd759f8b933..4e5581005936a1e7832e3ab224cfe142ac777a63
  #include "submodule.h"
  #include "column.h"
  #include "strbuf.h"
 +#include "utf8.h"
 +
 +static char cut_line[] =
 +"------------------------ >8 ------------------------\n";
  
  static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@@ -268,30 -264,6 +268,30 @@@ static void wt_status_print_unmerged_da
        strbuf_release(&onebuf);
  }
  
 +static const char *wt_status_diff_status_string(int status)
 +{
 +      switch (status) {
 +      case DIFF_STATUS_ADDED:
 +              return _("new file");
 +      case DIFF_STATUS_COPIED:
 +              return _("copied");
 +      case DIFF_STATUS_DELETED:
 +              return _("deleted");
 +      case DIFF_STATUS_MODIFIED:
 +              return _("modified");
 +      case DIFF_STATUS_RENAMED:
 +              return _("renamed");
 +      case DIFF_STATUS_TYPE_CHANGED:
 +              return _("typechange");
 +      case DIFF_STATUS_UNKNOWN:
 +              return _("unknown");
 +      case DIFF_STATUS_UNMERGED:
 +              return _("unmerged");
 +      default:
 +              return NULL;
 +      }
 +}
 +
  static void wt_status_print_change_data(struct wt_status *s,
                                        int change_type,
                                        struct string_list_item *it)
        const char *one, *two;
        struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
        struct strbuf extra = STRBUF_INIT;
 +      static char *padding;
 +      const char *what;
 +      int len;
 +
 +      if (!padding) {
 +              int width = 0;
 +              /* If DIFF_STATUS_* uses outside this range, we're in trouble */
 +              for (status = 'A'; status <= 'Z'; status++) {
 +                      what = wt_status_diff_status_string(status);
 +                      len = what ? strlen(what) : 0;
 +                      if (len > width)
 +                              width = len;
 +              }
 +              width += 2;     /* colon and a space */
 +              padding = xmallocz(width);
 +              memset(padding, ' ', width);
 +      }
  
        one_name = two_name = it->string;
        switch (change_type) {
        two = quote_path(two_name, s->prefix, &twobuf);
  
        status_printf(s, color(WT_STATUS_HEADER, s), "\t");
 -      switch (status) {
 -      case DIFF_STATUS_ADDED:
 -              status_printf_more(s, c, _("new file:   %s"), one);
 -              break;
 -      case DIFF_STATUS_COPIED:
 -              status_printf_more(s, c, _("copied:     %s -> %s"), one, two);
 -              break;
 -      case DIFF_STATUS_DELETED:
 -              status_printf_more(s, c, _("deleted:    %s"), one);
 -              break;
 -      case DIFF_STATUS_MODIFIED:
 -              status_printf_more(s, c, _("modified:   %s"), one);
 -              break;
 -      case DIFF_STATUS_RENAMED:
 -              status_printf_more(s, c, _("renamed:    %s -> %s"), one, two);
 -              break;
 -      case DIFF_STATUS_TYPE_CHANGED:
 -              status_printf_more(s, c, _("typechange: %s"), one);
 -              break;
 -      case DIFF_STATUS_UNKNOWN:
 -              status_printf_more(s, c, _("unknown:    %s"), one);
 -              break;
 -      case DIFF_STATUS_UNMERGED:
 -              status_printf_more(s, c, _("unmerged:   %s"), one);
 -              break;
 -      default:
 +      what = wt_status_diff_status_string(status);
 +      if (!what)
                die(_("bug: unhandled diff status %c"), status);
 -      }
 +      /* 1 for colon, which is not part of "what" */
 +      len = strlen(padding) - (utf8_strwidth(what) + 1);
 +      assert(len >= 0);
 +      if (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)
 +              status_printf_more(s, c, "%s:%.*s%s -> %s",
 +                                 what, len, padding, one, two);
 +      else
 +              status_printf_more(s, c, "%s:%.*s%s",
 +                                 what, len, padding, one);
        if (extra.len) {
                status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf);
                strbuf_release(&extra);
@@@ -796,18 -767,6 +796,18 @@@ conclude
        status_printf_ln(s, GIT_COLOR_NORMAL, "");
  }
  
 +void wt_status_truncate_message_at_cut_line(struct strbuf *buf)
 +{
 +      const char *p;
 +      struct strbuf pattern = STRBUF_INIT;
 +
 +      strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line);
 +      p = strstr(buf->buf, pattern.buf);
 +      if (p && (p == buf->buf || p[-1] == '\n'))
 +              strbuf_setlen(buf, p - buf->buf);
 +      strbuf_release(&pattern);
 +}
 +
  static void wt_status_print_verbose(struct wt_status *s)
  {
        struct rev_info rev;
         * If we're not going to stdout, then we definitely don't
         * want color, since we are going to the commit message
         * file (and even the "auto" setting won't work, since it
 -       * will have checked isatty on stdout).
 +       * will have checked isatty on stdout). But we then do want
 +       * to insert the scissor line here to reliably remove the
 +       * diff before committing.
         */
 -      if (s->fp != stdout)
 +      if (s->fp != stdout) {
 +              const char *explanation = _("Do not touch the line above.\nEverything below will be removed.");
 +              struct strbuf buf = STRBUF_INIT;
 +
                rev.diffopt.use_color = 0;
 +              fprintf(s->fp, "%c %s", comment_line_char, cut_line);
 +              strbuf_add_commented_lines(&buf, explanation, strlen(explanation));
 +              fputs(buf.buf, s->fp);
 +              strbuf_release(&buf);
 +      }
        run_diff_index(&rev, 1);
  }
  
@@@ -854,7 -803,7 +854,7 @@@ static void wt_status_print_tracking(st
        int i;
  
        assert(s->branch && !s->is_initial);
-       if (prefixcmp(s->branch, "refs/heads/"))
+       if (!starts_with(s->branch, "refs/heads/"))
                return;
        branch = branch_get(s->branch + 11);
        if (!format_tracking_info(branch, &sb))
@@@ -1113,9 -1062,9 +1113,9 @@@ static char *read_and_strip_branch(cons
                strbuf_setlen(&sb, sb.len - 1);
        if (!sb.len)
                goto got_nothing;
-       if (!prefixcmp(sb.buf, "refs/heads/"))
+       if (starts_with(sb.buf, "refs/heads/"))
                strbuf_remove(&sb,0, strlen("refs/heads/"));
-       else if (!prefixcmp(sb.buf, "refs/"))
+       else if (starts_with(sb.buf, "refs/"))
                ;
        else if (!get_sha1_hex(sb.buf, sha1)) {
                const char *abbrev;
@@@ -1145,7 -1094,7 +1145,7 @@@ static int grab_1st_switch(unsigned cha
        struct grab_1st_switch_cbdata *cb = cb_data;
        const char *target = NULL, *end;
  
-       if (prefixcmp(message, "checkout: moving from "))
+       if (!starts_with(message, "checkout: moving from "))
                return 0;
        message += strlen("checkout: moving from ");
        target = strstr(message, " to ");
@@@ -1180,9 -1129,9 +1180,9 @@@ static void wt_status_get_detached_from
             ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
              !hashcmp(cb.nsha1, commit->object.sha1)))) {
                int ofs;
-               if (!prefixcmp(ref, "refs/tags/"))
+               if (starts_with(ref, "refs/tags/"))
                        ofs = strlen("refs/tags/");
-               else if (!prefixcmp(ref, "refs/remotes/"))
+               else if (starts_with(ref, "refs/remotes/"))
                        ofs = strlen("refs/remotes/");
                else
                        ofs = 0;
@@@ -1271,7 -1220,7 +1271,7 @@@ void wt_status_print(struct wt_status *
        if (s->branch) {
                const char *on_what = _("On branch ");
                const char *branch_name = s->branch;
-               if (!prefixcmp(branch_name, "refs/heads/"))
+               if (starts_with(branch_name, "refs/heads/"))
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
                        branch_status_color = color(WT_STATUS_NOBRANCH, s);
@@@ -1472,7 -1421,7 +1472,7 @@@ static void wt_shortstatus_print_tracki
                return;
        branch_name = s->branch;
  
-       if (!prefixcmp(branch_name, "refs/heads/"))
+       if (starts_with(branch_name, "refs/heads/"))
                branch_name += 11;
        else if (!strcmp(branch_name, "HEAD")) {
                branch_name = _("HEAD (no branch)");