Merge branch 'jp/string-list-api-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Wed, 30 Jun 2010 18:55:38 +0000 (11:55 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 30 Jun 2010 18:55:38 +0000 (11:55 -0700)
* jp/string-list-api-cleanup:
string_list: Fix argument order for string_list_append
string_list: Fix argument order for string_list_lookup
string_list: Fix argument order for string_list_insert_at_index
string_list: Fix argument order for string_list_insert
string_list: Fix argument order for for_each_string_list
string_list: Fix argument order for print_string_list

15 files changed:
1  2 
builtin/apply.c
builtin/commit.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/log.c
builtin/ls-files.c
builtin/receive-pack.c
builtin/remote.c
builtin/rerere.c
http-backend.c
notes.c
remote.c
rerere.c
revision.c
wt-status.c
diff --combined builtin/apply.c
index 562e5345fc969a5ad03b3ade4952668bcec09136,03639282e1cc3e14d83043047a174bd71a776db3..12ef9ea8afb0aa1e554e3ce6c6085e97ff7e7466
@@@ -56,7 -56,7 +56,7 @@@ static enum ws_error_action 
        nowarn_ws_error,
        warn_on_ws_error,
        die_on_ws_error,
 -      correct_ws_error,
 +      correct_ws_error
  } ws_error_action = warn_on_ws_error;
  static int whitespace_error;
  static int squelch_whitespace_errors = 5;
@@@ -64,7 -64,7 +64,7 @@@ static int applied_after_fixing_ws
  
  static enum ws_ignore {
        ignore_ws_none,
 -      ignore_ws_change,
 +      ignore_ws_change
  } ws_ignore_action = ignore_ws_none;
  
  
@@@ -2628,7 -2628,7 +2628,7 @@@ static struct patch *in_fn_table(const 
        if (name == NULL)
                return NULL;
  
-       item = string_list_lookup(name, &fn_table);
+       item = string_list_lookup(&fn_table, name);
        if (item != NULL)
                return (struct patch *)item->util;
  
@@@ -2664,7 -2664,7 +2664,7 @@@ static void add_to_fn_table(struct patc
         * file creations and copies
         */
        if (patch->new_name != NULL) {
-               item = string_list_insert(patch->new_name, &fn_table);
+               item = string_list_insert(&fn_table, patch->new_name);
                item->util = patch;
        }
  
         * later chunks shouldn't patch old names
         */
        if ((patch->new_name == NULL) || (patch->is_rename)) {
-               item = string_list_insert(patch->old_name, &fn_table);
+               item = string_list_insert(&fn_table, patch->old_name);
                item->util = PATH_WAS_DELETED;
        }
  }
@@@ -2686,7 -2686,7 +2686,7 @@@ static void prepare_fn_table(struct pat
        while (patch) {
                if ((patch->new_name == NULL) || (patch->is_rename)) {
                        struct string_list_item *item;
-                       item = string_list_insert(patch->old_name, &fn_table);
+                       item = string_list_insert(&fn_table, patch->old_name);
                        item->util = PATH_TO_BE_DELETED;
                }
                patch = patch->next;
@@@ -3394,7 -3394,7 +3394,7 @@@ static void add_name_limit(const char *
  {
        struct string_list_item *it;
  
-       it = string_list_append(name, &limit_by_name);
+       it = string_list_append(&limit_by_name, name);
        it->util = exclude ? NULL : (void *) 1;
  }
  
diff --combined builtin/commit.c
index 3d99cf91587593ecce56a603ca064888cdd60988,763fe74b905f28a2061fa1da16b935d3bf4d5fd4..c6b053a508facc2029df489d069e0667cfeb28fe
@@@ -48,11 -48,6 +48,11 @@@ static const char implicit_ident_advice
  "\n"
  "    git commit --amend --author='Your Name <you@example.com>'\n";
  
 +static const char empty_amend_advice[] =
 +"You asked to amend the most recent commit, but doing so would make\n"
 +"it empty. You can repeat your command with --allow-empty, or you can\n"
 +"remove the commit entirely with \"git reset HEAD^\".\n";
 +
  static unsigned char head_sha1[20];
  
  static char *use_message_buffer;
@@@ -62,7 -57,7 +62,7 @@@ static struct lock_file false_lock; /* 
  static enum {
        COMMIT_AS_IS = 1,
        COMMIT_NORMAL,
 -      COMMIT_PARTIAL,
 +      COMMIT_PARTIAL
  } commit_style;
  
  static const char *logfile, *force_author;
@@@ -83,7 -78,7 +83,7 @@@ static char *untracked_files_arg, *forc
  static enum {
        CLEANUP_SPACE,
        CLEANUP_NONE,
 -      CLEANUP_ALL,
 +      CLEANUP_ALL
  } cleanup_mode;
  static char *cleanup_arg;
  
@@@ -96,9 -91,8 +96,9 @@@ static int null_termination
  static enum {
        STATUS_FORMAT_LONG,
        STATUS_FORMAT_SHORT,
 -      STATUS_FORMAT_PORCELAIN,
 +      STATUS_FORMAT_PORCELAIN
  } status_format = STATUS_FORMAT_LONG;
 +static int status_show_branch;
  
  static int opt_parse_m(const struct option *opt, const char *arg, int unset)
  {
@@@ -140,7 -134,6 +140,7 @@@ static struct option builtin_commit_opt
        OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
        OPT_SET_INT(0, "short", &status_format, "show status concisely",
                    STATUS_FORMAT_SHORT),
 +      OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
        OPT_SET_INT(0, "porcelain", &status_format,
                    "show porcelain output format", STATUS_FORMAT_PORCELAIN),
        OPT_BOOLEAN('z', "null", &null_termination,
@@@ -219,7 -212,7 +219,7 @@@ static int list_paths(struct string_lis
                        continue;
                if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
                        continue;
-               item = string_list_insert(ce->name, list);
+               item = string_list_insert(list, ce->name);
                if (ce_skip_worktree(ce))
                        item->util = item; /* better a valid pointer than a fake one */
        }
@@@ -431,7 -424,7 +431,7 @@@ static int run_status(FILE *fp, const c
  
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
 -              wt_shortstatus_print(s, null_termination);
 +              wt_shortstatus_print(s, null_termination, status_show_branch);
                break;
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(s, null_termination);
@@@ -469,21 -462,15 +469,21 @@@ static void determine_author_info(void
                if (!a)
                        die("invalid commit: %s", use_message);
  
 -              lb = strstr(a + 8, " <");
 -              rb = strstr(a + 8, "> ");
 -              eol = strchr(a + 8, '\n');
 -              if (!lb || !rb || !eol)
 +              lb = strchrnul(a + strlen("\nauthor "), '<');
 +              rb = strchrnul(lb, '>');
 +              eol = strchrnul(rb, '\n');
 +              if (!*lb || !*rb || !*eol)
                        die("invalid commit: %s", use_message);
  
 -              name = xstrndup(a + 8, lb - (a + 8));
 -              email = xstrndup(lb + 2, rb - (lb + 2));
 -              date = xstrndup(rb + 2, eol - (rb + 2));
 +              if (lb == a + strlen("\nauthor "))
 +                      /* \nauthor <foo@example.com> */
 +                      name = xcalloc(1, 1);
 +              else
 +                      name = xmemdupz(a + strlen("\nauthor "),
 +                                      (lb - strlen(" ") -
 +                                       (a + strlen("\nauthor "))));
 +              email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
 +              date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
        }
  
        if (force_author) {
@@@ -713,8 -700,6 +713,8 @@@ static int prepare_to_commit(const cha
        if (!commitable && !in_merge && !allow_empty &&
            !(amend && is_a_merge(head_sha1))) {
                run_status(stdout, index_file, prefix, 0, s);
 +              if (amend)
 +                      fputs(empty_amend_advice, stderr);
                return 0;
        }
  
  
        if (use_editor) {
                char index[PATH_MAX];
 -              const char *env[2] = { index, NULL };
 +              const char *env[2] = { NULL };
 +              env[0] =  index;
                snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
                if (launch_editor(git_path(commit_editmsg), NULL, env)) {
                        fprintf(stderr,
@@@ -1046,8 -1030,6 +1046,8 @@@ int cmd_status(int argc, const char **a
                OPT__VERBOSE(&verbose),
                OPT_SET_INT('s', "short", &status_format,
                            "show status concisely", STATUS_FORMAT_SHORT),
 +              OPT_BOOLEAN('b', "branch", &status_show_branch,
 +                          "show branch information"),
                OPT_SET_INT(0, "porcelain", &status_format,
                            "show porcelain output format",
                            STATUS_FORMAT_PORCELAIN),
  
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
 -              wt_shortstatus_print(&s, null_termination);
 +              wt_shortstatus_print(&s, null_termination, status_show_branch);
                break;
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(&s, null_termination);
@@@ -1175,11 -1157,13 +1175,11 @@@ static void print_summary(const char *p
                initial_commit ? " (root-commit)" : "");
  
        if (!log_tree_commit(&rev, commit)) {
 -              struct pretty_print_context ctx = {0};
 -              struct strbuf buf = STRBUF_INIT;
 -              ctx.date_mode = DATE_NORMAL;
 -              format_commit_message(commit, format.buf + 7, &buf, &ctx);
 -              printf("%s\n", buf.buf);
 -              strbuf_release(&buf);
 +              rev.always_show_header = 1;
 +              rev.use_terminator = 1;
 +              log_tree_commit(&rev, commit);
        }
 +
        strbuf_release(&format);
  }
  
@@@ -1267,16 -1251,13 +1267,16 @@@ int cmd_commit(int argc, const char **a
        }
  
        /* Determine parents */
 +      reflog_msg = getenv("GIT_REFLOG_ACTION");
        if (initial_commit) {
 -              reflog_msg = "commit (initial)";
 +              if (!reflog_msg)
 +                      reflog_msg = "commit (initial)";
        } else if (amend) {
                struct commit_list *c;
                struct commit *commit;
  
 -              reflog_msg = "commit (amend)";
 +              if (!reflog_msg)
 +                      reflog_msg = "commit (amend)";
                commit = lookup_commit(head_sha1);
                if (!commit || parse_commit(commit))
                        die("could not parse HEAD commit");
                struct strbuf m = STRBUF_INIT;
                FILE *fp;
  
 -              reflog_msg = "commit (merge)";
 +              if (!reflog_msg)
 +                      reflog_msg = "commit (merge)";
                pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
                fp = fopen(git_path("MERGE_HEAD"), "r");
                if (fp == NULL)
                if (allow_fast_forward)
                        parents = reduce_heads(parents);
        } else {
 -              reflog_msg = "commit";
 +              if (!reflog_msg)
 +                      reflog_msg = "commit";
                pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
        }
  
diff --combined builtin/fetch.c
index 5cb369cfd1fbca1ceeee06b751c2b047f8a0e5b5,c5e24b50ec73d181bc67a0702d84b96748c3fbb5..6eb1dfea092e2873c5910bf4389d3b071786d8b0
@@@ -528,7 -528,7 +528,7 @@@ static int add_existing(const char *ref
                        int flag, void *cbdata)
  {
        struct string_list *list = (struct string_list *)cbdata;
-       struct string_list_item *item = string_list_insert(refname, list);
+       struct string_list_item *item = string_list_insert(list, refname);
        item->util = (void *)sha1;
        return 0;
  }
@@@ -574,10 -574,9 +574,10 @@@ static void find_non_local_tags(struct 
  {
        struct string_list existing_refs = { NULL, 0, 0, 0 };
        struct string_list remote_refs = { NULL, 0, 0, 0 };
 -      struct tag_data data = {head, tail};
 +      struct tag_data data;
        const struct ref *ref;
        struct string_list_item *item = NULL;
 +      data.head = head; data.tail = tail;
  
        for_each_ref(add_existing, &existing_refs);
        for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
                    string_list_has_string(&existing_refs, ref->name))
                        continue;
  
-               item = string_list_insert(ref->name, &remote_refs);
+               item = string_list_insert(&remote_refs, ref->name);
                item->util = (void *)ref->old_sha1;
        }
        string_list_clear(&existing_refs, 0);
         * For all the tags in the remote_refs string list, call
         * add_to_tail to add them to the list of refs to be fetched
         */
-       for_each_string_list(add_to_tail, &remote_refs, &data);
+       for_each_string_list(&remote_refs, add_to_tail, &data);
  
        string_list_clear(&remote_refs, 0);
  }
@@@ -696,8 -695,8 +696,8 @@@ static int do_fetch(struct transport *t
  
        for (rm = ref_map; rm; rm = rm->next) {
                if (rm->peer_ref) {
-                       peer_item = string_list_lookup(rm->peer_ref->name,
-                                                      &existing_refs);
+                       peer_item = string_list_lookup(&existing_refs,
+                                                      rm->peer_ref->name);
                        if (peer_item)
                                hashcpy(rm->peer_ref->old_sha1,
                                        peer_item->util);
@@@ -746,7 -745,7 +746,7 @@@ static int get_one_remote_for_fetch(str
  {
        struct string_list *list = priv;
        if (!remote->skip_default_update)
-               string_list_append(remote->name, list);
+               string_list_append(list, remote->name);
        return 0;
  }
  
@@@ -765,8 -764,8 +765,8 @@@ static int get_remote_group(const char 
                int space = strcspn(value, " \t\n");
                while (*value) {
                        if (space > 1) {
-                               string_list_append(xstrndup(value, space),
-                                                  g->list);
+                               string_list_append(g->list,
+                                                  xstrndup(value, space));
                        }
                        value += space + (value[space] != '\0');
                        space = strcspn(value, " \t\n");
  static int add_remote_or_group(const char *name, struct string_list *list)
  {
        int prev_nr = list->nr;
 -      struct remote_group_data g = { name, list };
 +      struct remote_group_data g;
 +      g.name = name; g.list = list;
  
        git_config(get_remote_group, &g);
        if (list->nr == prev_nr) {
                if (!remote_is_configured(name))
                        return 0;
                remote = remote_get(name);
-               string_list_append(remote->name, list);
+               string_list_append(list, remote->name);
        }
        return 1;
  }
diff --combined builtin/fmt-merge-msg.c
index 44204257c72afd3a6a24966b9a11cf9f2374e9ce,601201264ed0cd6f331c6517efa3e6a1504002bd..bc3c5e6d3ec50eaa9829358ee0a82a1438c19f89
@@@ -82,7 -82,7 +82,7 @@@ static int handle_line(char *line
  
        item = unsorted_string_list_lookup(&srcs, src);
        if (!item) {
-               item = string_list_append(src, &srcs);
+               item = string_list_append(&srcs, src);
                item->util = xcalloc(1, sizeof(struct src_data));
                init_src_data(item->util);
        }
                src_data->head_status |= 1;
        } else if (!prefixcmp(line, "branch ")) {
                origin = line + 7;
-               string_list_append(origin, &src_data->branch);
+               string_list_append(&src_data->branch, origin);
                src_data->head_status |= 2;
        } else if (!prefixcmp(line, "tag ")) {
                origin = line;
-               string_list_append(origin + 4, &src_data->tag);
+               string_list_append(&src_data->tag, origin + 4);
                src_data->head_status |= 2;
        } else if (!prefixcmp(line, "remote branch ")) {
                origin = line + 14;
-               string_list_append(origin, &src_data->r_branch);
+               string_list_append(&src_data->r_branch, origin);
                src_data->head_status |= 2;
        } else {
                origin = src;
-               string_list_append(line, &src_data->generic);
+               string_list_append(&src_data->generic, line);
                src_data->head_status |= 2;
        }
  
                sprintf(new_origin, "%s of %s", origin, src);
                origin = new_origin;
        }
-       string_list_append(origin, &origins)->util = sha1;
+       string_list_append(&origins, origin)->util = sha1;
        return 0;
  }
  
@@@ -176,10 -176,10 +176,10 @@@ static void shortlog(const char *name, 
                strbuf_ltrim(&sb);
  
                if (!sb.len)
-                       string_list_append(sha1_to_hex(commit->object.sha1),
-                                          &subjects);
+                       string_list_append(&subjects,
+                                          sha1_to_hex(commit->object.sha1));
                else
-                       string_list_append(strbuf_detach(&sb, NULL), &subjects);
+                       string_list_append(&subjects, strbuf_detach(&sb, NULL));
        }
  
        if (count > limit)
        string_list_clear(&subjects, 0);
  }
  
 -int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
 -      int limit = 20, i = 0, pos = 0;
 +static void do_fmt_merge_msg_title(struct strbuf *out,
 +      const char *current_branch) {
 +      int i = 0;
        char *sep = "";
 -      unsigned char head_sha1[20];
 -      const char *current_branch;
 -
 -      /* get current branch */
 -      current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
 -      if (!current_branch)
 -              die("No current branch");
 -      if (!prefixcmp(current_branch, "refs/heads/"))
 -              current_branch += 11;
 -
 -      /* get a line */
 -      while (pos < in->len) {
 -              int len;
 -              char *newline, *p = in->buf + pos;
 -
 -              newline = strchr(p, '\n');
 -              len = newline ? newline - p : strlen(p);
 -              pos += len + !!newline;
 -              i++;
 -              p[len] = 0;
 -              if (handle_line(p))
 -                      die ("Error in line %d: %.*s", i, len, p);
 -      }
 -
 -      if (!srcs.nr)
 -              return 0;
  
        strbuf_addstr(out, "Merge ");
        for (i = 0; i < srcs.nr; i++) {
                strbuf_addch(out, '\n');
        else
                strbuf_addf(out, " into %s\n", current_branch);
 +}
 +
 +static int do_fmt_merge_msg(int merge_title, int merge_summary,
 +      struct strbuf *in, struct strbuf *out) {
 +      int limit = 20, i = 0, pos = 0;
 +      unsigned char head_sha1[20];
 +      const char *current_branch;
 +
 +      /* get current branch */
 +      current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
 +      if (!current_branch)
 +              die("No current branch");
 +      if (!prefixcmp(current_branch, "refs/heads/"))
 +              current_branch += 11;
 +
 +      /* get a line */
 +      while (pos < in->len) {
 +              int len;
 +              char *newline, *p = in->buf + pos;
 +
 +              newline = strchr(p, '\n');
 +              len = newline ? newline - p : strlen(p);
 +              pos += len + !!newline;
 +              i++;
 +              p[len] = 0;
 +              if (handle_line(p))
 +                      die ("Error in line %d: %.*s", i, len, p);
 +      }
 +
 +      if (!srcs.nr)
 +              return 0;
 +
 +      if (merge_title)
 +              do_fmt_merge_msg_title(out, current_branch);
  
        if (merge_summary) {
                struct commit *head;
                rev.ignore_merges = 1;
                rev.limited = 1;
  
 +              if (suffixcmp(out->buf, "\n"))
 +                      strbuf_addch(out, '\n');
 +
                for (i = 0; i < origins.nr; i++)
                        shortlog(origins.items[i].string, origins.items[i].util,
                                        head, &rev, limit, out);
        return 0;
  }
  
 +int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
 +      return do_fmt_merge_msg(1, merge_summary, in, out);
 +}
 +
 +int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out) {
 +      return do_fmt_merge_msg(0, 1, in, out);
 +}
 +
  int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
  {
        const char *inpath = NULL;
diff --combined builtin/log.c
index f068583618d4120756ea394f9d64a4461f1c0c4b,40bdd01e82136594f23472636373b081dd26bc98..7cb9317af7d7de95197244b60cb90fbac8ddd947
@@@ -535,13 -535,13 +535,13 @@@ static void add_header(const char *valu
                len--;
  
        if (!strncasecmp(value, "to: ", 4)) {
-               item = string_list_append(value + 4, &extra_to);
+               item = string_list_append(&extra_to, value + 4);
                len -= 4;
        } else if (!strncasecmp(value, "cc: ", 4)) {
-               item = string_list_append(value + 4, &extra_cc);
+               item = string_list_append(&extra_cc, value + 4);
                len -= 4;
        } else {
-               item = string_list_append(value, &extra_hdr);
+               item = string_list_append(&extra_hdr, value);
        }
  
        item->string[len] = '\0';
  
  #define THREAD_SHALLOW 1
  #define THREAD_DEEP 2
 -static int thread = 0;
 -static int do_signoff = 0;
 +static int thread;
 +static int do_signoff;
 +static const char *signature = git_version_string;
  
  static int git_format_config(const char *var, const char *value, void *cb)
  {
        if (!strcmp(var, "format.to")) {
                if (!value)
                        return config_error_nonbool(var);
-               string_list_append(value, &extra_to);
+               string_list_append(&extra_to, value);
                return 0;
        }
        if (!strcmp(var, "format.cc")) {
                if (!value)
                        return config_error_nonbool(var);
-               string_list_append(value, &extra_cc);
+               string_list_append(&extra_cc, value);
                return 0;
        }
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
                do_signoff = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp(var, "format.signature"))
 +              return git_config_string(&signature, var, value);
  
        return git_log_config(var, value, cb);
  }
@@@ -706,12 -703,6 +706,12 @@@ static void gen_message_id(struct rev_i
        info->message_id = strbuf_detach(&buf, NULL);
  }
  
 +static void print_signature(void)
 +{
 +      if (signature && *signature)
 +              printf("-- \n%s\n\n", signature);
 +}
 +
  static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              int numbered, int numbered_files,
                              struct commit *origin,
        diff_flush(&opts);
  
        printf("\n");
 +      print_signature();
  }
  
  static const char *clean_message_id(const char *msg_id)
@@@ -959,7 -949,7 +959,7 @@@ static int to_callback(const struct opt
        if (unset)
                string_list_clear(&extra_to, 0);
        else
-               string_list_append(arg, &extra_to);
+               string_list_append(&extra_to, arg);
        return 0;
  }
  
@@@ -968,7 -958,7 +968,7 @@@ static int cc_callback(const struct opt
        if (unset)
                string_list_clear(&extra_cc, 0);
        else
-               string_list_append(arg, &extra_cc);
+               string_list_append(&extra_cc, arg);
        return 0;
  }
  
@@@ -1045,8 -1035,6 +1045,8 @@@ int cmd_format_patch(int argc, const ch
                { OPTION_CALLBACK, 0, "thread", &thread, "style",
                            "enable message threading, styles: shallow, deep",
                            PARSE_OPT_OPTARG, thread_callback },
 +              OPT_STRING(0, "signature", &signature, "signature",
 +                          "add a signature"),
                OPT_END()
        };
  
                rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
        if (in_reply_to) {
                const char *msgid = clean_message_id(in_reply_to);
-               string_list_append(msgid, rev.ref_message_ids);
+               string_list_append(rev.ref_message_ids, msgid);
        }
        rev.numbered_files = numbered_files;
        rev.patch_suffix = fmt_patch_suffix;
                                    && (!cover_letter || rev.nr > 1))
                                        free(rev.message_id);
                                else
-                                       string_list_append(rev.message_id,
-                                                          rev.ref_message_ids);
+                                       string_list_append(rev.ref_message_ids,
+                                                          rev.message_id);
                        }
                        gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
                }
                                       mime_boundary_leader,
                                       rev.mime_boundary);
                        else
 -                              printf("-- \n%s\n\n", git_version_string);
 +                              print_signature();
                }
                if (!use_stdout)
                        fclose(stdout);
diff --combined builtin/ls-files.c
index 080404769365fef8176c75fff5f526cf6138df6a,3eeacdc6999249983e68add8a6c5745df90c647b..1b9b8a8b4ac2baff6b9676bcf23b148f147dac6a
@@@ -26,9 -26,8 +26,9 @@@ static int show_killed
  static int show_valid_bit;
  static int line_terminator = '\n';
  
 +static const char *prefix;
 +static int max_prefix_len;
  static int prefix_len;
 -static int prefix_offset;
  static const char **pathspec;
  static int error_unmatch;
  static char *ps_matched;
@@@ -44,15 -43,10 +44,15 @@@ static const char *tag_modified = ""
  static const char *tag_skip_worktree = "";
  static const char *tag_resolve_undo = "";
  
 +static void write_name(const char* name, size_t len)
 +{
 +      write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
 +                      line_terminator);
 +}
 +
  static void show_dir_entry(const char *tag, struct dir_entry *ent)
  {
 -      int len = prefix_len;
 -      int offset = prefix_offset;
 +      int len = max_prefix_len;
  
        if (len >= ent->len)
                die("git ls-files: internal error - directory entry not superset of prefix");
@@@ -61,7 -55,7 +61,7 @@@
                return;
  
        fputs(tag, stdout);
 -      write_name_quoted(ent->name + offset, stdout, line_terminator);
 +      write_name(ent->name, ent->len);
  }
  
  static void show_other_files(struct dir_struct *dir)
@@@ -127,7 -121,8 +127,7 @@@ static void show_killed_files(struct di
  
  static void show_ce_entry(const char *tag, struct cache_entry *ce)
  {
 -      int len = prefix_len;
 -      int offset = prefix_offset;
 +      int len = max_prefix_len;
  
        if (len >= ce_namelen(ce))
                die("git ls-files: internal error - cache entry not superset of prefix");
                       find_unique_abbrev(ce->sha1,abbrev),
                       ce_stage(ce));
        }
 -      write_name_quoted(ce->name + offset, stdout, line_terminator);
 +      write_name(ce->name, ce_namelen(ce));
  }
  
  static int show_one_ru(struct string_list_item *item, void *cbdata)
  {
 -      int offset = prefix_offset;
        const char *path = item->string;
        struct resolve_undo_info *ui = item->util;
        int i, len;
  
        len = strlen(path);
 -      if (len < prefix_len)
 +      if (len < max_prefix_len)
                return 0; /* outside of the prefix */
 -      if (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))
 +      if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
                return 0; /* uninterested */
        for (i = 0; i < 3; i++) {
                if (!ui->mode[i])
                printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
                       find_unique_abbrev(ui->sha1[i], abbrev),
                       i + 1);
 -              write_name_quoted(path + offset, stdout, line_terminator);
 +              write_name(path, len);
        }
        return 0;
  }
  
 -static void show_ru_info(const char *prefix)
 +static void show_ru_info(void)
  {
        if (!the_index.resolve_undo)
                return;
-       for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
+       for_each_string_list(the_index.resolve_undo, show_one_ru, NULL);
  }
  
 -static void show_files(struct dir_struct *dir, const char *prefix)
 +static void show_files(struct dir_struct *dir)
  {
        int i;
  
   */
  static void prune_cache(const char *prefix)
  {
 -      int pos = cache_name_pos(prefix, prefix_len);
 +      int pos = cache_name_pos(prefix, max_prefix_len);
        unsigned int first, last;
  
        if (pos < 0)
        while (last > first) {
                int next = (last + first) >> 1;
                struct cache_entry *ce = active_cache[next];
 -              if (!strncmp(ce->name, prefix, prefix_len)) {
 +              if (!strncmp(ce->name, prefix, max_prefix_len)) {
                        first = next+1;
                        continue;
                }
        active_nr = last;
  }
  
 -static const char *verify_pathspec(const char *prefix)
 +static const char *pathspec_prefix(const char *prefix)
  {
        const char **p, *n, *prev;
        unsigned long max;
  
 +      if (!pathspec) {
 +              max_prefix_len = prefix ? strlen(prefix) : 0;
 +              return prefix;
 +      }
 +
        prev = NULL;
        max = PATH_MAX;
        for (p = pathspec; (n = *p) != NULL; p++) {
                }
        }
  
 -      if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
 -              die("git ls-files: cannot generate relative filenames containing '..'");
 -
 -      prefix_len = max;
 +      max_prefix_len = max;
        return max ? xmemdupz(prev, max) : NULL;
  }
  
@@@ -380,7 -374,7 +380,7 @@@ void overlay_tree_on_cache(const char *
        }
  }
  
 -int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
 +int report_path_error(const char *ps_matched, const char **pathspec, int prefix_len)
  {
        /*
         * Make sure all pathspec matched; otherwise it is an error.
                        continue;
  
                error("pathspec '%s' did not match any file(s) known to git.",
 -                    pathspec[num] + prefix_offset);
 +                    pathspec[num] + prefix_len);
                errors++;
        }
        return errors;
@@@ -462,10 -456,9 +462,10 @@@ static int option_parse_exclude_standar
        return 0;
  }
  
 -int cmd_ls_files(int argc, const char **argv, const char *prefix)
 +int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
  {
        int require_work_tree = 0, show_tag = 0;
 +      const char *max_prefix;
        struct dir_struct dir;
        struct option builtin_ls_files_options[] = {
                { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
                { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
                        "add the standard git exclusions",
                        PARSE_OPT_NOARG, option_parse_exclude_standard },
 -              { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL,
 +              { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
                        "make the output relative to the project top directory",
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
                OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
        };
  
        memset(&dir, 0, sizeof(dir));
 +      prefix = cmd_prefix;
        if (prefix)
 -              prefix_offset = strlen(prefix);
 +              prefix_len = strlen(prefix);
        git_config(git_default_config, NULL);
  
        if (read_cache() < 0)
        if (pathspec)
                strip_trailing_slash_from_submodules();
  
 -      /* Verify that the pathspec matches the prefix */
 -      if (pathspec)
 -              prefix = verify_pathspec(prefix);
 +      /* Find common prefix for all pathspec's */
 +      max_prefix = pathspec_prefix(prefix);
  
        /* Treat unmatching pathspec elements as errors */
        if (pathspec && error_unmatch) {
              show_killed | show_modified | show_resolve_undo))
                show_cached = 1;
  
 -      if (prefix)
 -              prune_cache(prefix);
 +      if (max_prefix)
 +              prune_cache(max_prefix);
        if (with_tree) {
                /*
                 * Basic sanity check; show-stages and show-unmerged
                 */
                if (show_stage || show_unmerged)
                        die("ls-files --with-tree is incompatible with -s or -u");
 -              overlay_tree_on_cache(with_tree, prefix);
 +              overlay_tree_on_cache(with_tree, max_prefix);
        }
 -      show_files(&dir, prefix);
 +      show_files(&dir);
        if (show_resolve_undo)
 -              show_ru_info(prefix);
 +              show_ru_info();
  
        if (ps_matched) {
                int bad;
 -              bad = report_path_error(ps_matched, pathspec, prefix_offset);
 +              bad = report_path_error(ps_matched, pathspec, prefix_len);
                if (bad)
                        fprintf(stderr, "Did you forget to 'git add'?\n");
  
diff --combined builtin/receive-pack.c
index 29bc8d50bbe187654e8b0903627655fbb05a6aea,5170abf2a02d915238bfb5a01e7a1223cdefb642..d634b5a3d5212b8e49217d94430c294b911dfc7d
@@@ -17,7 -17,7 +17,7 @@@ enum deny_action 
        DENY_UNCONFIGURED,
        DENY_IGNORE,
        DENY_WARN,
 -      DENY_REFUSE,
 +      DENY_REFUSE
  };
  
  static int deny_deletes;
@@@ -501,7 -501,7 +501,7 @@@ static void check_aliased_update(struc
        if (!(flag & REF_ISSYMREF))
                return;
  
-       if ((item = string_list_lookup(dst_name, list)) == NULL)
+       if ((item = string_list_lookup(list, dst_name)) == NULL)
                return;
  
        cmd->skip_update = 1;
        dst_cmd->skip_update = 1;
  
        strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));
 -      strcat(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
 +      strcpy(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
        strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));
 -      strcat(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
 +      strcpy(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
        rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
                 " its target '%s' (%s..%s)",
                 cmd->ref_name, cmd_oldh, cmd_newh,
@@@ -534,7 -534,7 +534,7 @@@ static void check_aliased_updates(struc
  
        for (cmd = commands; cmd; cmd = cmd->next) {
                struct string_list_item *item =
-                       string_list_append(cmd->ref_name, &ref_list);
+                       string_list_append(&ref_list, cmd->ref_name);
                item->util = (void *)cmd;
        }
        sort_string_list(&ref_list);
diff --combined builtin/remote.c
index 0a52667e0f7cc14d3a760bf0ec5f6857a0ce9051,03d90cde024967f56af53122ba2b1ca175a83566..6699bc571235167f5cae60106522c09a10ae341d
@@@ -16,7 -16,6 +16,7 @@@ static const char * const builtin_remot
        "git remote [-v | --verbose] show [-n] <name>",
        "git remote prune [-n | --dry-run] <name>",
        "git remote [-v | --verbose] update [-p | --prune] [group | remote]",
 +      "git remote set-branches <name> [--add] <branch>...",
        "git remote set-url <name> <newurl> [<oldurl>]",
        "git remote set-url --add <name> <newurl>",
        "git remote set-url --delete <name> <url>",
@@@ -43,12 -42,6 +43,12 @@@ static const char * const builtin_remot
        NULL
  };
  
 +static const char * const builtin_remote_setbranches_usage[] = {
 +      "git remote set-branches <name> <branch>...",
 +      "git remote set-branches --add <name> <branch>...",
 +      NULL
 +};
 +
  static const char * const builtin_remote_show_usage[] = {
        "git remote show [<options>] <name>",
        NULL
@@@ -94,7 -87,7 +94,7 @@@ static int opt_parse_track(const struc
        if (not)
                string_list_clear(list, 0);
        else
-               string_list_append(arg, list);
+               string_list_append(list, arg);
        return 0;
  }
  
@@@ -117,20 -110,6 +117,20 @@@ enum 
        TAGS_SET = 2
  };
  
 +static int add_branch(const char *key, const char *branchname,
 +              const char *remotename, int mirror, struct strbuf *tmp)
 +{
 +      strbuf_reset(tmp);
 +      strbuf_addch(tmp, '+');
 +      if (mirror)
 +              strbuf_addf(tmp, "refs/%s:refs/%s",
 +                              branchname, branchname);
 +      else
 +              strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
 +                              branchname, remotename, branchname);
 +      return git_config_set_multivar(key, tmp->buf, "^$", 0);
 +}
 +
  static int add(int argc, const char **argv)
  {
        int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
        strbuf_addf(&buf, "remote.%s.fetch", name);
  
        if (track.nr == 0)
-               string_list_append("*", &track);
+               string_list_append(&track, "*");
        for (i = 0; i < track.nr; i++) {
 -              struct string_list_item *item = track.items + i;
 -
 -              strbuf_reset(&buf2);
 -              strbuf_addch(&buf2, '+');
 -              if (mirror)
 -                      strbuf_addf(&buf2, "refs/%s:refs/%s",
 -                                      item->string, item->string);
 -              else
 -                      strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
 -                                      item->string, name, item->string);
 -              if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
 +              if (add_branch(buf.buf, track.items[i].string,
 +                              name, mirror, &buf2))
                        return 1;
        }
  
@@@ -263,7 -251,7 +263,7 @@@ static int config_read_branches(const c
                } else
                        return 0;
  
-               item = string_list_insert(name, &branch_list);
+               item = string_list_insert(&branch_list, name);
  
                if (!item->util)
                        item->util = xcalloc(sizeof(struct branch_info), 1);
                        while (space) {
                                char *merge;
                                merge = xstrndup(value, space - value);
-                               string_list_append(merge, &info->merge);
+                               string_list_append(&info->merge, merge);
                                value = abbrev_branch(space + 1);
                                space = strchr(value, ' ');
                        }
-                       string_list_append(xstrdup(value), &info->merge);
+                       string_list_append(&info->merge, xstrdup(value));
                } else
                        info->rebase = git_config_bool(orig_key, value);
        }
@@@ -319,14 -307,14 +319,14 @@@ static int get_ref_states(const struct 
        for (ref = fetch_map; ref; ref = ref->next) {
                unsigned char sha1[20];
                if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
-                       string_list_append(abbrev_branch(ref->name), &states->new);
+                       string_list_append(&states->new, abbrev_branch(ref->name));
                else
-                       string_list_append(abbrev_branch(ref->name), &states->tracked);
+                       string_list_append(&states->tracked, abbrev_branch(ref->name));
        }
        stale_refs = get_stale_heads(states->remote, fetch_map);
        for (ref = stale_refs; ref; ref = ref->next) {
                struct string_list_item *item =
-                       string_list_append(abbrev_branch(ref->name), &states->stale);
+                       string_list_append(&states->stale, abbrev_branch(ref->name));
                item->util = xstrdup(ref->name);
        }
        free_refs(stale_refs);
@@@ -348,7 -336,7 +348,7 @@@ struct push_info 
                PUSH_STATUS_UPTODATE,
                PUSH_STATUS_FASTFORWARD,
                PUSH_STATUS_OUTOFDATE,
 -              PUSH_STATUS_NOTQUERIED,
 +              PUSH_STATUS_NOTQUERIED
        } status;
  };
  
@@@ -375,8 -363,8 +375,8 @@@ static int get_push_ref_states(const st
                        continue;
                hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
  
-               item = string_list_append(abbrev_branch(ref->peer_ref->name),
-                                         &states->push);
+               item = string_list_append(&states->push,
+                                         abbrev_branch(ref->peer_ref->name));
                item->util = xcalloc(sizeof(struct push_info), 1);
                info = item->util;
                info->forced = ref->force;
@@@ -411,7 -399,7 +411,7 @@@ static int get_push_ref_states_noquery(
  
        states->push.strdup_strings = 1;
        if (!remote->push_refspec_nr) {
-               item = string_list_append("(matching)", &states->push);
+               item = string_list_append(&states->push, "(matching)");
                info = item->util = xcalloc(sizeof(struct push_info), 1);
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(item->string);
        for (i = 0; i < remote->push_refspec_nr; i++) {
                struct refspec *spec = remote->push + i;
                if (spec->matching)
-                       item = string_list_append("(matching)", &states->push);
+                       item = string_list_append(&states->push, "(matching)");
                else if (strlen(spec->src))
-                       item = string_list_append(spec->src, &states->push);
+                       item = string_list_append(&states->push, spec->src);
                else
-                       item = string_list_append("(delete)", &states->push);
+                       item = string_list_append(&states->push, "(delete)");
  
                info = item->util = xcalloc(sizeof(struct push_info), 1);
                info->forced = spec->force;
@@@ -447,7 -435,7 +447,7 @@@ static int get_head_names(const struct 
        matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
                                    fetch_map, 1);
        for (ref = matches; ref; ref = ref->next)
-               string_list_append(abbrev_branch(ref->name), &states->heads);
+               string_list_append(&states->heads, abbrev_branch(ref->name));
  
        free_refs(fetch_map);
        free_refs(matches);
@@@ -511,8 -499,8 +511,8 @@@ static int add_branch_for_removal(cons
        if (prefixcmp(refname, "refs/remotes")) {
                /* advise user how to delete local branches */
                if (!prefixcmp(refname, "refs/heads/"))
-                       string_list_append(abbrev_branch(refname),
-                                          branches->skipped);
+                       string_list_append(branches->skipped,
+                                          abbrev_branch(refname));
                /* silently skip over other non-remote refs */
                return 0;
        }
        if (flags & REF_ISSYMREF)
                return unlink(git_path("%s", refname));
  
-       item = string_list_append(refname, branches->branches);
+       item = string_list_append(branches->branches, refname);
        item->util = xmalloc(20);
        hashcpy(item->util, sha1);
  
@@@ -546,7 -534,7 +546,7 @@@ static int read_remote_branches(const c
  
        strbuf_addf(&buf, "refs/remotes/%s", rename->old);
        if (!prefixcmp(refname, buf.buf)) {
-               item = string_list_append(xstrdup(refname), rename->remote_branches);
+               item = string_list_append(rename->remote_branches, xstrdup(refname));
                symref = resolve_ref(refname, orig_sha1, 1, &flag);
                if (flag & REF_ISSYMREF)
                        item->util = xstrdup(symref);
@@@ -736,14 -724,11 +736,14 @@@ static int rm(int argc, const char **ar
        struct known_remotes known_remotes = { NULL, NULL };
        struct string_list branches = { NULL, 0, 0, 1 };
        struct string_list skipped = { NULL, 0, 0, 1 };
 -      struct branches_for_remote cb_data = {
 -              NULL, &branches, &skipped, &known_remotes
 -      };
 +      struct branches_for_remote cb_data;
        int i, result;
  
 +      memset(&cb_data, 0, sizeof(cb_data));
 +      cb_data.branches = &branches;
 +      cb_data.skipped = &skipped;
 +      cb_data.keep = &known_remotes;
 +
        if (argc != 2)
                usage_with_options(builtin_remote_rm_usage, options);
  
@@@ -832,7 -817,7 +832,7 @@@ static int append_ref_to_tracked_list(c
        memset(&refspec, 0, sizeof(refspec));
        refspec.dst = (char *)refname;
        if (!remote_find_tracking(states->remote, &refspec))
-               string_list_append(abbrev_branch(refspec.src), &states->tracked);
+               string_list_append(&states->tracked, abbrev_branch(refspec.src));
  
        return 0;
  }
@@@ -885,7 -870,7 +885,7 @@@ static int add_remote_to_show_info(stru
        int n = strlen(item->string);
        if (n > info->width)
                info->width = n;
-       string_list_insert(item->string, info->list);
+       string_list_insert(info->list, item->string);
        return 0;
  }
  
@@@ -932,7 -917,7 +932,7 @@@ static int add_local_to_show_info(struc
        if (branch_info->rebase)
                show_info->any_rebase = 1;
  
-       item = string_list_insert(branch_item->string, show_info->list);
+       item = string_list_insert(show_info->list, branch_item->string);
        item->util = branch_info;
  
        return 0;
@@@ -980,7 -965,7 +980,7 @@@ static int add_push_to_show_info(struc
                show_info->width = n;
        if ((n = strlen(push_info->dest)) > show_info->width2)
                show_info->width2 = n;
-       item = string_list_append(push_item->string, show_info->list);
+       item = string_list_append(show_info->list, push_item->string);
        item->util = push_item->util;
        return 0;
  }
@@@ -1096,24 -1081,24 +1096,24 @@@ static int show(int argc, const char **
  
                /* remote branch info */
                info.width = 0;
-               for_each_string_list(add_remote_to_show_info, &states.new, &info);
-               for_each_string_list(add_remote_to_show_info, &states.tracked, &info);
-               for_each_string_list(add_remote_to_show_info, &states.stale, &info);
+               for_each_string_list(&states.new, add_remote_to_show_info, &info);
+               for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
+               for_each_string_list(&states.stale, add_remote_to_show_info, &info);
                if (info.list->nr)
                        printf("  Remote branch%s:%s\n",
                               info.list->nr > 1 ? "es" : "",
                                no_query ? " (status not queried)" : "");
-               for_each_string_list(show_remote_info_item, info.list, &info);
+               for_each_string_list(info.list, show_remote_info_item, &info);
                string_list_clear(info.list, 0);
  
                /* git pull info */
                info.width = 0;
                info.any_rebase = 0;
-               for_each_string_list(add_local_to_show_info, &branch_list, &info);
+               for_each_string_list(&branch_list, add_local_to_show_info, &info);
                if (info.list->nr)
                        printf("  Local branch%s configured for 'git pull':\n",
                               info.list->nr > 1 ? "es" : "");
-               for_each_string_list(show_local_info_item, info.list, &info);
+               for_each_string_list(info.list, show_local_info_item, &info);
                string_list_clear(info.list, 0);
  
                /* git push info */
                        printf("  Local refs will be mirrored by 'git push'\n");
  
                info.width = info.width2 = 0;
-               for_each_string_list(add_push_to_show_info, &states.push, &info);
+               for_each_string_list(&states.push, add_push_to_show_info, &info);
                qsort(info.list->items, info.list->nr,
                        sizeof(*info.list->items), cmp_string_with_push);
                if (info.list->nr)
                        printf("  Local ref%s configured for 'git push'%s:\n",
                                info.list->nr > 1 ? "s" : "",
                                no_query ? " (status not queried)" : "");
-               for_each_string_list(show_push_info_item, info.list, &info);
+               for_each_string_list(info.list, show_push_info_item, &info);
                string_list_clear(info.list, 0);
  
                free_remote_ref_states(&states);
@@@ -1299,72 -1284,6 +1299,72 @@@ static int update(int argc, const char 
        return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
  }
  
 +static int remove_all_fetch_refspecs(const char *remote, const char *key)
 +{
 +      return git_config_set_multivar(key, NULL, NULL, 1);
 +}
 +
 +static int add_branches(struct remote *remote, const char **branches,
 +                      const char *key)
 +{
 +      const char *remotename = remote->name;
 +      int mirror = remote->mirror;
 +      struct strbuf refspec = STRBUF_INIT;
 +
 +      for (; *branches; branches++)
 +              if (add_branch(key, *branches, remotename, mirror, &refspec)) {
 +                      strbuf_release(&refspec);
 +                      return 1;
 +              }
 +
 +      strbuf_release(&refspec);
 +      return 0;
 +}
 +
 +static int set_remote_branches(const char *remotename, const char **branches,
 +                              int add_mode)
 +{
 +      struct strbuf key = STRBUF_INIT;
 +      struct remote *remote;
 +
 +      strbuf_addf(&key, "remote.%s.fetch", remotename);
 +
 +      if (!remote_is_configured(remotename))
 +              die("No such remote '%s'", remotename);
 +      remote = remote_get(remotename);
 +
 +      if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
 +              strbuf_release(&key);
 +              return 1;
 +      }
 +      if (add_branches(remote, branches, key.buf)) {
 +              strbuf_release(&key);
 +              return 1;
 +      }
 +
 +      strbuf_release(&key);
 +      return 0;
 +}
 +
 +static int set_branches(int argc, const char **argv)
 +{
 +      int add_mode = 0;
 +      struct option options[] = {
 +              OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
 +              OPT_END()
 +      };
 +
 +      argc = parse_options(argc, argv, NULL, options,
 +                           builtin_remote_setbranches_usage, 0);
 +      if (argc == 0) {
 +              error("no remote specified");
 +              usage_with_options(builtin_remote_seturl_usage, options);
 +      }
 +      argv[argc] = NULL;
 +
 +      return set_remote_branches(argv[0], argv + 1, add_mode);
 +}
 +
  static int set_url(int argc, const char **argv)
  {
        int i, push_mode = 0, add_mode = 0, delete_mode = 0;
@@@ -1460,10 -1379,10 +1460,10 @@@ static int get_one_entry(struct remote 
  
        if (remote->url_nr > 0) {
                strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
-               string_list_append(remote->name, list)->util =
+               string_list_append(list, remote->name)->util =
                                strbuf_detach(&url_buf, NULL);
        } else
-               string_list_append(remote->name, list)->util = NULL;
+               string_list_append(list, remote->name)->util = NULL;
        if (remote->pushurl_nr) {
                url = remote->pushurl;
                url_nr = remote->pushurl_nr;
        for (i = 0; i < url_nr; i++)
        {
                strbuf_addf(&url_buf, "%s (push)", url[i]);
-               string_list_append(remote->name, list)->util =
+               string_list_append(list, remote->name)->util =
                                strbuf_detach(&url_buf, NULL);
        }
  
@@@ -1530,8 -1449,6 +1530,8 @@@ int cmd_remote(int argc, const char **a
                result = rm(argc, argv);
        else if (!strcmp(argv[0], "set-head"))
                result = set_head(argc, argv);
 +      else if (!strcmp(argv[0], "set-branches"))
 +              result = set_branches(argc, argv);
        else if (!strcmp(argv[0], "set-url"))
                result = set_url(argc, argv);
        else if (!strcmp(argv[0], "show"))
diff --combined builtin/rerere.c
index 0048f9ef7fee24e5e058ef226f3b0fc93703fcf1,73610b6bc29285a4a4481458c86817ea2ab3ccf6..980d5421eee881ccb84c20dfb7aaae7dbb6a06ab
@@@ -59,7 -59,7 +59,7 @@@ static void garbage_collect(struct stri
                cutoff = (has_rerere_resolution(e->d_name)
                          ? cutoff_resolve : cutoff_noresolve);
                if (then < now - cutoff * 86400)
-                       string_list_append(e->d_name, &to_remove);
+                       string_list_append(&to_remove, e->d_name);
        }
        for (i = 0; i < to_remove.nr; i++)
                unlink_rr_item(to_remove.items[i].string);
@@@ -89,7 -89,7 +89,7 @@@ static int diff_two(const char *file1, 
        printf("--- a/%s\n+++ b/%s\n", label1, label2);
        fflush(stdout);
        memset(&xpp, 0, sizeof(xpp));
 -      xpp.flags = XDF_NEED_MINIMAL;
 +      xpp.flags = 0;
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
        ecb.outf = outf;
diff --combined http-backend.c
index 44ce6bb32b0e98681f7c523537082065e8cb5fc1,8ec15f99c05cbdcc432cce95bdf1b6a45347368d..14c90c2e84afd9997e1a6453f0065b3f59b32e57
@@@ -6,7 -6,6 +6,7 @@@
  #include "exec_cmd.h"
  #include "run-command.h"
  #include "string-list.h"
 +#include "url.h"
  
  static const char content_type[] = "Content-Type";
  static const char content_length[] = "Content-Length";
@@@ -26,6 -25,60 +26,6 @@@ static struct rpc_service rpc_service[
        { "receive-pack", "receivepack", -1 },
  };
  
 -static int decode_char(const char *q)
 -{
 -      int i;
 -      unsigned char val = 0;
 -      for (i = 0; i < 2; i++) {
 -              unsigned char c = *q++;
 -              val <<= 4;
 -              if (c >= '0' && c <= '9')
 -                      val += c - '0';
 -              else if (c >= 'a' && c <= 'f')
 -                      val += c - 'a' + 10;
 -              else if (c >= 'A' && c <= 'F')
 -                      val += c - 'A' + 10;
 -              else
 -                      return -1;
 -      }
 -      return val;
 -}
 -
 -static char *decode_parameter(const char **query, int is_name)
 -{
 -      const char *q = *query;
 -      struct strbuf out;
 -
 -      strbuf_init(&out, 16);
 -      do {
 -              unsigned char c = *q;
 -
 -              if (!c)
 -                      break;
 -              if (c == '&' || (is_name && c == '=')) {
 -                      q++;
 -                      break;
 -              }
 -
 -              if (c == '%') {
 -                      int val = decode_char(q + 1);
 -                      if (0 <= val) {
 -                              strbuf_addch(&out, val);
 -                              q += 3;
 -                              continue;
 -                      }
 -              }
 -
 -              if (c == '+')
 -                      strbuf_addch(&out, ' ');
 -              else
 -                      strbuf_addch(&out, c);
 -              q++;
 -      } while (1);
 -      *query = q;
 -      return strbuf_detach(&out, NULL);
 -}
 -
  static struct string_list *get_parameters(void)
  {
        if (!query_params) {
  
                query_params = xcalloc(1, sizeof(*query_params));
                while (query && *query) {
 -                      char *name = decode_parameter(&query, 1);
 -                      char *value = decode_parameter(&query, 0);
 +                      char *name = url_decode_parameter_name(&query);
 +                      char *value = url_decode_parameter_value(&query);
                        struct string_list_item *i;
  
-                       i = string_list_lookup(name, query_params);
+                       i = string_list_lookup(query_params, name);
                        if (!i)
-                               i = string_list_insert(name, query_params);
+                               i = string_list_insert(query_params, name);
                        else
                                free(i->util);
                        i->util = value;
  static const char *get_parameter(const char *name)
  {
        struct string_list_item *i;
-       i = string_list_lookup(name, get_parameters());
+       i = string_list_lookup(get_parameters(), name);
        return i ? i->util : NULL;
  }
  
@@@ -488,12 -541,14 +488,12 @@@ static NORETURN void die_webcgi(const c
        static int dead;
  
        if (!dead) {
 -              char buffer[1000];
                dead = 1;
 -
 -              vsnprintf(buffer, sizeof(buffer), err, params);
 -              fprintf(stderr, "fatal: %s\n", buffer);
                http_status(500, "Internal Server Error");
                hdr_nocache();
                end_headers();
 +
 +              vreportf("fatal: ", err, params);
        }
        exit(0); /* we successfully reported a failure ;-) */
  }
diff --combined notes.c
index 30d6ded78ef61cceeec9d0d0377e6622d6abd5f6,4a541e43711dea791e64d84f7e6cab851ced6161..197824439826881fc0de3275353ec1b74d86cfa3
+++ b/notes.c
@@@ -716,7 -716,7 +716,7 @@@ static int write_each_non_note_until(co
                struct write_each_note_data *d)
  {
        struct non_note *n = d->next_non_note;
 -      int cmp, ret;
 +      int cmp = 0, ret;
        while (n && (!note_path || (cmp = strcmp(n->path, note_path)) <= 0)) {
                if (note_path && cmp == 0)
                        ; /* do nothing, prefer note to non-note */
@@@ -838,7 -838,7 +838,7 @@@ static int string_list_add_one_ref(cons
  {
        struct string_list *refs = cb;
        if (!unsorted_string_list_has_string(refs, path))
-               string_list_append(path, refs);
+               string_list_append(refs, path);
        return 0;
  }
  
@@@ -851,7 -851,7 +851,7 @@@ void string_list_add_refs_by_glob(struc
                if (get_sha1(glob, sha1))
                        warning("notes ref %s is invalid", glob);
                if (!unsorted_string_list_has_string(list, glob))
-                       string_list_append(glob, list);
+                       string_list_append(list, glob);
        }
  }
  
@@@ -969,7 -969,7 +969,7 @@@ struct notes_tree **load_notes_trees(st
        trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
        cb_data.counter = 0;
        cb_data.trees = trees;
-       for_each_string_list(load_one_display_note_ref, refs, &cb_data);
+       for_each_string_list(refs, load_one_display_note_ref, &cb_data);
        trees[cb_data.counter] = NULL;
        return trees;
  }
@@@ -983,7 -983,7 +983,7 @@@ void init_display_notes(struct display_
        assert(!display_notes_trees);
  
        if (!opt || !opt->suppress_default_notes) {
-               string_list_append(default_notes_ref(), &display_notes_refs);
+               string_list_append(&display_notes_refs, default_notes_ref());
                display_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT);
                if (display_ref_env) {
                        string_list_add_refs_from_colon_sep(&display_notes_refs,
        git_config(notes_display_config, &load_config_refs);
  
        if (opt && opt->extra_notes_refs)
-               for_each_string_list(string_list_add_refs_from_list,
-                                    opt->extra_notes_refs,
+               for_each_string_list(opt->extra_notes_refs,
+                                    string_list_add_refs_from_list,
                                     &display_notes_refs);
  
        display_notes_trees = load_notes_trees(&display_notes_refs);
@@@ -1083,7 -1083,7 +1083,7 @@@ int write_notes_tree(struct notes_tree 
        return ret;
  }
  
 -void prune_notes(struct notes_tree *t)
 +void prune_notes(struct notes_tree *t, int flags)
  {
        struct note_delete_list *l = NULL;
  
        for_each_note(t, 0, prune_notes_helper, &l);
  
        while (l) {
 -              remove_note(t, l->sha1);
 +              if (flags & NOTES_PRUNE_VERBOSE)
 +                      printf("%s\n", sha1_to_hex(l->sha1));
 +              if (!(flags & NOTES_PRUNE_DRYRUN))
 +                      remove_note(t, l->sha1);
                l = l->next;
        }
  }
diff --combined remote.c
index e51cd22d6bcbd29bd47392434d37fb50d7b97588,9f0c6ef8f5797b09fc4e63fe87a1221f38d22ce5..afbba47460c0204721d61800033ed0a9f93f92bc
+++ b/remote.c
@@@ -478,7 -478,7 +478,7 @@@ static void read_config(void
        unsigned char sha1[20];
        const char *head_ref;
        int flag;
 -      if (default_remote_name) // did this already
 +      if (default_remote_name) /* did this already */
                return;
        default_remote_name = xstrdup("origin");
        current_branch = NULL;
@@@ -659,9 -659,10 +659,9 @@@ static struct refspec *parse_refspec_in
  
  int valid_fetch_refspec(const char *fetch_refspec_str)
  {
 -      const char *fetch_refspec[] = { fetch_refspec_str };
        struct refspec *refspec;
  
 -      refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
 +      refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
        free_refspecs(refspec, 1);
        return !!refspec;
  }
@@@ -762,7 -763,7 +762,7 @@@ void ref_remove_duplicates(struct ref *
                if (!ref_map->peer_ref)
                        continue;
  
-               item = string_list_lookup(ref_map->peer_ref->name, &refs);
+               item = string_list_lookup(&refs, ref_map->peer_ref->name);
                if (item) {
                        if (strcmp(((struct ref *)item->util)->name,
                                   ref_map->name))
                        continue;
                }
  
-               item = string_list_insert(ref_map->peer_ref->name, &refs);
+               item = string_list_insert(&refs, ref_map->peer_ref->name);
                item->util = ref_map;
        }
        string_list_clear(&refs, 0);
@@@ -1710,7 -1711,7 +1710,7 @@@ struct ref *get_stale_heads(struct remo
        info.ref_names = &ref_names;
        info.stale_refs_tail = &stale_refs;
        for (ref = fetch_map; ref; ref = ref->next)
-               string_list_append(ref->name, &ref_names);
+               string_list_append(&ref_names, ref->name);
        sort_string_list(&ref_names);
        for_each_ref(get_stale_heads_cb, &info);
        string_list_clear(&ref_names, 0);
diff --combined rerere.c
index 2197890982e55d7ff0832b6c59b6661874fa4223,910cfd963dcdbcb096b36b91868955a65ecdffa1..d03a69634b81e1146c20182670a400b0c29b275f
+++ b/rerere.c
@@@ -46,7 -46,7 +46,7 @@@ static void read_rr(struct string_list 
                        ; /* do nothing */
                if (i == sizeof(buf))
                        die("filename too long");
-               string_list_insert(buf, rr)->util = name;
+               string_list_insert(rr, buf)->util = name;
        }
        fclose(in);
  }
@@@ -153,7 -153,7 +153,7 @@@ static int handle_path(unsigned char *s
        git_SHA_CTX ctx;
        int hunk_no = 0;
        enum {
 -              RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,
 +              RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL
        } hunk = RR_CONTEXT;
        struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
        struct strbuf buf = STRBUF_INIT;
@@@ -354,7 -354,7 +354,7 @@@ static int find_conflict(struct string_
                    ce_same_name(e2, e3) &&
                    S_ISREG(e2->ce_mode) &&
                    S_ISREG(e3->ce_mode)) {
-                       string_list_insert((const char *)e2->name, conflict);
+                       string_list_insert(conflict, (const char *)e2->name);
                        i++; /* skip over both #2 and #3 */
                }
        }
@@@ -449,7 -449,7 +449,7 @@@ static int do_plain_rerere(struct strin
                        if (ret < 1)
                                continue;
                        hex = xstrdup(sha1_to_hex(sha1));
-                       string_list_insert(path, rr)->util = hex;
+                       string_list_insert(rr, path)->util = hex;
                        if (mkdir(git_path("rr-cache/%s", hex), 0755))
                                continue;
                        handle_file(path, NULL, rerere_path(hex, "preimage"));
                if (has_rerere_resolution(name)) {
                        if (!merge(name, path)) {
                                if (rerere_autoupdate)
-                                       string_list_insert(path, &update);
+                                       string_list_insert(&update, path);
                                fprintf(stderr,
                                        "%s '%s' using previous resolution.\n",
                                        rerere_autoupdate
@@@ -577,7 -577,7 +577,7 @@@ static int rerere_forget_one_path(cons
        fprintf(stderr, "Updated preimage for '%s'\n", path);
  
  
-       string_list_insert(path, rr)->util = hex;
+       string_list_insert(rr, path)->util = hex;
        fprintf(stderr, "Forgot resolution for %s\n", path);
        return 0;
  }
diff --combined revision.c
index 527ec34e63122807137346c1f8e477d17d675c81,28f1c6d014d5428948967224ee34f048bd2a04b5..7e82efd9324e84582732485c53c6c25a24c29997
@@@ -646,93 -646,6 +646,93 @@@ static int still_interesting(struct com
        return slop-1;
  }
  
 +/*
 + * "rev-list --ancestry-path A..B" computes commits that are ancestors
 + * of B but not ancestors of A but further limits the result to those
 + * that are descendants of A.  This takes the list of bottom commits and
 + * the result of "A..B" without --ancestry-path, and limits the latter
 + * further to the ones that can reach one of the commits in "bottom".
 + */
 +static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list)
 +{
 +      struct commit_list *p;
 +      struct commit_list *rlist = NULL;
 +      int made_progress;
 +
 +      /*
 +       * Reverse the list so that it will be likely that we would
 +       * process parents before children.
 +       */
 +      for (p = list; p; p = p->next)
 +              commit_list_insert(p->item, &rlist);
 +
 +      for (p = bottom; p; p = p->next)
 +              p->item->object.flags |= TMP_MARK;
 +
 +      /*
 +       * Mark the ones that can reach bottom commits in "list",
 +       * in a bottom-up fashion.
 +       */
 +      do {
 +              made_progress = 0;
 +              for (p = rlist; p; p = p->next) {
 +                      struct commit *c = p->item;
 +                      struct commit_list *parents;
 +                      if (c->object.flags & (TMP_MARK | UNINTERESTING))
 +                              continue;
 +                      for (parents = c->parents;
 +                           parents;
 +                           parents = parents->next) {
 +                              if (!(parents->item->object.flags & TMP_MARK))
 +                                      continue;
 +                              c->object.flags |= TMP_MARK;
 +                              made_progress = 1;
 +                              break;
 +                      }
 +              }
 +      } while (made_progress);
 +
 +      /*
 +       * NEEDSWORK: decide if we want to remove parents that are
 +       * not marked with TMP_MARK from commit->parents for commits
 +       * in the resulting list.  We may not want to do that, though.
 +       */
 +
 +      /*
 +       * The ones that are not marked with TMP_MARK are uninteresting
 +       */
 +      for (p = list; p; p = p->next) {
 +              struct commit *c = p->item;
 +              if (c->object.flags & TMP_MARK)
 +                      continue;
 +              c->object.flags |= UNINTERESTING;
 +      }
 +
 +      /* We are done with the TMP_MARK */
 +      for (p = list; p; p = p->next)
 +              p->item->object.flags &= ~TMP_MARK;
 +      for (p = bottom; p; p = p->next)
 +              p->item->object.flags &= ~TMP_MARK;
 +      free_commit_list(rlist);
 +}
 +
 +/*
 + * Before walking the history, keep the set of "negative" refs the
 + * caller has asked to exclude.
 + *
 + * This is used to compute "rev-list --ancestry-path A..B", as we need
 + * to filter the result of "A..B" further to the ones that can actually
 + * reach A.
 + */
 +static struct commit_list *collect_bottom_commits(struct commit_list *list)
 +{
 +      struct commit_list *elem, *bottom = NULL;
 +      for (elem = list; elem; elem = elem->next)
 +              if (elem->item->object.flags & UNINTERESTING)
 +                      commit_list_insert(elem->item, &bottom);
 +      return bottom;
 +}
 +
  static int limit_list(struct rev_info *revs)
  {
        int slop = SLOP;
        struct commit_list *list = revs->commits;
        struct commit_list *newlist = NULL;
        struct commit_list **p = &newlist;
 +      struct commit_list *bottom = NULL;
 +
 +      if (revs->ancestry_path) {
 +              bottom = collect_bottom_commits(list);
 +              if (!bottom)
 +                      die("--ancestry-path given but there are no bottom commits");
 +      }
  
        while (list) {
                struct commit_list *entry = list;
        if (revs->cherry_pick)
                cherry_pick_list(newlist, revs);
  
 +      if (bottom) {
 +              limit_to_ancestry(bottom, newlist);
 +              free_commit_list(bottom);
 +      }
 +
        revs->commits = newlist;
        return 0;
  }
@@@ -1162,22 -1063,18 +1162,22 @@@ static int handle_revision_opt(struct r
  
        if (!prefixcmp(arg, "--max-count=")) {
                revs->max_count = atoi(arg + 12);
 +              revs->no_walk = 0;
        } else if (!prefixcmp(arg, "--skip=")) {
                revs->skip_count = atoi(arg + 7);
        } else if ((*arg == '-') && isdigit(arg[1])) {
        /* accept -<digit>, like traditional "head" */
                revs->max_count = atoi(arg + 1);
 +              revs->no_walk = 0;
        } else if (!strcmp(arg, "-n")) {
                if (argc <= 1)
                        return error("-n requires an argument");
                revs->max_count = atoi(argv[1]);
 +              revs->no_walk = 0;
                return 2;
        } else if (!prefixcmp(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
 +              revs->no_walk = 0;
        } else if (!prefixcmp(arg, "--max-age=")) {
                revs->max_age = atoi(arg + 10);
        } else if (!prefixcmp(arg, "--since=")) {
                revs->min_age = approxidate(arg + 8);
        } else if (!strcmp(arg, "--first-parent")) {
                revs->first_parent_only = 1;
 +      } else if (!strcmp(arg, "--ancestry-path")) {
 +              revs->ancestry_path = 1;
 +              revs->simplify_history = 0;
 +              revs->limited = 1;
        } else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
                init_reflog_walk(&revs->reflog_info);
        } else if (!strcmp(arg, "--default")) {
                revs->boundary = 1;
        } else if (!strcmp(arg, "--left-right")) {
                revs->left_right = 1;
 +      } else if (!strcmp(arg, "--count")) {
 +              revs->count = 1;
        } else if (!strcmp(arg, "--cherry-pick")) {
                revs->cherry_pick = 1;
                revs->limited = 1;
                else
                        strbuf_addstr(&buf, "refs/notes/");
                strbuf_addstr(&buf, arg+13);
-               string_list_append(strbuf_detach(&buf, NULL),
-                                  revs->notes_opt.extra_notes_refs);
+               string_list_append(revs->notes_opt.extra_notes_refs,
+                                  strbuf_detach(&buf, NULL));
        } else if (!strcmp(arg, "--no-notes")) {
                revs->show_notes = 0;
                revs->show_notes_given = 1;
@@@ -1890,7 -1781,7 +1890,7 @@@ int prepare_revision_walk(struct rev_in
  enum rewrite_result {
        rewrite_one_ok,
        rewrite_one_noparents,
 -      rewrite_one_error,
 +      rewrite_one_error
  };
  
  static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
diff --combined wt-status.c
index 9d9cb9556225301c98e8cc98d51bc516881afd73,1653cfa9d66395b97ccdd64f9bd9d35ca9521484..38754ad735e9e6b090eb23990d82926cd2d82396
@@@ -9,7 -9,6 +9,7 @@@
  #include "quote.h"
  #include "run-command.h"
  #include "remote.h"
 +#include "refs.h"
  
  static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@@ -18,8 -17,6 +18,8 @@@
        GIT_COLOR_RED,    /* WT_STATUS_UNTRACKED */
        GIT_COLOR_RED,    /* WT_STATUS_NOBRANCH */
        GIT_COLOR_RED,    /* WT_STATUS_UNMERGED */
 +      GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */
 +      GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */
  };
  
  static const char *color(int slot, struct wt_status *s)
@@@ -235,7 -232,7 +235,7 @@@ static void wt_status_collect_changed_c
                struct wt_status_change_data *d;
  
                p = q->queue[i];
-               it = string_list_insert(p->one->path, &s->change);
+               it = string_list_insert(&s->change, p->one->path);
                d = it->util;
                if (!d) {
                        d = xcalloc(1, sizeof(*d));
@@@ -282,7 -279,7 +282,7 @@@ static void wt_status_collect_updated_c
                struct wt_status_change_data *d;
  
                p = q->queue[i];
-               it = string_list_insert(p->two->path, &s->change);
+               it = string_list_insert(&s->change, p->two->path);
                d = it->util;
                if (!d) {
                        d = xcalloc(1, sizeof(*d));
@@@ -349,7 -346,7 +349,7 @@@ static void wt_status_collect_changes_i
  
                if (!ce_path_match(ce, s->pathspec))
                        continue;
-               it = string_list_insert(ce->name, &s->change);
+               it = string_list_insert(&s->change, ce->name);
                d = it->util;
                if (!d) {
                        d = xcalloc(1, sizeof(*d));
@@@ -384,7 -381,7 +384,7 @@@ static void wt_status_collect_untracked
                        continue;
                if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
                        continue;
-               string_list_insert(ent->name, &s->untracked);
+               string_list_insert(&s->untracked, ent->name);
                free(ent);
        }
  
                                continue;
                        if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
                                continue;
-                       string_list_insert(ent->name, &s->ignored);
+                       string_list_insert(&s->ignored, ent->name);
                        free(ent);
                }
        }
@@@ -521,18 -518,17 +521,18 @@@ static void wt_status_print_submodule_s
        struct child_process sm_summary;
        char summary_limit[64];
        char index[PATH_MAX];
 -      const char *env[] = { index, NULL };
 -      const char *argv[] = {
 -              "submodule",
 -              "summary",
 -              uncommitted ? "--files" : "--cached",
 -              "--for-status",
 -              "--summary-limit",
 -              summary_limit,
 -              uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD"),
 -              NULL
 -      };
 +      const char *env[] = { NULL, NULL };
 +      const char *argv[8];
 +
 +      env[0] =        index;
 +      argv[0] =       "submodule";
 +      argv[1] =       "summary";
 +      argv[2] =       uncommitted ? "--files" : "--cached";
 +      argv[3] =       "--for-status";
 +      argv[4] =       "--summary-limit";
 +      argv[5] =       summary_limit;
 +      argv[6] =       uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD");
 +      argv[7] =       NULL;
  
        sprintf(summary_limit, "%d", s->submodule_summary);
        snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
@@@ -760,69 -756,9 +760,69 @@@ static void wt_shortstatus_other(int nu
        }
  }
  
 -void wt_shortstatus_print(struct wt_status *s, int null_termination)
 +static void wt_shortstatus_print_tracking(struct wt_status *s)
 +{
 +      struct branch *branch;
 +      const char *header_color = color(WT_STATUS_HEADER, s);
 +      const char *branch_color_local = color(WT_STATUS_LOCAL_BRANCH, s);
 +      const char *branch_color_remote = color(WT_STATUS_REMOTE_BRANCH, s);
 +
 +      const char *base;
 +      const char *branch_name;
 +      int num_ours, num_theirs;
 +
 +      color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
 +
 +      if (!s->branch)
 +              return;
 +      branch_name = s->branch;
 +
 +      if (!prefixcmp(branch_name, "refs/heads/"))
 +              branch_name += 11;
 +      else if (!strcmp(branch_name, "HEAD")) {
 +              branch_name = "HEAD (no branch)";
 +              branch_color_local = color(WT_STATUS_NOBRANCH, s);
 +      }
 +
 +      branch = branch_get(s->branch + 11);
 +      if (s->is_initial)
 +              color_fprintf(s->fp, header_color, "Initial commit on ");
 +      if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
 +              color_fprintf_ln(s->fp, branch_color_local,
 +                      "%s", branch_name);
 +              return;
 +      }
 +
 +      base = branch->merge[0]->dst;
 +      base = shorten_unambiguous_ref(base, 0);
 +      color_fprintf(s->fp, branch_color_local, "%s", branch_name);
 +      color_fprintf(s->fp, header_color, "...");
 +      color_fprintf(s->fp, branch_color_remote, "%s", base);
 +
 +      color_fprintf(s->fp, header_color, " [");
 +      if (!num_ours) {
 +              color_fprintf(s->fp, header_color, "behind ");
 +              color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
 +      } else if (!num_theirs) {
 +              color_fprintf(s->fp, header_color, "ahead ");
 +              color_fprintf(s->fp, branch_color_local, "%d", num_ours);
 +      } else {
 +              color_fprintf(s->fp, header_color, "ahead ");
 +              color_fprintf(s->fp, branch_color_local, "%d", num_ours);
 +              color_fprintf(s->fp, header_color, ", behind ");
 +              color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
 +      }
 +
 +      color_fprintf_ln(s->fp, header_color, "]");
 +}
 +
 +void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch)
  {
        int i;
 +
 +      if (show_branch)
 +              wt_shortstatus_print_tracking(s);
 +
        for (i = 0; i < s->change.nr; i++) {
                struct wt_status_change_data *d;
                struct string_list_item *it;
@@@ -853,5 -789,5 +853,5 @@@ void wt_porcelain_print(struct wt_statu
        s->use_color = 0;
        s->relative_paths = 0;
        s->prefix = NULL;
 -      wt_shortstatus_print(s, null_termination);
 +      wt_shortstatus_print(s, null_termination, 0);
  }