Merge branch 'jk/tighten-alloc'
authorJunio C Hamano <gitster@pobox.com>
Fri, 26 Feb 2016 21:37:16 +0000 (13:37 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 26 Feb 2016 21:37:16 +0000 (13:37 -0800)
Update various codepaths to avoid manually-counted malloc().

* jk/tighten-alloc: (22 commits)
ewah: convert to REALLOC_ARRAY, etc
convert ewah/bitmap code to use xmalloc
diff_populate_gitlink: use a strbuf
transport_anonymize_url: use xstrfmt
git-compat-util: drop mempcpy compat code
sequencer: simplify memory allocation of get_message
test-path-utils: fix normalize_path_copy output buffer size
fetch-pack: simplify add_sought_entry
fast-import: simplify allocation in start_packfile
write_untracked_extension: use FLEX_ALLOC helper
prepare_{git,shell}_cmd: use argv_array
use st_add and st_mult for allocation size computation
convert trivial cases to FLEX_ARRAY macros
use xmallocz to avoid size arithmetic
convert trivial cases to ALLOC_ARRAY
convert manual allocations to argv_array
argv-array: add detach function
add helpers for allocating flex-array structs
harden REALLOC_ARRAY and xcalloc against size_t overflow
tree-diff: catch integer overflow in combine_diff_path allocation
...

38 files changed:
1  2 
bisect.c
builtin/apply.c
builtin/blame.c
builtin/clean.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/grep.c
builtin/mktree.c
builtin/pack-objects.c
builtin/worktree.c
cache-tree.c
compat/mingw.c
config.c
daemon.c
diff.c
diff.h
dir.c
exec_cmd.c
fast-import.c
git-compat-util.h
git.c
notes.c
pack-revindex.c
ref-filter.c
refs/files-backend.c
remote-curl.c
remote.c
revision.c
run-command.c
sequencer.c
setup.c
sha1_file.c
sha1_name.c
strbuf.c
submodule.c
test-path-utils.c
transport.c
wrapper.c
diff --combined bisect.c
index 06ec54e599586f7cfe0f08c4b63354900af33d18,6d0793b9fc240f4f4e3898644e9506ecec6a1035..7996c2907b0e571578f2ce18095c83c134a503b3
+++ b/bisect.c
@@@ -440,7 -440,7 +440,7 @@@ static void read_bisect_paths(struct ar
        if (!fp)
                die_errno("Could not open file '%s'", filename);
  
 -      while (strbuf_getline(&str, fp, '\n') != EOF) {
 +      while (strbuf_getline_lf(&str, fp) != EOF) {
                strbuf_trim(&str);
                if (sq_dequote_to_argv_array(str.buf, array))
                        die("Badly quoted content in file '%s': %s",
@@@ -668,7 -668,7 +668,7 @@@ static int is_expected_rev(const struc
        if (!fp)
                return 0;
  
 -      if (strbuf_getline(&str, fp, '\n') != EOF)
 +      if (strbuf_getline_lf(&str, fp) != EOF)
                res = !strcmp(str.buf, oid_to_hex(oid));
  
        strbuf_release(&str);
@@@ -708,10 -708,10 +708,10 @@@ static struct commit *get_commit_refere
  
  static struct commit **get_bad_and_good_commits(int *rev_nr)
  {
-       int len = 1 + good_revs.nr;
-       struct commit **rev = xmalloc(len * sizeof(*rev));
+       struct commit **rev;
        int i, n = 0;
  
+       ALLOC_ARRAY(rev, 1 + good_revs.nr);
        rev[n++] = get_commit_reference(current_bad_oid->hash);
        for (i = 0; i < good_revs.nr; i++)
                rev[n++] = get_commit_reference(good_revs.sha1[i]);
@@@ -914,9 -914,9 +914,9 @@@ void read_bisect_terms(const char **rea
                                strerror(errno));
                }
        } else {
 -              strbuf_getline(&str, fp, '\n');
 +              strbuf_getline_lf(&str, fp);
                *read_bad = strbuf_detach(&str, NULL);
 -              strbuf_getline(&str, fp, '\n');
 +              strbuf_getline_lf(&str, fp);
                *read_good = strbuf_detach(&str, NULL);
        }
        strbuf_release(&str);
diff --combined builtin/apply.c
index d61ac65dab28a766a13782bdef572431e20e80ff,0db6d14cc2eeaf3a08fb08301ab02af1b22fc8f8..42c610e2ec180e789fdae4bc637dd96f533d1e10
@@@ -2632,7 -2632,7 +2632,7 @@@ static void update_image(struct image *
        insert_count = postimage->len;
  
        /* Adjust the contents */
-       result = xmalloc(img->len + insert_count - remove_count + 1);
+       result = xmalloc(st_add3(st_sub(img->len, remove_count), insert_count, 1));
        memcpy(result, img->buf, applied_at);
        memcpy(result + applied_at, postimage->buf, postimage->len);
        memcpy(result + applied_at + postimage->len,
@@@ -4464,6 -4464,16 +4464,6 @@@ static int option_parse_p(const struct 
        return 0;
  }
  
 -static int option_parse_z(const struct option *opt,
 -                        const char *arg, int unset)
 -{
 -      if (unset)
 -              line_termination = '\n';
 -      else
 -              line_termination = 0;
 -      return 0;
 -}
 -
  static int option_parse_space_change(const struct option *opt,
                          const char *arg, int unset)
  {
@@@ -4536,9 -4546,9 +4536,9 @@@ int cmd_apply(int argc, const char **ar
                         N_( "attempt three-way merge if a patch does not apply")),
                OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
                        N_("build a temporary index based on embedded index information")),
 -              { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
 -                      N_("paths are separated with NUL character"),
 -                      PARSE_OPT_NOARG, option_parse_z },
 +              /* Think twice before adding "--nul" synonym to this */
 +              OPT_SET_INT('z', NULL, &line_termination,
 +                      N_("paths are separated with NUL character"), '\0'),
                OPT_INTEGER('C', NULL, &p_context,
                                N_("ensure at least <n> lines of context match")),
                { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
diff --combined builtin/blame.c
index 55bf5fae9d5d6ea6c4e058e50d9353e77e09b34b,e175d86e56c670f7463a1f6b00e75239c7ba1dfe..e982fb81379f57152e34eeda706a57fa1ea4c143
@@@ -28,7 -28,6 +28,7 @@@
  #include "line-range.h"
  #include "line-log.h"
  #include "dir.h"
 +#include "progress.h"
  
  static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
  
@@@ -51,7 -50,6 +51,7 @@@ static int incremental
  static int xdl_opts;
  static int abbrev = -1;
  static int no_whole_file_rename;
 +static int show_progress;
  
  static struct date_mode blame_date_mode = { DATE_ISO8601 };
  static size_t blame_date_width;
@@@ -129,11 -127,6 +129,11 @@@ struct origin 
        char path[FLEX_ARRAY];
  };
  
 +struct progress_info {
 +      struct progress *progress;
 +      int blamed_lines;
 +};
 +
  static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
                      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
  {
@@@ -466,13 -459,11 +466,11 @@@ static void queue_blames(struct scorebo
  static struct origin *make_origin(struct commit *commit, const char *path)
  {
        struct origin *o;
-       size_t pathlen = strlen(path) + 1;
-       o = xcalloc(1, sizeof(*o) + pathlen);
+       FLEX_ALLOC_STR(o, path, path);
        o->commit = commit;
        o->refcnt = 1;
        o->next = commit->util;
        commit->util = o;
-       memcpy(o->path, path, pathlen); /* includes NUL */
        return o;
  }
  
@@@ -1753,8 -1744,7 +1751,8 @@@ static int emit_one_suspect_detail(stru
   * The blame_entry is found to be guilty for the range.
   * Show it in incremental output.
   */
 -static void found_guilty_entry(struct blame_entry *ent)
 +static void found_guilty_entry(struct blame_entry *ent,
 +                         struct progress_info *pi)
  {
        if (incremental) {
                struct origin *suspect = ent->suspect;
                write_filename_info(suspect->path);
                maybe_flush_or_die(stdout, "stdout");
        }
 +      pi->blamed_lines += ent->num_lines;
 +      display_progress(pi->progress, pi->blamed_lines);
  }
  
  /*
@@@ -1778,11 -1766,6 +1776,11 @@@ static void assign_blame(struct scorebo
  {
        struct rev_info *revs = sb->revs;
        struct commit *commit = prio_queue_get(&sb->commits);
 +      struct progress_info pi = { NULL, 0 };
 +
 +      if (show_progress)
 +              pi.progress = start_progress_delay(_("Blaming lines"),
 +                                                 sb->num_lines, 50, 1);
  
        while (commit) {
                struct blame_entry *ent;
                        suspect->guilty = 1;
                        for (;;) {
                                struct blame_entry *next = ent->next;
 -                              found_guilty_entry(ent);
 +                              found_guilty_entry(ent, &pi);
                                if (next) {
                                        ent = next;
                                        continue;
                if (DEBUG) /* sanity */
                        sanity_check_refcnt(sb);
        }
 +
 +      stop_progress(&pi.progress);
  }
  
  static const char *format_time(unsigned long time, const char *tz_str,
@@@ -2059,7 -2040,8 +2057,8 @@@ static int prepare_lines(struct scorebo
        for (p = buf; p < end; p = get_next_line(p, end))
                num++;
  
-       sb->lineno = lineno = xmalloc(sizeof(*sb->lineno) * (num + 1));
+       ALLOC_ARRAY(sb->lineno, num + 1);
+       lineno = sb->lineno;
  
        for (p = buf; p < end; p = get_next_line(p, end))
                *lineno++ = p - buf;
@@@ -2409,6 -2391,11 +2408,6 @@@ static struct commit *fake_working_tree
        ce->ce_mode = create_ce_mode(mode);
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
  
 -      /*
 -       * We are not going to write this out, so this does not matter
 -       * right now, but someday we might optimize diff-index --cached
 -       * with cache-tree information.
 -       */
        cache_tree_invalidate_path(&the_index, path);
  
        return commit;
@@@ -2532,7 -2519,6 +2531,7 @@@ int cmd_blame(int argc, const char **ar
                OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
                OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
                OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")),
 +              OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")),
                OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE),
                OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME),
                OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER),
  
        save_commit_buffer = 0;
        dashdash_pos = 0;
 +      show_progress = -1;
  
        parse_options_start(&ctx, argc, argv, prefix, options,
                            PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
@@@ -2593,13 -2578,6 +2592,13 @@@ parse_done
        DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
        argc = parse_options_end(&ctx);
  
 +      if (incremental || (output_option & OUTPUT_PORCELAIN)) {
 +              if (show_progress > 0)
 +                      die("--progress can't be used with --incremental or porcelain formats");
 +              show_progress = 0;
 +      } else if (show_progress < 0)
 +              show_progress = isatty(2);
 +
        if (0 < abbrev)
                /* one more abbrev length is needed for the boundary commit */
                abbrev++;
  
        read_mailmap(&mailmap, NULL);
  
 +      assign_blame(&sb, opt);
 +
        if (!incremental)
                setup_pager();
  
 -      assign_blame(&sb, opt);
 -
        free(final_commit_name);
  
        if (incremental)
diff --combined builtin/clean.c
index 7b08237480fde101640ce361b3b95106e7ffa930,fb1824ce951b62e51fb7205424e1496c50c700c6..0371010afbad54283ceca9883f2a9fbe6da4686b
@@@ -543,7 -543,7 +543,7 @@@ static int *list_and_choose(struct menu
        int eof = 0;
        int i;
  
-       chosen = xmalloc(sizeof(int) * stuff->nr);
+       ALLOC_ARRAY(chosen, stuff->nr);
        /* set chosen as uninitialized */
        for (i = 0; i < stuff->nr; i++)
                chosen[i] = -1;
                               clean_get_color(CLEAN_COLOR_RESET));
                }
  
 -              if (strbuf_getline(&choice, stdin, '\n') != EOF) {
 +              if (strbuf_getline_lf(&choice, stdin) != EOF) {
                        strbuf_trim(&choice);
                } else {
                        eof = 1;
                                nr += chosen[i];
                }
  
-               result = xcalloc(nr + 1, sizeof(int));
+               result = xcalloc(st_add(nr, 1), sizeof(int));
                for (i = 0; i < stuff->nr && j < nr; i++) {
                        if (chosen[i])
                                result[j++] = i;
@@@ -652,7 -652,7 +652,7 @@@ static int filter_by_patterns_cmd(void
                clean_print_color(CLEAN_COLOR_PROMPT);
                printf(_("Input ignore patterns>> "));
                clean_print_color(CLEAN_COLOR_RESET);
 -              if (strbuf_getline(&confirm, stdin, '\n') != EOF)
 +              if (strbuf_getline_lf(&confirm, stdin) != EOF)
                        strbuf_trim(&confirm);
                else
                        putchar('\n');
@@@ -750,7 -750,7 +750,7 @@@ static int ask_each_cmd(void
                        qname = quote_path_relative(item->string, NULL, &buf);
                        /* TRANSLATORS: Make sure to keep [y/N] as is */
                        printf(_("Remove %s [y/N]? "), qname);
 -                      if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
 +                      if (strbuf_getline_lf(&confirm, stdin) != EOF) {
                                strbuf_trim(&confirm);
                        } else {
                                putchar('\n');
diff --combined builtin/fetch-pack.c
index 9b2a514e1d787784c2fac8d844d58013b6091aa6,7d5914f9212c194e7f45cffb81540ee297fcb54b..79a611fda1f8b344ce619dd9318695a69eebb695
@@@ -10,33 -10,24 +10,24 @@@ static const char fetch_pack_usage[] 
  "[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
  "[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]";
  
- static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
-                                const char *name, int namelen)
+ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
+                            const char *name)
  {
-       struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
+       struct ref *ref;
        struct object_id oid;
-       const int chunksz = GIT_SHA1_HEXSZ + 1;
  
-       if (namelen > chunksz && name[chunksz - 1] == ' ' &&
-               !get_oid_hex(name, &oid)) {
-               oidcpy(&ref->old_oid, &oid);
-               name += chunksz;
-               namelen -= chunksz;
-       }
+       if (!get_oid_hex(name, &oid) && name[GIT_SHA1_HEXSZ] == ' ')
+               name += GIT_SHA1_HEXSZ + 1;
+       else
+               oidclr(&oid);
  
-       memcpy(ref->name, name, namelen);
-       ref->name[namelen] = '\0';
+       ref = alloc_ref(name);
+       oidcpy(&ref->old_oid, &oid);
        (*nr)++;
        ALLOC_GROW(*sought, *nr, *alloc);
        (*sought)[*nr - 1] = ref;
  }
  
- static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
-                            const char *string)
- {
-       add_sought_entry_mem(sought, nr, alloc, string, strlen(string));
- }
  int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
  {
        int i, ret;
                else {
                        /* read from stdin one ref per line, until EOF */
                        struct strbuf line = STRBUF_INIT;
 -                      while (strbuf_getline(&line, stdin, '\n') != EOF)
 +                      while (strbuf_getline_lf(&line, stdin) != EOF)
                                add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf);
                        strbuf_release(&line);
                }
diff --combined builtin/fetch.c
index 48c45ea5e3b77d5e7af339128971f294e6ea9a20,683f08ec91295e2fe56c372497685f40121f3b32..e4639d8eb1d5fda586520f10271c05a0897f2ea5
@@@ -37,8 -37,6 +37,8 @@@ static int prune = -1; /* unspecified *
  static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
  static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
  static int tags = TAGS_DEFAULT, unshallow, update_shallow;
 +static int max_children = 1;
 +static enum transport_family family;
  static const char *depth;
  static const char *upload_pack;
  static struct strbuf default_rla = STRBUF_INIT;
@@@ -101,8 -99,6 +101,8 @@@ static struct option builtin_fetch_opti
                    N_("fetch all tags and associated objects"), TAGS_SET),
        OPT_SET_INT('n', NULL, &tags,
                    N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
 +      OPT_INTEGER('j', "jobs", &max_children,
 +                  N_("number of submodules fetched in parallel")),
        OPT_BOOL('p', "prune", &prune,
                 N_("prune remote-tracking branches no longer on remote")),
        { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"),
                 N_("accept refs that update .git/shallow")),
        { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
          N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
 +      OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
 +                      TRANSPORT_FAMILY_IPV4),
 +      OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
 +                      TRANSPORT_FAMILY_IPV6),
        OPT_END()
  };
  
@@@ -869,7 -861,6 +869,7 @@@ static struct transport *prepare_transp
        struct transport *transport;
        transport = transport_get(remote, NULL);
        transport_set_verbosity(transport, verbosity, progress);
 +      transport->family = family;
        if (upload_pack)
                set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
        if (keep)
@@@ -1022,9 -1013,10 +1022,9 @@@ static int add_remote_or_group(const ch
  
        git_config(get_remote_group, &g);
        if (list->nr == prev_nr) {
 -              struct remote *remote;
 -              if (!remote_is_configured(name))
 +              struct remote *remote = remote_get(name);
 +              if (!remote_is_configured(remote))
                        return 0;
 -              remote = remote_get(name);
                string_list_append(list, remote->name);
        }
        return 1;
@@@ -1115,7 -1107,7 +1115,7 @@@ static int fetch_one(struct remote *rem
        if (argc > 0) {
                int j = 0;
                int i;
-               refs = xcalloc(argc + 1, sizeof(const char *));
+               refs = xcalloc(st_add(argc, 1), sizeof(const char *));
                for (i = 0; i < argc; i++) {
                        if (!strcmp(argv[i], "tag")) {
                                i++;
@@@ -1221,8 -1213,7 +1221,8 @@@ int cmd_fetch(int argc, const char **ar
                result = fetch_populated_submodules(&options,
                                                    submodule_prefix,
                                                    recurse_submodules,
 -                                                  verbosity < 0);
 +                                                  verbosity < 0,
 +                                                  max_children);
                argv_array_clear(&options);
        }
  
diff --combined builtin/grep.c
index 8c516a95438e5aa997049da24160764ff4ca985c,95ddf96d1e416f59dff08a9dde71b04cdb2b6ccd..aa7435f380e95d87982cd1ae71e91bc53e77af50
@@@ -24,11 -24,11 +24,11 @@@ static char const * const grep_usage[] 
        NULL
  };
  
 -static int use_threads = 1;
 +#define GREP_NUM_THREADS_DEFAULT 8
 +static int num_threads;
  
  #ifndef NO_PTHREADS
 -#define THREADS 8
 -static pthread_t threads[THREADS];
 +static pthread_t *threads;
  
  /* We use one producer thread and THREADS consumer
   * threads. The producer adds struct work_items to 'todo' and the
@@@ -63,13 -63,13 +63,13 @@@ static pthread_mutex_t grep_mutex
  
  static inline void grep_lock(void)
  {
 -      if (use_threads)
 +      if (num_threads)
                pthread_mutex_lock(&grep_mutex);
  }
  
  static inline void grep_unlock(void)
  {
 -      if (use_threads)
 +      if (num_threads)
                pthread_mutex_unlock(&grep_mutex);
  }
  
@@@ -206,8 -206,7 +206,8 @@@ static void start_threads(struct grep_o
                strbuf_init(&todo[i].out, 0);
        }
  
 -      for (i = 0; i < ARRAY_SIZE(threads); i++) {
 +      threads = xcalloc(num_threads, sizeof(*threads));
 +      for (i = 0; i < num_threads; i++) {
                int err;
                struct grep_opt *o = grep_opt_dup(opt);
                o->output = strbuf_out;
@@@ -239,14 -238,12 +239,14 @@@ static int wait_all(void
        pthread_cond_broadcast(&cond_add);
        grep_unlock();
  
 -      for (i = 0; i < ARRAY_SIZE(threads); i++) {
 +      for (i = 0; i < num_threads; i++) {
                void *h;
                pthread_join(threads[i], &h);
                hit |= (int) (intptr_t) h;
        }
  
 +      free(threads);
 +
        pthread_mutex_destroy(&grep_mutex);
        pthread_mutex_destroy(&grep_read_mutex);
        pthread_mutex_destroy(&grep_attr_mutex);
@@@ -270,14 -267,6 +270,14 @@@ static int grep_cmd_config(const char *
        int st = grep_config(var, value, cb);
        if (git_color_default_config(var, value, cb) < 0)
                st = -1;
 +
 +      if (!strcmp(var, "grep.threads")) {
 +              num_threads = git_config_int(var, value);
 +              if (num_threads < 0)
 +                      die(_("invalid number of threads specified (%d) for %s"),
 +                          num_threads, var);
 +      }
 +
        return st;
  }
  
@@@ -305,7 -294,7 +305,7 @@@ static int grep_sha1(struct grep_opt *o
        }
  
  #ifndef NO_PTHREADS
 -      if (use_threads) {
 +      if (num_threads) {
                add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
                strbuf_release(&pathbuf);
                return 0;
@@@ -334,7 -323,7 +334,7 @@@ static int grep_file(struct grep_opt *o
                strbuf_addstr(&buf, filename);
  
  #ifndef NO_PTHREADS
 -      if (use_threads) {
 +      if (num_threads) {
                add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename);
                strbuf_release(&buf);
                return 0;
@@@ -365,17 -354,17 +365,17 @@@ static void append_path(struct grep_op
  static void run_pager(struct grep_opt *opt, const char *prefix)
  {
        struct string_list *path_list = opt->output_priv;
-       const char **argv = xmalloc(sizeof(const char *) * (path_list->nr + 1));
+       struct child_process child = CHILD_PROCESS_INIT;
        int i, status;
  
        for (i = 0; i < path_list->nr; i++)
-               argv[i] = path_list->items[i].string;
-       argv[path_list->nr] = NULL;
+               argv_array_push(&child.args, path_list->items[i].string);
+       child.dir = prefix;
+       child.use_shell = 1;
  
-       status = run_command_v_opt_cd_env(argv, RUN_USING_SHELL, prefix, NULL);
+       status = run_command(&child);
        if (status)
                exit(status);
-       free(argv);
  }
  
  static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)
  
        for (nr = 0; nr < active_nr; nr++) {
                const struct cache_entry *ce = active_cache[nr];
 -              if (!S_ISREG(ce->ce_mode))
 +              if (!S_ISREG(ce->ce_mode) || ce_intent_to_add(ce))
                        continue;
                if (!ce_path_match(ce, pathspec, NULL))
                        continue;
@@@ -573,7 -562,7 +573,7 @@@ static int file_callback(const struct o
        patterns = from_stdin ? stdin : fopen(arg, "r");
        if (!patterns)
                die_errno(_("cannot open '%s'"), arg);
 -      while (strbuf_getline(&sb, patterns, '\n') == 0) {
 +      while (strbuf_getline(&sb, patterns) == 0) {
                /* ignore empty line like grep does */
                if (sb.len == 0)
                        continue;
@@@ -708,8 -697,6 +708,8 @@@ int cmd_grep(int argc, const char **arg
                        N_("show <n> context lines before matches")),
                OPT_INTEGER('A', "after-context", &opt.post_context,
                        N_("show <n> context lines after matches")),
 +              OPT_INTEGER(0, "threads", &num_threads,
 +                      N_("use <n> worker threads")),
                OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"),
                        context_callback),
                OPT_BOOL('p', "show-function", &opt.funcname,
                             PARSE_OPT_STOP_AT_NON_OPTION);
        grep_commit_pattern_type(pattern_type_arg, &opt);
  
 -      if (use_index && !startup_info->have_repository)
 -              /* die the same way as if we did it at the beginning */
 -              setup_git_directory();
 +      if (use_index && !startup_info->have_repository) {
 +              int fallback = 0;
 +              git_config_get_bool("grep.fallbacktonoindex", &fallback);
 +              if (fallback)
 +                      use_index = 0;
 +              else
 +                      /* die the same way as if we did it at the beginning */
 +                      setup_git_directory();
 +      }
  
        /*
         * skip a -- separator; we know it cannot be
                opt.output_priv = &path_list;
                opt.output = append_path;
                string_list_append(&path_list, show_in_pager);
 -              use_threads = 0;
        }
  
        if (!opt.pattern_list)
        }
  
  #ifndef NO_PTHREADS
 -      if (list.nr || cached || online_cpus() == 1)
 -              use_threads = 0;
 +      if (list.nr || cached || show_in_pager)
 +              num_threads = 0;
 +      else if (num_threads == 0)
 +              num_threads = GREP_NUM_THREADS_DEFAULT;
 +      else if (num_threads < 0)
 +              die(_("invalid number of threads specified (%d)"), num_threads);
  #else
 -      use_threads = 0;
 +      num_threads = 0;
  #endif
  
  #ifndef NO_PTHREADS
 -      if (use_threads) {
 +      if (num_threads) {
                if (!(opt.name_only || opt.unmatch_name_only || opt.count)
                    && (opt.pre_context || opt.post_context ||
                        opt.file_break || opt.funcbody))
                hit = grep_objects(&opt, &pathspec, &list);
        }
  
 -      if (use_threads)
 +      if (num_threads)
                hit |= wait_all();
        if (hit && show_in_pager)
                run_pager(&opt, prefix);
diff --combined builtin/mktree.c
index a237caacfdfc5905067bb6e3f9ae517b5f83ed17,b0aab653539c4000044f88bfeb18e55dc55cbbd1..4282b62c595edd987a87eb23ba679079211e0835
@@@ -19,16 -19,17 +19,17 @@@ static int alloc, used
  static void append_to_tree(unsigned mode, unsigned char *sha1, char *path)
  {
        struct treeent *ent;
-       int len = strlen(path);
+       size_t len = strlen(path);
        if (strchr(path, '/'))
                die("path %s contains slash", path);
  
-       ALLOC_GROW(entries, used + 1, alloc);
-       ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1);
+       FLEX_ALLOC_MEM(ent, name, path, len);
        ent->mode = mode;
        ent->len = len;
        hashcpy(ent->sha1, sha1);
-       memcpy(ent->name, path, len+1);
+       ALLOC_GROW(entries, used + 1, alloc);
+       entries[used++] = ent;
  }
  
  static int ent_compare(const void *a_, const void *b_)
@@@ -65,7 -66,7 +66,7 @@@ static const char *mktree_usage[] = 
        NULL
  };
  
 -static void mktree_line(char *buf, size_t len, int line_termination, int allow_missing)
 +static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_missing)
  {
        char *ptr, *ntr;
        unsigned mode;
@@@ -97,7 -98,7 +98,7 @@@
        *ntr++ = 0; /* now at the beginning of SHA1 */
  
        path = ntr + 41;  /* at the beginning of name */
 -      if (line_termination && path[0] == '"') {
 +      if (!nul_term_line && path[0] == '"') {
                struct strbuf p_uq = STRBUF_INIT;
                if (unquote_c_style(&p_uq, path, NULL))
                        die("invalid quoting");
@@@ -141,25 -142,23 +142,25 @@@ int cmd_mktree(int ac, const char **av
  {
        struct strbuf sb = STRBUF_INIT;
        unsigned char sha1[20];
 -      int line_termination = '\n';
 +      int nul_term_line = 0;
        int allow_missing = 0;
        int is_batch_mode = 0;
        int got_eof = 0;
 +      strbuf_getline_fn getline_fn;
  
        const struct option option[] = {
 -              OPT_SET_INT('z', NULL, &line_termination, N_("input is NUL terminated"), '\0'),
 +              OPT_BOOL('z', NULL, &nul_term_line, N_("input is NUL terminated")),
                OPT_SET_INT( 0 , "missing", &allow_missing, N_("allow missing objects"), 1),
                OPT_SET_INT( 0 , "batch", &is_batch_mode, N_("allow creation of more than one tree"), 1),
                OPT_END()
        };
  
        ac = parse_options(ac, av, prefix, option, mktree_usage, 0);
 +      getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
  
        while (!got_eof) {
                while (1) {
 -                      if (strbuf_getline(&sb, stdin, line_termination) == EOF) {
 +                      if (getline_fn(&sb, stdin) == EOF) {
                                got_eof = 1;
                                break;
                        }
                                        break;
                                die("input format error: (blank line only valid in batch mode)");
                        }
 -                      mktree_line(sb.buf, sb.len, line_termination, allow_missing);
 +                      mktree_line(sb.buf, sb.len, nul_term_line, allow_missing);
                }
                if (is_batch_mode && got_eof && used < 1) {
                        /*
diff --combined builtin/pack-objects.c
index a6609f19ffa2c5320eee3e2994eda1d13e922729,b4f1fa6d3396f7b8845216bc99d1aaa243d3e518..a27de5b323f3fc7852a48fdc6de99414e8005c10
@@@ -624,7 -624,7 +624,7 @@@ static struct object_entry **compute_wr
  {
        unsigned int i, wo_end, last_untagged;
  
-       struct object_entry **wo = xmalloc(to_pack.nr_objects * sizeof(*wo));
+       struct object_entry **wo;
        struct object_entry *objects = to_pack.objects;
  
        for (i = 0; i < to_pack.nr_objects; i++) {
         * Give the objects in the original recency order until
         * we see a tagged tip.
         */
+       ALLOC_ARRAY(wo, to_pack.nr_objects);
        for (i = wo_end = 0; i < to_pack.nr_objects; i++) {
                if (objects[i].tagged)
                        break;
@@@ -769,7 -770,7 +770,7 @@@ static void write_pack_file(void
  
        if (progress > pack_to_stdout)
                progress_state = start_progress(_("Writing objects"), nr_result);
-       written_list = xmalloc(to_pack.nr_objects * sizeof(*written_list));
+       ALLOC_ARRAY(written_list, to_pack.nr_objects);
        write_order = compute_write_order();
  
        do {
@@@ -2129,7 -2130,7 +2130,7 @@@ static void prepare_pack(int window, in
        if (!to_pack.nr_objects || !window || !depth)
                return;
  
-       delta_list = xmalloc(to_pack.nr_objects * sizeof(*delta_list));
+       ALLOC_ARRAY(delta_list, to_pack.nr_objects);
        nr_deltas = n = 0;
  
        for (i = 0; i < to_pack.nr_objects; i++) {
@@@ -2284,11 -2285,21 +2285,11 @@@ static void show_commit(struct commit *
                index_commit_for_bitmap(commit);
  }
  
 -static void show_object(struct object *obj,
 -                      const struct name_path *path, const char *last,
 -                      void *data)
 +static void show_object(struct object *obj, const char *name, void *data)
  {
 -      char *name = path_name(path, last);
 -
        add_preferred_base_object(name);
        add_object_entry(obj->oid.hash, obj->type, name, 0);
        obj->flags |= OBJECT_ADDED;
 -
 -      /*
 -       * We will have generated the hash from the name,
 -       * but not saved a pointer to it - we can free it
 -       */
 -      free((char *)name);
  }
  
  static void show_edge(struct commit *commit)
@@@ -2470,7 -2481,8 +2471,7 @@@ static int get_object_list_from_bitmap(
  }
  
  static void record_recent_object(struct object *obj,
 -                               const struct name_path *path,
 -                               const char *last,
 +                               const char *name,
                                 void *data)
  {
        sha1_array_append(&recent_objects, obj->oid.hash);
diff --combined builtin/worktree.c
index 20cf67a549b0d293f42e7f7c21f8822d06aa9c7c,0a45710be8561e51d1b2e29586a63b0aa8518e56..38b56096bd3b914d1128e0bebb5c964a3cdd1b74
@@@ -52,7 -52,7 +52,7 @@@ static int prune_worktree(const char *i
                return 1;
        }
        len = st.st_size;
-       path = xmalloc(len + 1);
+       path = xmallocz(len);
        read_in_full(fd, path, len);
        close(fd);
        while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
@@@ -201,7 -201,9 +201,7 @@@ static int add_worktree(const char *pat
                die(_("'%s' already exists"), path);
  
        /* is 'refname' a branch or commit? */
 -      if (opts->force_new_branch) /* definitely a branch */
 -              ;
 -      else if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
 +      if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
                 ref_exists(symref.buf)) { /* it's a branch */
                if (!opts->force)
                        die_if_checked_out(symref.buf);
@@@ -334,18 -336,9 +334,18 @@@ static int add(int ac, const char **av
        branch = ac < 2 ? "HEAD" : av[1];
  
        opts.force_new_branch = !!new_branch_force;
 -      if (opts.force_new_branch)
 +      if (opts.force_new_branch) {
 +              struct strbuf symref = STRBUF_INIT;
 +
                opts.new_branch = new_branch_force;
  
 +              if (!opts.force &&
 +                  !strbuf_check_branch_ref(&symref, opts.new_branch) &&
 +                  ref_exists(symref.buf))
 +                      die_if_checked_out(symref.buf);
 +              strbuf_release(&symref);
 +      }
 +
        if (ac < 2 && !opts.new_branch && !opts.detach) {
                int n;
                const char *s = worktree_basename(path, &n);
diff --combined cache-tree.c
index 20ee7b52df0c33d4473a198270705dffead01cdc,1fbe79a0032d898404580246800c3a7f6250ae6d..3ebf9c3aa44eb2ab25f573192b6449f63311ffd7
@@@ -79,11 -79,9 +79,9 @@@ static struct cache_tree_sub *find_subt
        ALLOC_GROW(it->down, it->subtree_nr + 1, it->subtree_alloc);
        it->subtree_nr++;
  
-       down = xmalloc(sizeof(*down) + pathlen + 1);
+       FLEX_ALLOC_MEM(down, name, path, pathlen);
        down->cache_tree = NULL;
        down->namelen = pathlen;
-       memcpy(down->name, path, pathlen);
-       down->name[pathlen] = 0;
  
        if (pos < it->subtree_nr)
                memmove(it->down + pos + 1,
@@@ -377,7 -375,7 +375,7 @@@ static int update_one(struct cache_tre
                 * they are not part of generated trees. Invalidate up
                 * to root to force cache-tree users to read elsewhere.
                 */
 -              if (ce->ce_flags & CE_INTENT_TO_ADD) {
 +              if (ce_intent_to_add(ce)) {
                        to_invalidate = 1;
                        continue;
                }
diff --combined compat/mingw.c
index fbe69b874b06259e1d699572ace7dd8d2c4667c6,ae16d089ad234cd4d3486eb3406d985bb89d8d17..cfedcf9656549050c1d97ab69000d4279d1b0b5e
@@@ -6,8 -6,6 +6,8 @@@
  #include "../run-command.h"
  #include "../cache.h"
  
 +#define HCAST(type, handle) ((type)(intptr_t)handle)
 +
  static const int delay[] = { 0, 1, 10, 20, 40 };
  
  int err_win_to_posix(DWORD winerr)
@@@ -454,39 -452,6 +454,39 @@@ static inline time_t filetime_to_time_t
        return (time_t)(filetime_to_hnsec(ft) / 10000000);
  }
  
 +/**
 + * Verifies that safe_create_leading_directories() would succeed.
 + */
 +static int has_valid_directory_prefix(wchar_t *wfilename)
 +{
 +      int n = wcslen(wfilename);
 +
 +      while (n > 0) {
 +              wchar_t c = wfilename[--n];
 +              DWORD attributes;
 +
 +              if (!is_dir_sep(c))
 +                      continue;
 +
 +              wfilename[n] = L'\0';
 +              attributes = GetFileAttributesW(wfilename);
 +              wfilename[n] = c;
 +              if (attributes == FILE_ATTRIBUTE_DIRECTORY ||
 +                              attributes == FILE_ATTRIBUTE_DEVICE)
 +                      return 1;
 +              if (attributes == INVALID_FILE_ATTRIBUTES)
 +                      switch (GetLastError()) {
 +                      case ERROR_PATH_NOT_FOUND:
 +                              continue;
 +                      case ERROR_FILE_NOT_FOUND:
 +                              /* This implies parent directory exists. */
 +                              return 1;
 +                      }
 +              return 0;
 +      }
 +      return 1;
 +}
 +
  /* We keep the do_lstat code in a separate function to avoid recursion.
   * When a path ends with a slash, the stat will fail with ENOENT. In
   * this case, we strip the trailing slashes and stat again.
@@@ -547,12 -512,6 +547,12 @@@ static int do_lstat(int follow, const c
        case ERROR_NOT_ENOUGH_MEMORY:
                errno = ENOMEM;
                break;
 +      case ERROR_PATH_NOT_FOUND:
 +              if (!has_valid_directory_prefix(wfilename)) {
 +                      errno = ENOTDIR;
 +                      break;
 +              }
 +              /* fallthru */
        default:
                errno = ENOENT;
                break;
@@@ -732,13 -691,13 +732,13 @@@ int pipe(int filedes[2]
                errno = err_win_to_posix(GetLastError());
                return -1;
        }
 -      filedes[0] = _open_osfhandle((int)h[0], O_NOINHERIT);
 +      filedes[0] = _open_osfhandle(HCAST(int, h[0]), O_NOINHERIT);
        if (filedes[0] < 0) {
                CloseHandle(h[0]);
                CloseHandle(h[1]);
                return -1;
        }
 -      filedes[1] = _open_osfhandle((int)h[1], O_NOINHERIT);
 +      filedes[1] = _open_osfhandle(HCAST(int, h[1]), O_NOINHERIT);
        if (filedes[1] < 0) {
                close(filedes[0]);
                CloseHandle(h[1]);
@@@ -810,7 -769,7 +810,7 @@@ static const char *quote_arg(const cha
                return arg;
  
        /* insert \ where necessary */
-       d = q = xmalloc(len+n+3);
+       d = q = xmalloc(st_add3(len, n, 3));
        *d++ = '"';
        while (*arg) {
                if (*arg == '"')
@@@ -893,7 -852,7 +893,7 @@@ static char **get_path_split(void
        if (!n)
                return NULL;
  
-       path = xmalloc((n+1)*sizeof(char *));
+       ALLOC_ARRAY(path, n + 1);
        p = envpath;
        i = 0;
        do {
@@@ -978,7 -937,7 +978,7 @@@ static wchar_t *make_environment_block(
                i++;
  
        /* copy the environment, leaving space for changes */
-       tmpenv = xmalloc((size + i) * sizeof(char*));
+       ALLOC_ARRAY(tmpenv, size + i);
        memcpy(tmpenv, environ, size * sizeof(char*));
  
        /* merge supplied environment changes into the temporary environment */
@@@ -1069,7 -1028,7 +1069,7 @@@ static pid_t mingw_spawnve_fd(const cha
                        free(quoted);
        }
  
-       wargs = xmalloc((2 * args.len + 1) * sizeof(wchar_t));
+       wargs = xmalloc_array(st_add(st_mult(2, args.len), 1), sizeof(wchar_t));
        xutftowcs(wargs, args.buf, 2 * args.len + 1);
        strbuf_release(&args);
  
@@@ -1168,7 -1127,7 +1168,7 @@@ static int try_shell_exec(const char *c
                int argc = 0;
                const char **argv2;
                while (argv[argc]) argc++;
-               argv2 = xmalloc(sizeof(*argv) * (argc+1));
+               ALLOC_ARRAY(argv2, argc + 1);
                argv2[0] = (char *)cmd; /* full path to the script file */
                memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
                pid = mingw_spawnv(prog, argv2, 1);
@@@ -1642,12 -1601,7 +1642,12 @@@ repeat
        if (gle == ERROR_ACCESS_DENIED &&
            (attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
                if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
 -                      errno = EISDIR;
 +                      DWORD attrsold = GetFileAttributesW(wpold);
 +                      if (attrsold == INVALID_FILE_ATTRIBUTES ||
 +                          !(attrsold & FILE_ATTRIBUTE_DIRECTORY))
 +                              errno = EISDIR;
 +                      else if (!_wrmdir(wpnew))
 +                              goto repeat;
                        return -1;
                }
                if ((attrs & FILE_ATTRIBUTE_READONLY) &&
@@@ -1892,8 -1846,7 +1892,8 @@@ void mingw_open_html(const char *unixpa
                die("cannot run browser");
  
        printf("Launching default browser to display HTML ...\n");
 -      r = (int)ShellExecute(NULL, "open", htmlpath, NULL, "\\", SW_SHOWNORMAL);
 +      r = HCAST(int, ShellExecute(NULL, "open", htmlpath,
 +                              NULL, "\\", SW_SHOWNORMAL));
        FreeLibrary(shell32);
        /* see the MSDN documentation referring to the result codes here */
        if (r <= 32) {
@@@ -2091,37 -2044,6 +2091,37 @@@ int xwcstoutf(char *utf, const wchar_t 
        return -1;
  }
  
 +static void setup_windows_environment()
 +{
 +      char *tmp = getenv("TMPDIR");
 +
 +      /* on Windows it is TMP and TEMP */
 +      if (!tmp) {
 +              if (!(tmp = getenv("TMP")))
 +                      tmp = getenv("TEMP");
 +              if (tmp) {
 +                      setenv("TMPDIR", tmp, 1);
 +                      tmp = getenv("TMPDIR");
 +              }
 +      }
 +
 +      if (tmp) {
 +              /*
 +               * Convert all dir separators to forward slashes,
 +               * to help shell commands called from the Git
 +               * executable (by not mistaking the dir separators
 +               * for escape characters).
 +               */
 +              for (; *tmp; tmp++)
 +                      if (*tmp == '\\')
 +                              *tmp = '/';
 +      }
 +
 +      /* simulate TERM to enable auto-color (see color.c) */
 +      if (!getenv("TERM"))
 +              setenv("TERM", "cygwin", 1);
 +}
 +
  /*
   * Disable MSVCRT command line wildcard expansion (__getmainargs called from
   * mingw startup code, see init.c in mingw runtime).
@@@ -2200,7 -2122,19 +2200,7 @@@ void mingw_startup(
        qsort(environ, i, sizeof(char*), compareenv);
  
        /* fix Windows specific environment settings */
 -
 -      /* on Windows it is TMP and TEMP */
 -      if (!mingw_getenv("TMPDIR")) {
 -              const char *tmp = mingw_getenv("TMP");
 -              if (!tmp)
 -                      tmp = mingw_getenv("TEMP");
 -              if (tmp)
 -                      setenv("TMPDIR", tmp, 1);
 -      }
 -
 -      /* simulate TERM to enable auto-color (see color.c) */
 -      if (!getenv("TERM"))
 -              setenv("TERM", "cygwin", 1);
 +      setup_windows_environment();
  
        /* initialize critical section for waitpid pinfo_t list */
        InitializeCriticalSection(&pinfo_cs);
diff --combined config.c
index b95ac3a9cd5ca4fef392a2202f0a5722d15b8dde,ba8fd1376587848f0691883e7d1d71ad4d75d599..e7b589a3a847133e6062fb5b2306508542959238
+++ b/config.c
@@@ -1594,30 -1594,6 +1594,30 @@@ int git_config_get_pathname(const char 
        return ret;
  }
  
 +int git_config_get_untracked_cache(void)
 +{
 +      int val = -1;
 +      const char *v;
 +
 +      /* Hack for test programs like test-dump-untracked-cache */
 +      if (ignore_untracked_cache_config)
 +              return -1;
 +
 +      if (!git_config_get_maybe_bool("core.untrackedcache", &val))
 +              return val;
 +
 +      if (!git_config_get_value("core.untrackedcache", &v)) {
 +              if (!strcasecmp(v, "keep"))
 +                      return -1;
 +
 +              error("unknown core.untrackedCache value '%s'; "
 +                    "using 'keep' default value", v);
 +              return -1;
 +      }
 +
 +      return -1; /* default value */
 +}
 +
  NORETURN
  void git_die_config_linenr(const char *key, const char *filename, int linenr)
  {
@@@ -1902,7 -1878,7 +1902,7 @@@ static int git_config_parse_key_1(cons
         * Validate the key and while at it, lower case it for matching.
         */
        if (store_key)
-               *store_key = xmalloc(strlen(key) + 1);
+               *store_key = xmallocz(strlen(key));
  
        dot = 0;
        for (i = 0; key[i]; i++) {
                if (store_key)
                        (*store_key)[i] = c;
        }
-       if (store_key)
-               (*store_key)[i] = 0;
  
        return 0;
  
diff --combined daemon.c
index 46b411c7d941ecf862b7a7ffdce38ef8767e29e4,1e258ac8f9cebf135aa7ac07389f3ae4a49842ff..8d45c336f5f816768962c39e8af6b10e0bb0e21e
+++ b/daemon.c
@@@ -424,7 -424,7 +424,7 @@@ static void copy_to_log(int fd
                return;
        }
  
 -      while (strbuf_getline(&line, fp, '\n') != EOF) {
 +      while (strbuf_getline_lf(&line, fp) != EOF) {
                logerror("%s", line.buf);
                strbuf_setlen(&line, 0);
        }
@@@ -808,7 -808,7 +808,7 @@@ static void check_dead_children(void
                        cradle = &blanket->next;
  }
  
- static char **cld_argv;
+ static struct argv_array cld_argv = ARGV_ARRAY_INIT;
  static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
  {
        struct child_process cld = CHILD_PROCESS_INIT;
  #endif
        }
  
-       cld.argv = (const char **)cld_argv;
+       cld.argv = cld_argv.argv;
        cld.in = incoming;
        cld.out = dup(incoming);
  
@@@ -1374,12 -1374,10 +1374,10 @@@ int main(int argc, char **argv
                write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
  
        /* prepare argv for serving-processes */
-       cld_argv = xmalloc(sizeof (char *) * (argc + 2));
-       cld_argv[0] = argv[0];  /* git-daemon */
-       cld_argv[1] = "--serve";
+       argv_array_push(&cld_argv, argv[0]); /* git-daemon */
+       argv_array_push(&cld_argv, "--serve");
        for (i = 1; i < argc; ++i)
-               cld_argv[i+1] = argv[i];
-       cld_argv[argc+1] = NULL;
+               argv_array_push(&cld_argv, argv[i]);
  
        return serve(&listen_addr, listen_port, cred);
  }
diff --combined diff.c
index a088e269b42d4af24a756c991bf60dca8eb4153d,a70ec6ef1a613a63482b0f7cf8d997af685793d9..059123c5dcef4129763895b0f2ad5a54728b0c07
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -2607,12 -2607,9 +2607,9 @@@ static void builtin_checkdiff(const cha
  
  struct diff_filespec *alloc_filespec(const char *path)
  {
-       int namelen = strlen(path);
-       struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1);
+       struct diff_filespec *spec;
  
-       memset(spec, 0, sizeof(*spec));
-       spec->path = (char *)(spec + 1);
-       memcpy(spec->path, path, namelen+1);
+       FLEXPTR_ALLOC_STR(spec, path, path);
        spec->count = 1;
        spec->is_binary = -1;
        return spec;
@@@ -2707,21 -2704,21 +2704,21 @@@ static int reuse_worktree_file(const ch
  
  static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
  {
-       int len;
-       char *data = xmalloc(100), *dirty = "";
+       struct strbuf buf = STRBUF_INIT;
+       char *dirty = "";
  
        /* Are we looking at the work tree? */
        if (s->dirty_submodule)
                dirty = "-dirty";
  
-       len = snprintf(data, 100,
-                      "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
-       s->data = data;
-       s->size = len;
-       s->should_free = 1;
+       strbuf_addf(&buf, "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
+       s->size = buf.len;
        if (size_only) {
                s->data = NULL;
-               free(data);
+               strbuf_release(&buf);
+       } else {
+               s->data = strbuf_detach(&buf, NULL);
+               s->should_free = 1;
        }
        return 0;
  }
@@@ -5085,7 -5082,7 +5082,7 @@@ size_t fill_textconv(struct userdiff_dr
  {
        size_t size;
  
 -      if (!driver || !driver->textconv) {
 +      if (!driver) {
                if (!DIFF_FILE_VALID(df)) {
                        *outbuf = "";
                        return 0;
                return df->size;
        }
  
 +      if (!driver->textconv)
 +              die("BUG: fill_textconv called with non-textconv driver");
 +
        if (driver->textconv_cache && df->sha1_valid) {
                *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
                                          &size);
diff --combined diff.h
index 4505b4d91dd0985b25f5ba8c087c5afe34166631,beafbbdec763f9ac7fb6a41f225a97fa8d0ae580..e7d68edaf9d4744ce3c48356e1835c5506d5322d
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -222,8 -222,8 +222,8 @@@ struct combine_diff_path 
        } parent[FLEX_ARRAY];
  };
  #define combine_diff_path_size(n, l) \
-       (sizeof(struct combine_diff_path) + \
-        sizeof(struct combine_diff_parent) * (n) + (l) + 1)
+       st_add4(sizeof(struct combine_diff_path), (l), 1, \
+               st_mult(sizeof(struct combine_diff_parent), (n)))
  
  extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
                              int dense, struct rev_info *);
@@@ -349,26 -349,10 +349,26 @@@ extern void diff_no_index(struct rev_in
  
  extern int index_differs_from(const char *def, int diff_flags);
  
 +/*
 + * Fill the contents of the filespec "df", respecting any textconv defined by
 + * its userdiff driver.  The "driver" parameter must come from a
 + * previous call to get_textconv(), and therefore should either be NULL or have
 + * textconv enabled.
 + *
 + * Note that the memory ownership of the resulting buffer depends on whether
 + * the driver field is NULL. If it is, then the memory belongs to the filespec
 + * struct. If it is non-NULL, then "outbuf" points to a newly allocated buffer
 + * that should be freed by the caller.
 + */
  extern size_t fill_textconv(struct userdiff_driver *driver,
                            struct diff_filespec *df,
                            char **outbuf);
  
 +/*
 + * Look up the userdiff driver for the given filespec, and return it if
 + * and only if it has textconv enabled (otherwise return NULL). The result
 + * can be passed to fill_textconv().
 + */
  extern struct userdiff_driver *get_textconv(struct diff_filespec *one);
  
  extern int parse_rename_score(const char **cp_p);
diff --combined dir.c
index 552af237040b3bab95d94f43b169a6278623cea3,3087b7eb2e8f6fa8f3b3fb16310f621e80db301e..69e0be6aa28d6e9bcfa3baf4f80de5bcc3158fe0
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -53,8 -53,6 +53,8 @@@ static enum path_treatment read_directo
        int check_only, const struct path_simplify *simplify);
  static int get_dtype(struct dirent *de, const char *path, int len);
  
 +static struct trace_key trace_exclude = TRACE_KEY_INIT(EXCLUDE);
 +
  /* helper string functions with support for the ignore_case flag */
  int strcmp_icase(const char *a, const char *b)
  {
@@@ -505,12 -503,7 +505,7 @@@ void add_exclude(const char *string, co
  
        parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen);
        if (flags & EXC_FLAG_MUSTBEDIR) {
-               char *s;
-               x = xmalloc(sizeof(*x) + patternlen + 1);
-               s = (char *)(x+1);
-               memcpy(s, string, patternlen);
-               s[patternlen] = '\0';
-               x->pattern = s;
+               FLEXPTR_ALLOC_MEM(x, pattern, string, patternlen);
        } else {
                x = xmalloc(sizeof(*x));
                x->pattern = string;
        x->baselen = baselen;
        x->flags = flags;
        x->srcpos = srcpos;
 +      string_list_init(&x->sticky_paths, 1);
        ALLOC_GROW(el->excludes, el->nr + 1, el->alloc);
        el->excludes[el->nr++] = x;
        x->el = el;
@@@ -562,10 -554,8 +557,10 @@@ void clear_exclude_list(struct exclude_
  {
        int i;
  
 -      for (i = 0; i < el->nr; i++)
 +      for (i = 0; i < el->nr; i++) {
 +              string_list_clear(&el->excludes[i]->sticky_paths, 0);
                free(el->excludes[i]);
 +      }
        free(el->excludes);
        free(el->filebuf);
  
@@@ -630,10 -620,7 +625,7 @@@ static struct untracked_cache_dir *look
        }
  
        uc->dir_created++;
-       d = xmalloc(sizeof(*d) + len + 1);
-       memset(d, 0, sizeof(*d));
-       memcpy(d->name, name, len);
-       d->name[len] = '\0';
+       FLEX_ALLOC_MEM(d, name, name, len);
  
        ALLOC_GROW(dir->dirs, dir->dirs_nr + 1, dir->dirs_alloc);
        memmove(dir->dirs + first + 1, dir->dirs + first,
@@@ -702,7 -689,7 +694,7 @@@ static int add_excludes(const char *fna
                        return 0;
                }
                if (buf[size-1] != '\n') {
-                       buf = xrealloc(buf, size+1);
+                       buf = xrealloc(buf, st_add(size, 1));
                        buf[size++] = '\n';
                }
        } else {
                        close(fd);
                        return 0;
                }
-               buf = xmalloc(size+1);
+               buf = xmallocz(size);
                if (read_in_full(fd, buf, size) != size) {
                        free(buf);
                        close(fd);
@@@ -883,7 -870,7 +875,7 @@@ int match_pathname(const char *pathname
                 * then our prefix match is all we need; we
                 * do not need to call fnmatch at all.
                 */
 -              if (!patternlen && !namelen)
 +              if (!patternlen && (!namelen || *name == '/'))
                        return 1;
        }
  
                                 WM_PATHNAME) == 0;
  }
  
 +static void add_sticky(struct exclude *exc, const char *pathname, int pathlen)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      int i;
 +
 +      for (i = exc->sticky_paths.nr - 1; i >= 0; i--) {
 +              const char *sticky = exc->sticky_paths.items[i].string;
 +              int len = strlen(sticky);
 +
 +              if (pathlen < len && sticky[pathlen] == '/' &&
 +                  !strncmp(pathname, sticky, pathlen))
 +                      return;
 +      }
 +
 +      strbuf_add(&sb, pathname, pathlen);
 +      string_list_append_nodup(&exc->sticky_paths, strbuf_detach(&sb, NULL));
 +}
 +
 +static int match_sticky(struct exclude *exc, const char *pathname, int pathlen, int dtype)
 +{
 +      int i;
 +
 +      for (i = exc->sticky_paths.nr - 1; i >= 0; i--) {
 +              const char *sticky = exc->sticky_paths.items[i].string;
 +              int len = strlen(sticky);
 +
 +              if (pathlen == len && dtype == DT_DIR &&
 +                  !strncmp(pathname, sticky, len))
 +                      return 1;
 +
 +              if (pathlen > len && pathname[len] == '/' &&
 +                  !strncmp(pathname, sticky, len))
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +static inline int different_decisions(const struct exclude *a,
 +                                    const struct exclude *b)
 +{
 +      return (a->flags & EXC_FLAG_NEGATIVE) != (b->flags & EXC_FLAG_NEGATIVE);
 +}
 +
 +/*
 + * Return non-zero if pathname is a directory and an ancestor of the
 + * literal path in a pattern.
 + */
 +static int match_directory_part(const char *pathname, int pathlen,
 +                              int *dtype, struct exclude *x)
 +{
 +      const char      *base       = x->base;
 +      int              baselen    = x->baselen ? x->baselen - 1 : 0;
 +      const char      *pattern    = x->pattern;
 +      int              prefix     = x->nowildcardlen;
 +      int              patternlen = x->patternlen;
 +
 +      if (*dtype == DT_UNKNOWN)
 +              *dtype = get_dtype(NULL, pathname, pathlen);
 +      if (*dtype != DT_DIR)
 +              return 0;
 +
 +      if (*pattern == '/') {
 +              pattern++;
 +              patternlen--;
 +              prefix--;
 +      }
 +
 +      if (baselen) {
 +              if (((pathlen < baselen && base[pathlen] == '/') ||
 +                   pathlen == baselen) &&
 +                  !strncmp_icase(pathname, base, pathlen))
 +                      return 1;
 +              pathname += baselen + 1;
 +              pathlen  -= baselen + 1;
 +      }
 +
 +
 +      if (prefix &&
 +          (((pathlen < prefix && pattern[pathlen] == '/') ||
 +            pathlen == prefix) &&
 +           !strncmp_icase(pathname, pattern, pathlen)))
 +              return 1;
 +
 +      return 0;
 +}
 +
 +static struct exclude *should_descend(const char *pathname, int pathlen,
 +                                    int *dtype, struct exclude_list *el,
 +                                    struct exclude *exc)
 +{
 +      int i;
 +
 +      for (i = el->nr - 1; 0 <= i; i--) {
 +              struct exclude *x = el->excludes[i];
 +
 +              if (x == exc)
 +                      break;
 +
 +              if (!(x->flags & EXC_FLAG_NODIR) &&
 +                  different_decisions(x, exc) &&
 +                  match_directory_part(pathname, pathlen, dtype, x))
 +                      return x;
 +      }
 +      return NULL;
 +}
 +
  /*
   * Scan the given exclude list in reverse to see whether pathname
   * should be ignored.  The first match (i.e. the last on the list), if
@@@ -1012,32 -892,16 +1004,32 @@@ static struct exclude *last_exclude_mat
                                                       struct exclude_list *el)
  {
        struct exclude *exc = NULL; /* undecided */
 -      int i;
 +      int i, maybe_descend = 0;
  
        if (!el->nr)
                return NULL;    /* undefined */
  
 +      trace_printf_key(&trace_exclude, "exclude: from %s\n", el->src);
 +
        for (i = el->nr - 1; 0 <= i; i--) {
                struct exclude *x = el->excludes[i];
                const char *exclude = x->pattern;
                int prefix = x->nowildcardlen;
  
 +              if (!maybe_descend && i < el->nr - 1 &&
 +                  different_decisions(x, el->excludes[i+1]))
 +                      maybe_descend = 1;
 +
 +              if (x->sticky_paths.nr) {
 +                      if (*dtype == DT_UNKNOWN)
 +                              *dtype = get_dtype(NULL, pathname, pathlen);
 +                      if (match_sticky(x, pathname, pathlen, *dtype)) {
 +                              exc = x;
 +                              break;
 +                      }
 +                      continue;
 +              }
 +
                if (x->flags & EXC_FLAG_MUSTBEDIR) {
                        if (*dtype == DT_UNKNOWN)
                                *dtype = get_dtype(NULL, pathname, pathlen);
                        break;
                }
        }
 +
 +      if (!exc) {
 +              trace_printf_key(&trace_exclude, "exclude: %.*s => n/a\n",
 +                               pathlen, pathname);
 +              return NULL;
 +      }
 +
 +      /*
 +       * We have found a matching pattern "exc" that may exclude whole
 +       * directory. We also found that there may be a pattern that matches
 +       * something inside the directory and reincludes stuff.
 +       *
 +       * Go through the patterns again, find that pattern and double check.
 +       * If it's true, return "undecided" and keep descending in. "exc" is
 +       * marked sticky so that it continues to match inside the directory.
 +       */
 +      if (!(exc->flags & EXC_FLAG_NEGATIVE) && maybe_descend) {
 +              struct exclude *x;
 +
 +              if (*dtype == DT_UNKNOWN)
 +                      *dtype = get_dtype(NULL, pathname, pathlen);
 +
 +              if (*dtype == DT_DIR &&
 +                  (x = should_descend(pathname, pathlen, dtype, el, exc))) {
 +                      add_sticky(exc, pathname, pathlen);
 +                      trace_printf_key(&trace_exclude,
 +                                       "exclude: %.*s vs %s at line %d => %s,"
 +                                       " forced open by %s at line %d => n/a\n",
 +                                       pathlen, pathname, exc->pattern, exc->srcpos,
 +                                       exc->flags & EXC_FLAG_NEGATIVE ? "no" : "yes",
 +                                       x->pattern, x->srcpos);
 +                      return NULL;
 +              }
 +      }
 +
 +      trace_printf_key(&trace_exclude, "exclude: %.*s vs %s at line %d => %s%s\n",
 +                       pathlen, pathname, exc->pattern, exc->srcpos,
 +                       exc->flags & EXC_FLAG_NEGATIVE ? "no" : "yes",
 +                       exc->sticky_paths.nr ? " (stuck)" : "");
        return exc;
  }
  
@@@ -1334,10 -1159,8 +1326,8 @@@ static struct dir_entry *dir_entry_new(
  {
        struct dir_entry *ent;
  
-       ent = xmalloc(sizeof(*ent) + len + 1);
+       FLEX_ALLOC_MEM(ent, name, pathname, len);
        ent->len = len;
-       memcpy(ent->name, pathname, len);
-       ent->name[len] = 0;
        return ent;
  }
  
@@@ -1850,13 -1673,9 +1840,13 @@@ static enum path_treatment read_directo
        struct cached_dir cdir;
        enum path_treatment state, subdir_state, dir_state = path_none;
        struct strbuf path = STRBUF_INIT;
 +      static int level = 0;
  
        strbuf_add(&path, base, baselen);
  
 +      trace_printf_key(&trace_exclude, "exclude: [%d] enter '%.*s'\n",
 +                       level++, baselen, base);
 +
        if (open_cached_dir(&cdir, dir, untracked, &path, check_only))
                goto out;
  
        }
        close_cached_dir(&cdir);
   out:
 +      trace_printf_key(&trace_exclude, "exclude: [%d] leave '%.*s'\n",
 +                       --level, baselen, base);
        strbuf_release(&path);
  
        return dir_state;
@@@ -2012,67 -1829,31 +2002,67 @@@ static const char *get_ident_string(voi
                return sb.buf;
        if (uname(&uts) < 0)
                die_errno(_("failed to get kernel name and information"));
 -      strbuf_addf(&sb, "Location %s, system %s %s %s", get_git_work_tree(),
 -                  uts.sysname, uts.release, uts.version);
 +      strbuf_addf(&sb, "Location %s, system %s", get_git_work_tree(),
 +                  uts.sysname);
        return sb.buf;
  }
  
  static int ident_in_untracked(const struct untracked_cache *uc)
  {
 -      const char *end = uc->ident.buf + uc->ident.len;
 -      const char *p   = uc->ident.buf;
 +      /*
 +       * Previous git versions may have saved many NUL separated
 +       * strings in the "ident" field, but it is insane to manage
 +       * many locations, so just take care of the first one.
 +       */
  
 -      for (p = uc->ident.buf; p < end; p += strlen(p) + 1)
 -              if (!strcmp(p, get_ident_string()))
 -                      return 1;
 -      return 0;
 +      return !strcmp(uc->ident.buf, get_ident_string());
  }
  
 -void add_untracked_ident(struct untracked_cache *uc)
 +static void set_untracked_ident(struct untracked_cache *uc)
  {
 -      if (ident_in_untracked(uc))
 -              return;
 +      strbuf_reset(&uc->ident);
        strbuf_addstr(&uc->ident, get_ident_string());
 -      /* this strbuf contains a list of strings, save NUL too */
 +
 +      /*
 +       * This strbuf used to contain a list of NUL separated
 +       * strings, so save NUL too for backward compatibility.
 +       */
        strbuf_addch(&uc->ident, 0);
  }
  
 +static void new_untracked_cache(struct index_state *istate)
 +{
 +      struct untracked_cache *uc = xcalloc(1, sizeof(*uc));
 +      strbuf_init(&uc->ident, 100);
 +      uc->exclude_per_dir = ".gitignore";
 +      /* should be the same flags used by git-status */
 +      uc->dir_flags = DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
 +      set_untracked_ident(uc);
 +      istate->untracked = uc;
 +      istate->cache_changed |= UNTRACKED_CHANGED;
 +}
 +
 +void add_untracked_cache(struct index_state *istate)
 +{
 +      if (!istate->untracked) {
 +              new_untracked_cache(istate);
 +      } else {
 +              if (!ident_in_untracked(istate->untracked)) {
 +                      free_untracked_cache(istate->untracked);
 +                      new_untracked_cache(istate);
 +              }
 +      }
 +}
 +
 +void remove_untracked_cache(struct index_state *istate)
 +{
 +      if (istate->untracked) {
 +              free_untracked_cache(istate->untracked);
 +              istate->untracked = NULL;
 +              istate->cache_changed |= UNTRACKED_CHANGED;
 +      }
 +}
 +
  static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *dir,
                                                      int base_len,
                                                      const struct pathspec *pathspec)
                return NULL;
  
        if (!ident_in_untracked(dir->untracked)) {
 -              warning(_("Untracked cache is disabled on this system."));
 +              warning(_("Untracked cache is disabled on this system or location."));
                return NULL;
        }
  
        return root;
  }
  
 +static void clear_sticky(struct dir_struct *dir)
 +{
 +      struct exclude_list_group *g;
 +      struct exclude_list *el;
 +      struct exclude *x;
 +      int i, j, k;
 +
 +      for (i = EXC_CMDL; i <= EXC_FILE; i++) {
 +              g = &dir->exclude_list_group[i];
 +              for (j = g->nr - 1; j >= 0; j--) {
 +                      el = &g->el[j];
 +                      for (k = el->nr - 1; 0 <= k; k--) {
 +                              x = el->excludes[k];
 +                              string_list_clear(&x->sticky_paths, 0);
 +                      }
 +              }
 +      }
 +}
 +
  int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
  {
        struct path_simplify *simplify;
        if (has_symlink_leading_path(path, len))
                return dir->nr;
  
 +      /*
 +       * Stay on the safe side. if read_directory() has run once on
 +       * "dir", some sticky flag may have been left. Clear them all.
 +       */
 +      clear_sticky(dir);
 +
        /*
         * exclude patterns are treated like positive ones in
         * create_simplify. Usually exclude patterns should be a
@@@ -2568,16 -2324,15 +2558,15 @@@ void write_untracked_extension(struct s
        struct ondisk_untracked_cache *ouc;
        struct write_data wd;
        unsigned char varbuf[16];
-       int len = 0, varint_len;
-       if (untracked->exclude_per_dir)
-               len = strlen(untracked->exclude_per_dir);
-       ouc = xmalloc(sizeof(*ouc) + len + 1);
+       int varint_len;
+       size_t len = strlen(untracked->exclude_per_dir);
+       FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len);
        stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
        stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
        hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1);
        hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
        ouc->dir_flags = htonl(untracked->dir_flags);
-       memcpy(ouc->exclude_per_dir, untracked->exclude_per_dir, len + 1);
  
        varint_len = encode_varint(untracked->ident.len, varbuf);
        strbuf_add(out, varbuf, varint_len);
@@@ -2682,21 -2437,21 +2671,21 @@@ static int read_one_dir(struct untracke
        ud.untracked_alloc = value;
        ud.untracked_nr    = value;
        if (ud.untracked_nr)
-               ud.untracked = xmalloc(sizeof(*ud.untracked) * ud.untracked_nr);
+               ALLOC_ARRAY(ud.untracked, ud.untracked_nr);
        data = next;
  
        next = data;
        ud.dirs_alloc = ud.dirs_nr = decode_varint(&next);
        if (next > end)
                return -1;
-       ud.dirs = xmalloc(sizeof(*ud.dirs) * ud.dirs_nr);
+       ALLOC_ARRAY(ud.dirs, ud.dirs_nr);
        data = next;
  
        len = strlen((const char *)data);
        next = data + len + 1;
        if (next > rd->end)
                return -1;
-       *untracked_ = untracked = xmalloc(sizeof(*untracked) + len);
+       *untracked_ = untracked = xmalloc(st_add(sizeof(*untracked), len));
        memcpy(untracked, &ud, sizeof(ud));
        memcpy(untracked->name, data, len + 1);
        data = next;
@@@ -2809,7 -2564,7 +2798,7 @@@ struct untracked_cache *read_untracked_
        rd.data       = next;
        rd.end        = end;
        rd.index      = 0;
-       rd.ucd        = xmalloc(sizeof(*rd.ucd) * len);
+       ALLOC_ARRAY(rd.ucd, len);
  
        if (read_one_dir(&uc->root, &rd) || rd.index != len)
                goto done;
diff --combined exec_cmd.c
index 680b257cd5ae99175dc52e85a2aa4ba570c7f35b,cf442a97f86aea7accc673d3fe55b9b7494dbf78..9d5703a157fe8e9d8396e0771798e54dc5eea6eb
@@@ -1,6 -1,7 +1,7 @@@
  #include "cache.h"
  #include "exec_cmd.h"
  #include "quote.h"
+ #include "argv-array.h"
  #define MAX_ARGS      32
  
  static const char *argv_exec_path;
@@@ -43,10 -44,12 +44,10 @@@ const char *git_extract_argv0_path(cons
  
        if (!argv0 || !*argv0)
                return NULL;
 -      slash = argv0 + strlen(argv0);
  
 -      while (argv0 <= slash && !is_dir_sep(*slash))
 -              slash--;
 +      slash = find_last_dir_sep(argv0);
  
 -      if (slash >= argv0) {
 +      if (slash) {
                argv0_path = xstrndup(argv0, slash - argv0);
                return slash + 1;
        }
@@@ -105,32 -108,25 +106,25 @@@ void setup_path(void
        strbuf_release(&new_path);
  }
  
- const char **prepare_git_cmd(const char **argv)
+ const char **prepare_git_cmd(struct argv_array *out, const char **argv)
  {
-       int argc;
-       const char **nargv;
-       for (argc = 0; argv[argc]; argc++)
-               ; /* just counting */
-       nargv = xmalloc(sizeof(*nargv) * (argc + 2));
-       nargv[0] = "git";
-       for (argc = 0; argv[argc]; argc++)
-               nargv[argc + 1] = argv[argc];
-       nargv[argc + 1] = NULL;
-       return nargv;
+       argv_array_push(out, "git");
+       argv_array_pushv(out, argv);
+       return out->argv;
  }
  
  int execv_git_cmd(const char **argv) {
-       const char **nargv = prepare_git_cmd(argv);
-       trace_argv_printf(nargv, "trace: exec:");
+       struct argv_array nargv = ARGV_ARRAY_INIT;
+       prepare_git_cmd(&nargv, argv);
+       trace_argv_printf(nargv.argv, "trace: exec:");
  
        /* execvp() can only ever return if it fails */
-       sane_execvp("git", (char **)nargv);
+       sane_execvp("git", (char **)nargv.argv);
  
        trace_printf("trace: exec failed: %s\n", strerror(errno));
  
-       free(nargv);
+       argv_array_clear(&nargv);
        return -1;
  }
  
diff --combined fast-import.c
index bf01b3422142d6be204a983a9909efdb29d385d0,75f77480a2d3829f1f01f4df44d6a0cc5f4f7030..9fc7093406b1e2085a3b2180a8506bcd6d486e06
@@@ -622,7 -622,7 +622,7 @@@ static void *pool_alloc(size_t len
                        return xmalloc(len);
                }
                total_allocd += sizeof(struct mem_pool) + mem_pool_alloc;
-               p = xmalloc(sizeof(struct mem_pool) + mem_pool_alloc);
+               p = xmalloc(st_add(sizeof(struct mem_pool), mem_pool_alloc));
                p->next_pool = mem_pool;
                p->next_free = (char *) p->space;
                p->end = p->next_free + mem_pool_alloc;
@@@ -814,7 -814,8 +814,8 @@@ static struct tree_entry *new_tree_entr
        if (!avail_tree_entry) {
                unsigned int n = tree_entry_alloc;
                total_allocd += n * sizeof(struct tree_entry);
-               avail_tree_entry = e = xmalloc(n * sizeof(struct tree_entry));
+               ALLOC_ARRAY(e, n);
+               avail_tree_entry = e;
                while (n-- > 1) {
                        *((void**)e) = e + 1;
                        e++;
@@@ -864,15 -865,12 +865,12 @@@ static void start_packfile(void
  {
        static char tmp_file[PATH_MAX];
        struct packed_git *p;
-       int namelen;
        struct pack_header hdr;
        int pack_fd;
  
        pack_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
                              "pack/tmp_pack_XXXXXX");
-       namelen = strlen(tmp_file) + 2;
-       p = xcalloc(1, sizeof(*p) + namelen);
-       xsnprintf(p->pack_name, namelen, "%s", tmp_file);
+       FLEX_ALLOC_STR(p, pack_name, tmp_file);
        p->pack_fd = pack_fd;
        p->do_not_close = 1;
        pack_file = sha1fd(pack_fd, p->pack_name);
@@@ -898,7 -896,7 +896,7 @@@ static const char *create_index(void
        struct object_entry_pool *o;
  
        /* Build the table of object IDs. */
-       idx = xmalloc(object_count * sizeof(*idx));
+       ALLOC_ARRAY(idx, object_count);
        c = idx;
        for (o = blocks; o; o = o->next_pool)
                for (e = o->next_free; e-- != o->entries;)
@@@ -1888,7 -1886,7 +1886,7 @@@ static int read_next_command(void
                        struct recent_command *rc;
  
                        strbuf_detach(&command_buf, NULL);
 -                      stdin_eof = strbuf_getline(&command_buf, stdin, '\n');
 +                      stdin_eof = strbuf_getline_lf(&command_buf, stdin);
                        if (stdin_eof)
                                return EOF;
  
@@@ -1960,7 -1958,7 +1958,7 @@@ static int parse_data(struct strbuf *sb
  
                strbuf_detach(&command_buf, NULL);
                for (;;) {
 -                      if (strbuf_getline(&command_buf, stdin, '\n') == EOF)
 +                      if (strbuf_getline_lf(&command_buf, stdin) == EOF)
                                die("EOF in data (terminator '%s' not found)", term);
                        if (term_len == command_buf.len
                                && !strcmp(term, command_buf.buf))
diff --combined git-compat-util.h
index 2acef3f113f64cc3491f085b6535353d16f9a7c8,1459f9b87e9d939af3460d3461ad4ec805e1ef29..c07e0c177890639cf016eac9766d04f235dbfbcf
  #define unsigned_add_overflows(a, b) \
      ((b) > maximum_unsigned_value_of_type(a) - (a))
  
+ /*
+  * Returns true if the multiplication of "a" and "b" will
+  * overflow. The types of "a" and "b" must match and must be unsigned.
+  * Note that this macro evaluates "a" twice!
+  */
+ #define unsigned_mult_overflows(a, b) \
+     ((a) && (b) > maximum_unsigned_value_of_type(a) / (a))
  #ifdef __GNUC__
  #define TYPEOF(x) (__typeof__(x))
  #else
@@@ -325,6 -333,10 +333,6 @@@ extern char *gitdirname(char *)
  #define _PATH_DEFPATH "/usr/local/bin:/usr/bin:/bin"
  #endif
  
 -#ifndef STRIP_EXTENSION
 -#define STRIP_EXTENSION ""
 -#endif
 -
  #ifndef has_dos_drive_prefix
  static inline int git_has_dos_drive_prefix(const char *path)
  {
@@@ -669,7 -681,6 +677,6 @@@ extern int git_vsnprintf(char *str, siz
  #ifdef __GLIBC_PREREQ
  #if __GLIBC_PREREQ(2, 1)
  #define HAVE_STRCHRNUL
- #define HAVE_MEMPCPY
  #endif
  #endif
  
@@@ -683,14 -694,6 +690,6 @@@ static inline char *gitstrchrnul(const 
  }
  #endif
  
- #ifndef HAVE_MEMPCPY
- #define mempcpy gitmempcpy
- static inline void *gitmempcpy(void *dest, const void *src, size_t n)
- {
-       return (char *)memcpy(dest, src, n) + n;
- }
- #endif
  #ifdef NO_INET_PTON
  int inet_pton(int af, const char *src, void *dst);
  #endif
@@@ -709,6 -712,32 +708,32 @@@ extern void release_pack_memory(size_t)
  typedef void (*try_to_free_t)(size_t);
  extern try_to_free_t set_try_to_free_routine(try_to_free_t);
  
+ static inline size_t st_add(size_t a, size_t b)
+ {
+       if (unsigned_add_overflows(a, b))
+               die("size_t overflow: %"PRIuMAX" + %"PRIuMAX,
+                   (uintmax_t)a, (uintmax_t)b);
+       return a + b;
+ }
+ #define st_add3(a,b,c)   st_add((a),st_add((b),(c)))
+ #define st_add4(a,b,c,d) st_add((a),st_add3((b),(c),(d)))
+ static inline size_t st_mult(size_t a, size_t b)
+ {
+       if (unsigned_mult_overflows(a, b))
+               die("size_t overflow: %"PRIuMAX" * %"PRIuMAX,
+                   (uintmax_t)a, (uintmax_t)b);
+       return a * b;
+ }
+ static inline size_t st_sub(size_t a, size_t b)
+ {
+       if (a < b)
+               die("size_t underflow: %"PRIuMAX" - %"PRIuMAX,
+                   (uintmax_t)a, (uintmax_t)b);
+       return a - b;
+ }
  #ifdef HAVE_ALLOCA_H
  # include <alloca.h>
  # define xalloca(size)      (alloca(size))
@@@ -741,7 -770,70 +766,70 @@@ extern int odb_pack_keep(char *name, si
  extern char *xgetcwd(void);
  extern FILE *fopen_for_writing(const char *path);
  
- #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), (alloc) * sizeof(*(x)))
+ #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc)))
+ #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))
+ /*
+  * These functions help you allocate structs with flex arrays, and copy
+  * the data directly into the array. For example, if you had:
+  *
+  *   struct foo {
+  *     int bar;
+  *     char name[FLEX_ARRAY];
+  *   };
+  *
+  * you can do:
+  *
+  *   struct foo *f;
+  *   FLEX_ALLOC_MEM(f, name, src, len);
+  *
+  * to allocate a "foo" with the contents of "src" in the "name" field.
+  * The resulting struct is automatically zero'd, and the flex-array field
+  * is NUL-terminated (whether the incoming src buffer was or not).
+  *
+  * The FLEXPTR_* variants operate on structs that don't use flex-arrays,
+  * but do want to store a pointer to some extra data in the same allocated
+  * block. For example, if you have:
+  *
+  *   struct foo {
+  *     char *name;
+  *     int bar;
+  *   };
+  *
+  * you can do:
+  *
+  *   struct foo *f;
+  *   FLEX_ALLOC_STR(f, name, src);
+  *
+  * and "name" will point to a block of memory after the struct, which will be
+  * freed along with the struct (but the pointer can be repointed anywhere).
+  *
+  * The *_STR variants accept a string parameter rather than a ptr/len
+  * combination.
+  *
+  * Note that these macros will evaluate the first parameter multiple
+  * times, and it must be assignable as an lvalue.
+  */
+ #define FLEX_ALLOC_MEM(x, flexname, buf, len) do { \
+       (x) = NULL; /* silence -Wuninitialized for offset calculation */ \
+       (x) = xalloc_flex(sizeof(*(x)), (char *)(&((x)->flexname)) - (char *)(x), (buf), (len)); \
+ } while (0)
+ #define FLEXPTR_ALLOC_MEM(x, ptrname, buf, len) do { \
+       (x) = xalloc_flex(sizeof(*(x)), sizeof(*(x)), (buf), (len)); \
+       (x)->ptrname = (void *)((x)+1); \
+ } while(0)
+ #define FLEX_ALLOC_STR(x, flexname, str) \
+       FLEX_ALLOC_MEM((x), flexname, (str), strlen(str))
+ #define FLEXPTR_ALLOC_STR(x, ptrname, str) \
+       FLEXPTR_ALLOC_MEM((x), ptrname, (str), strlen(str))
+ static inline void *xalloc_flex(size_t base_len, size_t offset,
+                               const void *src, size_t src_len)
+ {
+       unsigned char *ret = xcalloc(1, st_add3(base_len, src_len, 1));
+       memcpy(ret + offset, src, src_len);
+       return ret;
+ }
  
  static inline char *xstrdup_or_null(const char *str)
  {
diff --combined git.c
index 9bc04fd5285ff8c16d92554e0248e90f416089e7,e61a59c0145af8d24575e11d38ff91910b2cae8a..6cc0c077f9761f9b56e5fb7e666722b437c4fae4
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -25,14 -25,14 +25,14 @@@ static const char *env_names[] = 
        GIT_PREFIX_ENVIRONMENT
  };
  static char *orig_env[4];
 -static int saved_environment;
 +static int save_restore_env_balance;
  
 -static void save_env(void)
 +static void save_env_before_alias(void)
  {
        int i;
 -      if (saved_environment)
 -              return;
 -      saved_environment = 1;
 +
 +      assert(save_restore_env_balance == 0);
 +      save_restore_env_balance = 1;
        orig_cwd = xgetcwd();
        for (i = 0; i < ARRAY_SIZE(env_names); i++) {
                orig_env[i] = getenv(env_names[i]);
        }
  }
  
 -static void restore_env(void)
 +static void restore_env(int external_alias)
  {
        int i;
 -      if (orig_cwd && chdir(orig_cwd))
 +
 +      assert(save_restore_env_balance == 1);
 +      save_restore_env_balance = 0;
 +      if (!external_alias && orig_cwd && chdir(orig_cwd))
                die_errno("could not move to %s", orig_cwd);
        free(orig_cwd);
        for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 -              if (orig_env[i])
 +              if (external_alias &&
 +                  !strcmp(env_names[i], GIT_PREFIX_ENVIRONMENT))
 +                      continue;
 +              if (orig_env[i]) {
                        setenv(env_names[i], orig_env[i], 1);
 -              else
 +                      free(orig_env[i]);
 +              } else {
                        unsetenv(env_names[i]);
 +              }
        }
  }
  
@@@ -234,33 -226,28 +234,29 @@@ static int handle_options(const char **
  static int handle_alias(int *argcp, const char ***argv)
  {
        int envchanged = 0, ret = 0, saved_errno = errno;
 -      const char *subdir;
        int count, option_count;
        const char **new_argv;
        const char *alias_command;
        char *alias_string;
        int unused_nongit;
  
 -      subdir = setup_git_directory_gently(&unused_nongit);
 +      save_env_before_alias();
 +      setup_git_directory_gently(&unused_nongit);
  
        alias_command = (*argv)[0];
        alias_string = alias_lookup(alias_command);
        if (alias_string) {
                if (alias_string[0] == '!') {
-                       const char **alias_argv;
-                       int argc = *argcp, i;
+                       struct child_process child = CHILD_PROCESS_INIT;
  
                        commit_pager_choice();
 +                      restore_env(1);
  
-                       /* build alias_argv */
-                       alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
-                       alias_argv[0] = alias_string + 1;
-                       for (i = 1; i < argc; ++i)
-                               alias_argv[i] = (*argv)[i];
-                       alias_argv[argc] = NULL;
+                       child.use_shell = 1;
+                       argv_array_push(&child.args, alias_string + 1);
+                       argv_array_pushv(&child.args, (*argv) + 1);
  
-                       ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
+                       ret = run_command(&child);
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
  
                ret = 1;
        }
  
 -      if (subdir && chdir(subdir))
 -              die_errno("Cannot change to '%s'", subdir);
 +      restore_env(0);
  
        errno = saved_errno;
  
   * RUN_SETUP for reading from the configuration file.
   */
  #define NEED_WORK_TREE                (1<<3)
 -#define NO_SETUP              (1<<4)
  
  struct cmd_struct {
        const char *cmd;
@@@ -396,7 -385,7 +392,7 @@@ static struct cmd_struct commands[] = 
        { "cherry", cmd_cherry, RUN_SETUP },
        { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
        { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
 -      { "clone", cmd_clone, NO_SETUP },
 +      { "clone", cmd_clone },
        { "column", cmd_column, RUN_SETUP_GENTLY },
        { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
        { "commit-tree", cmd_commit_tree, RUN_SETUP },
        { "hash-object", cmd_hash_object },
        { "help", cmd_help },
        { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
 -      { "init", cmd_init_db, NO_SETUP },
 -      { "init-db", cmd_init_db, NO_SETUP },
 +      { "init", cmd_init_db },
 +      { "init-db", cmd_init_db },
        { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
        { "log", cmd_log, RUN_SETUP },
        { "ls-files", cmd_ls_files, RUN_SETUP },
@@@ -513,25 -502,21 +509,25 @@@ int is_builtin(const char *s
        return !!get_builtin(s);
  }
  
 +#ifdef STRIP_EXTENSION
 +static void strip_extension(const char **argv)
 +{
 +      size_t len;
 +
 +      if (strip_suffix(argv[0], STRIP_EXTENSION, &len))
 +              argv[0] = xmemdupz(argv[0], len);
 +}
 +#else
 +#define strip_extension(cmd)
 +#endif
 +
  static void handle_builtin(int argc, const char **argv)
  {
 -      const char *cmd = argv[0];
 -      int i;
 -      static const char ext[] = STRIP_EXTENSION;
 +      const char *cmd;
        struct cmd_struct *builtin;
  
 -      if (sizeof(ext) > 1) {
 -              i = strlen(argv[0]) - strlen(ext);
 -              if (i > 0 && !strcmp(argv[0] + i, ext)) {
 -                      char *argv0 = xstrdup(argv[0]);
 -                      argv[0] = cmd = argv0;
 -                      argv0[i] = '\0';
 -              }
 -      }
 +      strip_extension(argv);
 +      cmd = argv[0];
  
        /* Turn "git cmd --help" into "git help cmd" */
        if (argc > 1 && !strcmp(argv[1], "--help")) {
        }
  
        builtin = get_builtin(cmd);
 -      if (builtin) {
 -              if (saved_environment && (builtin->option & NO_SETUP))
 -                      restore_env();
 -              else
 -                      exit(run_builtin(builtin, argc, argv));
 -      }
 +      if (builtin)
 +              exit(run_builtin(builtin, argc, argv));
  }
  
  static void execv_dashed_external(const char **argv)
@@@ -585,17 -574,8 +581,17 @@@ static int run_argv(int *argcp, const c
        int done_alias = 0;
  
        while (1) {
 -              /* See if it's a builtin */
 -              handle_builtin(*argcp, *argv);
 +              /*
 +               * If we tried alias and futzed with our environment,
 +               * it no longer is safe to invoke builtins directly in
 +               * general.  We have to spawn them as dashed externals.
 +               *
 +               * NEEDSWORK: if we can figure out cases
 +               * where it is safe to do, we can avoid spawning a new
 +               * process.
 +               */
 +              if (!done_alias)
 +                      handle_builtin(*argcp, *argv);
  
                /* .. then try the external ones */
                execv_dashed_external(*argv);
                 */
                if (done_alias)
                        break;
 -              save_env();
                if (!handle_alias(argcp, argv))
                        break;
                done_alias = 1;
diff --combined notes.c
index c1e503559076398b1edc5fabbaab8f3cac5ac7d6,8b59ccdb1166dfc413035e2699f3eb1dc9bd853e..88cf4747c16724931682e606a2c1fba4c07b2f49
+++ b/notes.c
@@@ -1011,16 -1011,13 +1011,16 @@@ void init_notes(struct notes_tree *t, c
        t->first_non_note = NULL;
        t->prev_non_note = NULL;
        t->ref = xstrdup_or_null(notes_ref);
 +      t->update_ref = (flags & NOTES_INIT_WRITABLE) ? t->ref : NULL;
        t->combine_notes = combine_notes;
        t->initialized = 1;
        t->dirty = 0;
  
        if (flags & NOTES_INIT_EMPTY || !notes_ref ||
 -          read_ref(notes_ref, object_sha1))
 +          get_sha1_treeish(notes_ref, object_sha1))
                return;
 +      if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, object_sha1))
 +              die("Cannot use notes ref %s", notes_ref);
        if (get_tree_entry(object_sha1, "", sha1, &mode))
                die("Failed to read notes tree referenced by %s (%s)",
                    notes_ref, sha1_to_hex(object_sha1));
        load_subtree(t, &root_tree, t->root, 0);
  }
  
 -struct notes_tree **load_notes_trees(struct string_list *refs)
 +struct notes_tree **load_notes_trees(struct string_list *refs, int flags)
  {
        struct string_list_item *item;
        int counter = 0;
        struct notes_tree **trees;
-       trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
+       ALLOC_ARRAY(trees, refs->nr + 1);
        for_each_string_list_item(item, refs) {
                struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
 -              init_notes(t, item->string, combine_notes_ignore, 0);
 +              init_notes(t, item->string, combine_notes_ignore, flags);
                trees[counter++] = t;
        }
        trees[counter] = NULL;
@@@ -1074,7 -1071,7 +1074,7 @@@ void init_display_notes(struct display_
                                                     item->string);
        }
  
 -      display_notes_trees = load_notes_trees(&display_notes_refs);
 +      display_notes_trees = load_notes_trees(&display_notes_refs, 0);
        string_list_clear(&display_notes_refs, 0);
  }
  
@@@ -1306,13 -1303,3 +1306,13 @@@ void expand_notes_ref(struct strbuf *sb
        else
                strbuf_insert(sb, 0, "refs/notes/", 11);
  }
 +
 +void expand_loose_notes_ref(struct strbuf *sb)
 +{
 +      unsigned char object[20];
 +
 +      if (get_sha1(sb->buf, object)) {
 +              /* fallback to expand_notes_ref */
 +              expand_notes_ref(sb);
 +      }
 +}
diff --combined pack-revindex.c
index 155a8a3d69bf7d1c4c72aaa36ad483dae33ca9b5,f6a3613bebe4ae8d398ab3b0ada22ba81b1339ee..96d51c3467b9ef864f62962aa3567247338d2c3a
@@@ -8,13 -8,52 +8,13 @@@
   * size is easily available by examining the pack entry header).  It is
   * also rather expensive to find the sha1 for an object given its offset.
   *
 - * We build a hashtable of existing packs (pack_revindex), and keep reverse
 - * index here -- pack index file is sorted by object name mapping to offset;
 - * this pack_revindex[].revindex array is a list of offset/index_nr pairs
 + * The pack index file is sorted by object name mapping to offset;
 + * this revindex array is a list of offset/index_nr pairs
   * ordered by offset, so if you know the offset of an object, next offset
   * is where its packed representation ends and the index_nr can be used to
   * get the object sha1 from the main index.
   */
  
 -static struct pack_revindex *pack_revindex;
 -static int pack_revindex_hashsz;
 -
 -static int pack_revindex_ix(struct packed_git *p)
 -{
 -      unsigned long ui = (unsigned long)(intptr_t)p;
 -      int i;
 -
 -      ui = ui ^ (ui >> 16); /* defeat structure alignment */
 -      i = (int)(ui % pack_revindex_hashsz);
 -      while (pack_revindex[i].p) {
 -              if (pack_revindex[i].p == p)
 -                      return i;
 -              if (++i == pack_revindex_hashsz)
 -                      i = 0;
 -      }
 -      return -1 - i;
 -}
 -
 -static void init_pack_revindex(void)
 -{
 -      int num;
 -      struct packed_git *p;
 -
 -      for (num = 0, p = packed_git; p; p = p->next)
 -              num++;
 -      if (!num)
 -              return;
 -      pack_revindex_hashsz = num * 11;
 -      pack_revindex = xcalloc(pack_revindex_hashsz, sizeof(*pack_revindex));
 -      for (p = packed_git; p; p = p->next) {
 -              num = pack_revindex_ix(p);
 -              num = - 1 - num;
 -              pack_revindex[num].p = p;
 -      }
 -      /* revindex elements are lazily initialized */
 -}
 -
  /*
   * This is a least-significant-digit radix sort.
   *
@@@ -44,10 -83,14 +44,14 @@@ static void sort_revindex(struct revind
         * keep track of them with alias pointers, always sorting from "from"
         * to "to".
         */
-       struct revindex_entry *tmp = xmalloc(n * sizeof(*tmp));
-       struct revindex_entry *from = entries, *to = tmp;
+       struct revindex_entry *tmp, *from, *to;
        int bits;
-       unsigned *pos = xmalloc(BUCKETS * sizeof(*pos));
+       unsigned *pos;
+       ALLOC_ARRAY(pos, BUCKETS);
+       ALLOC_ARRAY(tmp, n);
+       from = entries;
+       to = tmp;
  
        /*
         * If (max >> bits) is zero, then we know that the radix digit we are
  /*
   * Ordered list of offsets of objects in the pack.
   */
 -static void create_pack_revindex(struct pack_revindex *rix)
 +static void create_pack_revindex(struct packed_git *p)
  {
 -      struct packed_git *p = rix->p;
        unsigned num_ent = p->num_objects;
        unsigned i;
        const char *index = p->index_data;
  
-       p->revindex = xmalloc(sizeof(*p->revindex) * (num_ent + 1));
 -      ALLOC_ARRAY(rix->revindex, num_ent + 1);
++      ALLOC_ARRAY(p->revindex, num_ent + 1);
        index += 4 * 256;
  
        if (p->index_version > 1) {
                for (i = 0; i < num_ent; i++) {
                        uint32_t off = ntohl(*off_32++);
                        if (!(off & 0x80000000)) {
 -                              rix->revindex[i].offset = off;
 +                              p->revindex[i].offset = off;
                        } else {
 -                              rix->revindex[i].offset =
 +                              p->revindex[i].offset =
                                        ((uint64_t)ntohl(*off_64++)) << 32;
 -                              rix->revindex[i].offset |=
 +                              p->revindex[i].offset |=
                                        ntohl(*off_64++);
                        }
 -                      rix->revindex[i].nr = i;
 +                      p->revindex[i].nr = i;
                }
        } else {
                for (i = 0; i < num_ent; i++) {
                        uint32_t hl = *((uint32_t *)(index + 24 * i));
 -                      rix->revindex[i].offset = ntohl(hl);
 -                      rix->revindex[i].nr = i;
 +                      p->revindex[i].offset = ntohl(hl);
 +                      p->revindex[i].nr = i;
                }
        }
  
        /* This knows the pack format -- the 20-byte trailer
         * follows immediately after the last object data.
         */
 -      rix->revindex[num_ent].offset = p->pack_size - 20;
 -      rix->revindex[num_ent].nr = -1;
 -      sort_revindex(rix->revindex, num_ent, p->pack_size);
 +      p->revindex[num_ent].offset = p->pack_size - 20;
 +      p->revindex[num_ent].nr = -1;
 +      sort_revindex(p->revindex, num_ent, p->pack_size);
  }
  
 -struct pack_revindex *revindex_for_pack(struct packed_git *p)
 +void load_pack_revindex(struct packed_git *p)
  {
 -      int num;
 -      struct pack_revindex *rix;
 -
 -      if (!pack_revindex_hashsz)
 -              init_pack_revindex();
 -
 -      num = pack_revindex_ix(p);
 -      if (num < 0)
 -              die("internal error: pack revindex fubar");
 -
 -      rix = &pack_revindex[num];
 -      if (!rix->revindex)
 -              create_pack_revindex(rix);
 -
 -      return rix;
 +      if (!p->revindex)
 +              create_pack_revindex(p);
  }
  
 -int find_revindex_position(struct pack_revindex *pridx, off_t ofs)
 +int find_revindex_position(struct packed_git *p, off_t ofs)
  {
        int lo = 0;
 -      int hi = pridx->p->num_objects + 1;
 -      struct revindex_entry *revindex = pridx->revindex;
 +      int hi = p->num_objects + 1;
 +      struct revindex_entry *revindex = p->revindex;
  
        do {
                unsigned mi = lo + (hi - lo) / 2;
  
  struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
  {
 -      struct pack_revindex *pridx = revindex_for_pack(p);
 -      int pos = find_revindex_position(pridx, ofs);
 +      int pos;
 +
 +      load_pack_revindex(p);
 +      pos = find_revindex_position(p, ofs);
  
        if (pos < 0)
                return NULL;
  
 -      return pridx->revindex + pos;
 +      return p->revindex + pos;
  }
diff --combined ref-filter.c
index d13d002270f3b0a04c2e7b5669dc87dba9132b79,9ccfc51ceadb79fdf376da79741c9693db8513bb..bb79d6b9cc0f5aec16eb1dffb842910ba55e99ab
  
  typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
  
 +struct align {
 +      align_type position;
 +      unsigned int width;
 +};
 +
 +/*
 + * An atom is a valid field atom listed below, possibly prefixed with
 + * a "*" to denote deref_tag().
 + *
 + * We parse given format string and sort specifiers, and make a list
 + * of properties that we need to extract out of objects.  ref_array_item
 + * structure will hold an array of values extracted that can be
 + * indexed with the "atom number", which is an index into this
 + * array.
 + */
 +static struct used_atom {
 +      const char *name;
 +      cmp_type type;
 +      union {
 +              char color[COLOR_MAXLEN];
 +              struct align align;
 +              enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }
 +                      remote_ref;
 +              struct {
 +                      enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;
 +                      unsigned int nlines;
 +              } contents;
 +              enum { O_FULL, O_SHORT } objectname;
 +      } u;
 +} *used_atom;
 +static int used_atom_cnt, need_tagged, need_symref;
 +static int need_color_reset_at_eol;
 +
 +static void color_atom_parser(struct used_atom *atom, const char *color_value)
 +{
 +      if (!color_value)
 +              die(_("expected format: %%(color:<color>)"));
 +      if (color_parse(color_value, atom->u.color) < 0)
 +              die(_("unrecognized color: %%(color:%s)"), color_value);
 +}
 +
 +static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
 +{
 +      if (!arg)
 +              atom->u.remote_ref = RR_NORMAL;
 +      else if (!strcmp(arg, "short"))
 +              atom->u.remote_ref = RR_SHORTEN;
 +      else if (!strcmp(arg, "track"))
 +              atom->u.remote_ref = RR_TRACK;
 +      else if (!strcmp(arg, "trackshort"))
 +              atom->u.remote_ref = RR_TRACKSHORT;
 +      else
 +              die(_("unrecognized format: %%(%s)"), atom->name);
 +}
 +
 +static void body_atom_parser(struct used_atom *atom, const char *arg)
 +{
 +      if (arg)
 +              die("%%(body) does not take arguments");
 +      atom->u.contents.option = C_BODY_DEP;
 +}
 +
 +static void subject_atom_parser(struct used_atom *atom, const char *arg)
 +{
 +      if (arg)
 +              die("%%(subject) does not take arguments");
 +      atom->u.contents.option = C_SUB;
 +}
 +
 +static void contents_atom_parser(struct used_atom *atom, const char *arg)
 +{
 +      if (!arg)
 +              atom->u.contents.option = C_BARE;
 +      else if (!strcmp(arg, "body"))
 +              atom->u.contents.option = C_BODY;
 +      else if (!strcmp(arg, "signature"))
 +              atom->u.contents.option = C_SIG;
 +      else if (!strcmp(arg, "subject"))
 +              atom->u.contents.option = C_SUB;
 +      else if (skip_prefix(arg, "lines=", &arg)) {
 +              atom->u.contents.option = C_LINES;
 +              if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
 +                      die(_("positive value expected contents:lines=%s"), arg);
 +      } else
 +              die(_("unrecognized %%(contents) argument: %s"), arg);
 +}
 +
 +static void objectname_atom_parser(struct used_atom *atom, const char *arg)
 +{
 +      if (!arg)
 +              atom->u.objectname = O_FULL;
 +      else if (!strcmp(arg, "short"))
 +              atom->u.objectname = O_SHORT;
 +      else
 +              die(_("unrecognized %%(objectname) argument: %s"), arg);
 +}
 +
 +static align_type parse_align_position(const char *s)
 +{
 +      if (!strcmp(s, "right"))
 +              return ALIGN_RIGHT;
 +      else if (!strcmp(s, "middle"))
 +              return ALIGN_MIDDLE;
 +      else if (!strcmp(s, "left"))
 +              return ALIGN_LEFT;
 +      return -1;
 +}
 +
 +static void align_atom_parser(struct used_atom *atom, const char *arg)
 +{
 +      struct align *align = &atom->u.align;
 +      struct string_list params = STRING_LIST_INIT_DUP;
 +      int i;
 +      unsigned int width = ~0U;
 +
 +      if (!arg)
 +              die(_("expected format: %%(align:<width>,<position>)"));
 +
 +      align->position = ALIGN_LEFT;
 +
 +      string_list_split(&params, arg, ',', -1);
 +      for (i = 0; i < params.nr; i++) {
 +              const char *s = params.items[i].string;
 +              int position;
 +
 +              if (skip_prefix(s, "position=", &s)) {
 +                      position = parse_align_position(s);
 +                      if (position < 0)
 +                              die(_("unrecognized position:%s"), s);
 +                      align->position = position;
 +              } else if (skip_prefix(s, "width=", &s)) {
 +                      if (strtoul_ui(s, 10, &width))
 +                              die(_("unrecognized width:%s"), s);
 +              } else if (!strtoul_ui(s, 10, &width))
 +                      ;
 +              else if ((position = parse_align_position(s)) >= 0)
 +                      align->position = position;
 +              else
 +                      die(_("unrecognized %%(align) argument: %s"), s);
 +      }
 +
 +      if (width == ~0U)
 +              die(_("positive width expected with the %%(align) atom"));
 +      align->width = width;
 +      string_list_clear(&params, 0);
 +}
 +
  static struct {
        const char *name;
        cmp_type cmp_type;
 +      void (*parser)(struct used_atom *atom, const char *arg);
  } valid_atom[] = {
        { "refname" },
        { "objecttype" },
        { "objectsize", FIELD_ULONG },
 -      { "objectname" },
 +      { "objectname", FIELD_STR, objectname_atom_parser },
        { "tree" },
        { "parent" },
        { "numparent", FIELD_ULONG },
        { "taggerdate", FIELD_TIME },
        { "creator" },
        { "creatordate", FIELD_TIME },
 -      { "subject" },
 -      { "body" },
 -      { "contents" },
 -      { "upstream" },
 -      { "push" },
 +      { "subject", FIELD_STR, subject_atom_parser },
 +      { "body", FIELD_STR, body_atom_parser },
 +      { "contents", FIELD_STR, contents_atom_parser },
 +      { "upstream", FIELD_STR, remote_ref_atom_parser },
 +      { "push", FIELD_STR, remote_ref_atom_parser },
        { "symref" },
        { "flag" },
        { "HEAD" },
 -      { "color" },
 -      { "align" },
 +      { "color", FIELD_STR, color_atom_parser },
 +      { "align", FIELD_STR, align_atom_parser },
        { "end" },
  };
  
  #define REF_FORMATTING_STATE_INIT  { 0, NULL }
  
 -struct align {
 -      align_type position;
 -      unsigned int width;
 -};
 -
 -struct contents {
 -      unsigned int lines;
 -      struct object_id oid;
 -};
 -
  struct ref_formatting_stack {
        struct ref_formatting_stack *prev;
        struct strbuf output;
@@@ -223,18 -85,33 +223,18 @@@ struct atom_value 
        const char *s;
        union {
                struct align align;
 -              struct contents contents;
        } u;
        void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
        unsigned long ul; /* used for sorting when not FIELD_STR */
  };
  
 -/*
 - * An atom is a valid field atom listed above, possibly prefixed with
 - * a "*" to denote deref_tag().
 - *
 - * We parse given format string and sort specifiers, and make a list
 - * of properties that we need to extract out of objects.  ref_array_item
 - * structure will hold an array of values extracted that can be
 - * indexed with the "atom number", which is an index into this
 - * array.
 - */
 -static const char **used_atom;
 -static cmp_type *used_atom_type;
 -static int used_atom_cnt, need_tagged, need_symref;
 -static int need_color_reset_at_eol;
 -
  /*
   * Used to parse format string and sort specifiers
   */
  int parse_ref_filter_atom(const char *atom, const char *ep)
  {
        const char *sp;
 +      const char *arg;
        int i, at;
  
        sp = atom;
  
        /* Do we have the atom already used elsewhere? */
        for (i = 0; i < used_atom_cnt; i++) {
 -              int len = strlen(used_atom[i]);
 -              if (len == ep - atom && !memcmp(used_atom[i], atom, len))
 +              int len = strlen(used_atom[i].name);
 +              if (len == ep - atom && !memcmp(used_atom[i].name, atom, len))
                        return i;
        }
  
        /* Is the atom a valid one? */
        for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
                int len = strlen(valid_atom[i].name);
 +
                /*
                 * If the atom name has a colon, strip it and everything after
                 * it off - it specifies the format for this entry, and
                 * shouldn't be used for checking against the valid_atom
                 * table.
                 */
 -              const char *formatp = strchr(sp, ':');
 -              if (!formatp || ep < formatp)
 -                      formatp = ep;
 -              if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
 +              arg = memchr(sp, ':', ep - sp);
 +              if (len == (arg ? arg : ep) - sp &&
 +                  !memcmp(valid_atom[i].name, sp, len))
                        break;
        }
  
        at = used_atom_cnt;
        used_atom_cnt++;
        REALLOC_ARRAY(used_atom, used_atom_cnt);
 -      REALLOC_ARRAY(used_atom_type, used_atom_cnt);
 -      used_atom[at] = xmemdupz(atom, ep - atom);
 -      used_atom_type[at] = valid_atom[i].cmp_type;
 +      used_atom[at].name = xmemdupz(atom, ep - atom);
 +      used_atom[at].type = valid_atom[i].cmp_type;
 +      if (arg)
 +              arg = used_atom[at].name + (arg - atom) + 1;
 +      memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
 +      if (valid_atom[i].parser)
 +              valid_atom[i].parser(&used_atom[at], arg);
        if (*atom == '*')
                need_tagged = 1;
 -      if (!strcmp(used_atom[at], "symref"))
 +      if (!strcmp(used_atom[at].name, "symref"))
                need_symref = 1;
        return at;
  }
@@@ -385,6 -258,22 +385,6 @@@ static void end_atom_handler(struct ato
        pop_stack_element(&state->stack);
  }
  
 -static int match_atom_name(const char *name, const char *atom_name, const char **val)
 -{
 -      const char *body;
 -
 -      if (!skip_prefix(name, atom_name, &body))
 -              return 0; /* doesn't even begin with "atom_name" */
 -      if (!body[0]) {
 -              *val = NULL; /* %(atom_name) and no customization */
 -              return 1;
 -      }
 -      if (body[0] != ':')
 -              return 0; /* "atom_namefoo" is not "atom_name" or "atom_name:..." */
 -      *val = body + 1; /* "atom_name:val" */
 -      return 1;
 -}
 -
  /*
   * In a format string, find the next occurrence of %(atom).
   */
@@@ -426,7 -315,7 +426,7 @@@ int verify_ref_format(const char *forma
                at = parse_ref_filter_atom(sp + 2, ep);
                cp = ep + 1;
  
 -              if (skip_prefix(used_atom[at], "color:", &color))
 +              if (skip_prefix(used_atom[at].name, "color:", &color))
                        need_color_reset_at_eol = !!strcmp(color, "reset");
        }
        return 0;
@@@ -451,17 -340,15 +451,17 @@@ static void *get_obj(const unsigned cha
  }
  
  static int grab_objectname(const char *name, const unsigned char *sha1,
 -                          struct atom_value *v)
 +                         struct atom_value *v, struct used_atom *atom)
  {
 -      if (!strcmp(name, "objectname")) {
 -              v->s = xstrdup(sha1_to_hex(sha1));
 -              return 1;
 -      }
 -      if (!strcmp(name, "objectname:short")) {
 -              v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
 -              return 1;
 +      if (starts_with(name, "objectname")) {
 +              if (atom->u.objectname == O_SHORT) {
 +                      v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
 +                      return 1;
 +              } else if (atom->u.objectname == O_FULL) {
 +                      v->s = xstrdup(sha1_to_hex(sha1));
 +                      return 1;
 +              } else
 +                      die("BUG: unknown %%(objectname) option");
        }
        return 0;
  }
@@@ -472,7 -359,7 +472,7 @@@ static void grab_common_values(struct a
        int i;
  
        for (i = 0; i < used_atom_cnt; i++) {
 -              const char *name = used_atom[i];
 +              const char *name = used_atom[i].name;
                struct atom_value *v = &val[i];
                if (!!deref != (*name == '*'))
                        continue;
                        v->s = xstrfmt("%lu", sz);
                }
                else if (deref)
 -                      grab_objectname(name, obj->oid.hash, v);
 +                      grab_objectname(name, obj->oid.hash, v, &used_atom[i]);
        }
  }
  
@@@ -496,7 -383,7 +496,7 @@@ static void grab_tag_values(struct atom
        struct tag *tag = (struct tag *) obj;
  
        for (i = 0; i < used_atom_cnt; i++) {
 -              const char *name = used_atom[i];
 +              const char *name = used_atom[i].name;
                struct atom_value *v = &val[i];
                if (!!deref != (*name == '*'))
                        continue;
@@@ -518,7 -405,7 +518,7 @@@ static void grab_commit_values(struct a
        struct commit *commit = (struct commit *) obj;
  
        for (i = 0; i < used_atom_cnt; i++) {
 -              const char *name = used_atom[i];
 +              const char *name = used_atom[i].name;
                struct atom_value *v = &val[i];
                if (!!deref != (*name == '*'))
                        continue;
@@@ -648,7 -535,7 +648,7 @@@ static void grab_person(const char *who
        const char *wholine = NULL;
  
        for (i = 0; i < used_atom_cnt; i++) {
 -              const char *name = used_atom[i];
 +              const char *name = used_atom[i].name;
                struct atom_value *v = &val[i];
                if (!!deref != (*name == '*'))
                        continue;
        if (!wholine)
                return;
        for (i = 0; i < used_atom_cnt; i++) {
 -              const char *name = used_atom[i];
 +              const char *name = used_atom[i].name;
                struct atom_value *v = &val[i];
                if (!!deref != (*name == '*'))
                        continue;
@@@ -776,16 -663,20 +776,16 @@@ static void grab_sub_body_contents(stru
        unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
  
        for (i = 0; i < used_atom_cnt; i++) {
 -              const char *name = used_atom[i];
 +              struct used_atom *atom = &used_atom[i];
 +              const char *name = atom->name;
                struct atom_value *v = &val[i];
 -              const char *valp = NULL;
                if (!!deref != (*name == '*'))
                        continue;
                if (deref)
                        name++;
                if (strcmp(name, "subject") &&
                    strcmp(name, "body") &&
 -                  strcmp(name, "contents") &&
 -                  strcmp(name, "contents:subject") &&
 -                  strcmp(name, "contents:body") &&
 -                  strcmp(name, "contents:signature") &&
 -                  !starts_with(name, "contents:lines="))
 +                  !starts_with(name, "contents"))
                        continue;
                if (!subpos)
                        find_subpos(buf, sz,
                                    &bodypos, &bodylen, &nonsiglen,
                                    &sigpos, &siglen);
  
 -              if (!strcmp(name, "subject"))
 +              if (atom->u.contents.option == C_SUB)
                        v->s = copy_subject(subpos, sublen);
 -              else if (!strcmp(name, "contents:subject"))
 -                      v->s = copy_subject(subpos, sublen);
 -              else if (!strcmp(name, "body"))
 +              else if (atom->u.contents.option == C_BODY_DEP)
                        v->s = xmemdupz(bodypos, bodylen);
 -              else if (!strcmp(name, "contents:body"))
 +              else if (atom->u.contents.option == C_BODY)
                        v->s = xmemdupz(bodypos, nonsiglen);
 -              else if (!strcmp(name, "contents:signature"))
 +              else if (atom->u.contents.option == C_SIG)
                        v->s = xmemdupz(sigpos, siglen);
 -              else if (!strcmp(name, "contents"))
 -                      v->s = xstrdup(subpos);
 -              else if (skip_prefix(name, "contents:lines=", &valp)) {
 +              else if (atom->u.contents.option == C_LINES) {
                        struct strbuf s = STRBUF_INIT;
                        const char *contents_end = bodylen + bodypos - siglen;
  
 -                      if (strtoul_ui(valp, 10, &v->u.contents.lines))
 -                              die(_("positive value expected contents:lines=%s"), valp);
                        /*  Size is the length of the message after removing the signature */
 -                      append_lines(&s, subpos, contents_end - subpos, v->u.contents.lines);
 +                      append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines);
                        v->s = strbuf_detach(&s, NULL);
 -              }
 +              } else if (atom->u.contents.option == C_BARE)
 +                      v->s = xstrdup(subpos);
        }
  }
  
@@@ -890,43 -786,6 +890,43 @@@ static const char *strip_ref_components
        return start;
  }
  
 +static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
 +                                  struct branch *branch, const char **s)
 +{
 +      int num_ours, num_theirs;
 +      if (atom->u.remote_ref == RR_SHORTEN)
 +              *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);
 +      else if (atom->u.remote_ref == RR_TRACK) {
 +              if (stat_tracking_info(branch, &num_ours,
 +                                     &num_theirs, NULL))
 +                      return;
 +
 +              if (!num_ours && !num_theirs)
 +                      *s = "";
 +              else if (!num_ours)
 +                      *s = xstrfmt("[behind %d]", num_theirs);
 +              else if (!num_theirs)
 +                      *s = xstrfmt("[ahead %d]", num_ours);
 +              else
 +                      *s = xstrfmt("[ahead %d, behind %d]",
 +                                   num_ours, num_theirs);
 +      } else if (atom->u.remote_ref == RR_TRACKSHORT) {
 +              if (stat_tracking_info(branch, &num_ours,
 +                                     &num_theirs, NULL))
 +                      return;
 +
 +              if (!num_ours && !num_theirs)
 +                      *s = "=";
 +              else if (!num_ours)
 +                      *s = "<";
 +              else if (!num_theirs)
 +                      *s = ">";
 +              else
 +                      *s = "<>";
 +      } else /* RR_NORMAL */
 +              *s = refname;
 +}
 +
  /*
   * Parse the object referred by ref, and grab needed value.
   */
@@@ -950,12 -809,12 +950,12 @@@ static void populate_value(struct ref_a
  
        /* Fill in specials first */
        for (i = 0; i < used_atom_cnt; i++) {
 -              const char *name = used_atom[i];
 +              struct used_atom *atom = &used_atom[i];
 +              const char *name = used_atom[i].name;
                struct atom_value *v = &ref->value[i];
                int deref = 0;
                const char *refname;
                const char *formatp;
 -              const char *valp;
                struct branch *branch = NULL;
  
                v->handler = append_atom;
                        branch = branch_get(branch_name);
  
                        refname = branch_get_upstream(branch, NULL);
 -                      if (!refname)
 -                              continue;
 +                      if (refname)
 +                              fill_remote_ref_details(atom, refname, branch, &v->s);
 +                      continue;
                } else if (starts_with(name, "push")) {
                        const char *branch_name;
                        if (!skip_prefix(ref->refname, "refs/heads/",
                        refname = branch_get_push(branch, NULL);
                        if (!refname)
                                continue;
 -              } else if (match_atom_name(name, "color", &valp)) {
 -                      char color[COLOR_MAXLEN] = "";
 -
 -                      if (!valp)
 -                              die(_("expected format: %%(color:<color>)"));
 -                      if (color_parse(valp, color) < 0)
 -                              die(_("unable to parse format"));
 -                      v->s = xstrdup(color);
 +                      fill_remote_ref_details(atom, refname, branch, &v->s);
 +                      continue;
 +              } else if (starts_with(name, "color:")) {
 +                      v->s = atom->u.color;
                        continue;
                } else if (!strcmp(name, "flag")) {
                        char buf[256], *cp = buf;
                                v->s = xstrdup(buf + 1);
                        }
                        continue;
 -              } else if (!deref && grab_objectname(name, ref->objectname, v)) {
 +              } else if (!deref && grab_objectname(name, ref->objectname, v, atom)) {
                        continue;
                } else if (!strcmp(name, "HEAD")) {
                        const char *head;
                        else
                                v->s = " ";
                        continue;
 -              } else if (match_atom_name(name, "align", &valp)) {
 -                      struct align *align = &v->u.align;
 -                      struct strbuf **s, **to_free;
 -                      int width = -1;
 -
 -                      if (!valp)
 -                              die(_("expected format: %%(align:<width>,<position>)"));
 -
 -                      /*
 -                       * TODO: Implement a function similar to strbuf_split_str()
 -                       * which would omit the separator from the end of each value.
 -                       */
 -                      s = to_free = strbuf_split_str(valp, ',', 0);
 -
 -                      align->position = ALIGN_LEFT;
 -
 -                      while (*s) {
 -                              /*  Strip trailing comma */
 -                              if (s[1])
 -                                      strbuf_setlen(s[0], s[0]->len - 1);
 -                              if (!strtoul_ui(s[0]->buf, 10, (unsigned int *)&width))
 -                                      ;
 -                              else if (!strcmp(s[0]->buf, "left"))
 -                                      align->position = ALIGN_LEFT;
 -                              else if (!strcmp(s[0]->buf, "right"))
 -                                      align->position = ALIGN_RIGHT;
 -                              else if (!strcmp(s[0]->buf, "middle"))
 -                                      align->position = ALIGN_MIDDLE;
 -                              else
 -                                      die(_("improper format entered align:%s"), s[0]->buf);
 -                              s++;
 -                      }
 -
 -                      if (width < 0)
 -                              die(_("positive width expected with the %%(align) atom"));
 -                      align->width = width;
 -                      strbuf_list_free(to_free);
 +              } else if (starts_with(name, "align")) {
 +                      v->u.align = atom->u.align;
                        v->handler = align_atom_handler;
                        continue;
                } else if (!strcmp(name, "end")) {
  
                formatp = strchr(name, ':');
                if (formatp) {
 -                      int num_ours, num_theirs;
                        const char *arg;
  
                        formatp++;
                                                      warn_ambiguous_refs);
                        else if (skip_prefix(formatp, "strip=", &arg))
                                refname = strip_ref_components(refname, arg);
 -                      else if (!strcmp(formatp, "track") &&
 -                               (starts_with(name, "upstream") ||
 -                                starts_with(name, "push"))) {
 -
 -                              if (stat_tracking_info(branch, &num_ours,
 -                                                     &num_theirs, NULL))
 -                                      continue;
 -
 -                              if (!num_ours && !num_theirs)
 -                                      v->s = "";
 -                              else if (!num_ours)
 -                                      v->s = xstrfmt("[behind %d]", num_theirs);
 -                              else if (!num_theirs)
 -                                      v->s = xstrfmt("[ahead %d]", num_ours);
 -                              else
 -                                      v->s = xstrfmt("[ahead %d, behind %d]",
 -                                                     num_ours, num_theirs);
 -                              continue;
 -                      } else if (!strcmp(formatp, "trackshort") &&
 -                                 (starts_with(name, "upstream") ||
 -                                  starts_with(name, "push"))) {
 -                              assert(branch);
 -
 -                              if (stat_tracking_info(branch, &num_ours,
 -                                                      &num_theirs, NULL))
 -                                      continue;
 -
 -                              if (!num_ours && !num_theirs)
 -                                      v->s = "=";
 -                              else if (!num_ours)
 -                                      v->s = "<";
 -                              else if (!num_theirs)
 -                                      v->s = ">";
 -                              else
 -                                      v->s = "<>";
 -                              continue;
 -                      } else
 +                      else
                                die("unknown %.*s format %s",
                                    (int)(formatp - name), name, formatp);
                }
@@@ -1321,10 -1255,8 +1321,8 @@@ static struct ref_array_item *new_ref_a
                                                 const unsigned char *objectname,
                                                 int flag)
  {
-       size_t len = strlen(refname);
-       struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);
-       memcpy(ref->refname, refname, len);
-       ref->refname[len] = '\0';
+       struct ref_array_item *ref;
+       FLEX_ALLOC_STR(ref, refname, refname);
        hashcpy(ref->objectname, objectname);
        ref->flag = flag;
  
@@@ -1537,7 -1469,7 +1535,7 @@@ static int cmp_ref_sorting(struct ref_s
  {
        struct atom_value *va, *vb;
        int cmp;
 -      cmp_type cmp_type = used_atom_type[s->atom];
 +      cmp_type cmp_type = used_atom[s->atom].type;
  
        get_ref_atom_value(a, s->atom, &va);
        get_ref_atom_value(b, s->atom, &vb);
diff --combined refs/files-backend.c
index b56976288819c87307c03e4e5abf9b9458d09970,de9af1615cc4f1aaf0c71c7050f57adc908dde85..81f68f846b69af65badfbe8c25d4a03601fb23c8
@@@ -199,17 -199,14 +199,14 @@@ static struct ref_entry *create_ref_ent
                                          const unsigned char *sha1, int flag,
                                          int check_name)
  {
-       int len;
        struct ref_entry *ref;
  
        if (check_name &&
            check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
                die("Reference has invalid format: '%s'", refname);
-       len = strlen(refname) + 1;
-       ref = xmalloc(sizeof(struct ref_entry) + len);
+       FLEX_ALLOC_STR(ref, name, refname);
        hashcpy(ref->u.value.oid.hash, sha1);
        oidclr(&ref->u.value.peeled);
-       memcpy(ref->name, refname, len);
        ref->flag = flag;
        return ref;
  }
@@@ -268,9 -265,7 +265,7 @@@ static struct ref_entry *create_dir_ent
                                          int incomplete)
  {
        struct ref_entry *direntry;
-       direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1);
-       memcpy(direntry->name, dirname, len);
-       direntry->name[len] = '\0';
+       FLEX_ALLOC_MEM(direntry, name, dirname, len);
        direntry->u.subdir.ref_cache = ref_cache;
        direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
        return direntry;
@@@ -939,13 -934,10 +934,10 @@@ static void clear_loose_ref_cache(struc
   */
  static struct ref_cache *create_ref_cache(const char *submodule)
  {
-       int len;
        struct ref_cache *refs;
        if (!submodule)
                submodule = "";
-       len = strlen(submodule) + 1;
-       refs = xcalloc(1, sizeof(struct ref_cache) + len);
-       memcpy(refs->name, submodule, len);
+       FLEX_ALLOC_STR(refs, name, submodule);
        refs->next = submodule_ref_caches;
        submodule_ref_caches = refs;
        return refs;
@@@ -1858,17 -1850,12 +1850,17 @@@ static int verify_lock(struct ref_lock 
        if (read_ref_full(lock->ref_name,
                          mustexist ? RESOLVE_REF_READING : 0,
                          lock->old_oid.hash, NULL)) {
 -              int save_errno = errno;
 -              strbuf_addf(err, "can't verify ref %s", lock->ref_name);
 -              errno = save_errno;
 -              return -1;
 +              if (old_sha1) {
 +                      int save_errno = errno;
 +                      strbuf_addf(err, "can't verify ref %s", lock->ref_name);
 +                      errno = save_errno;
 +                      return -1;
 +              } else {
 +                      hashclr(lock->old_oid.hash);
 +                      return 0;
 +              }
        }
 -      if (hashcmp(lock->old_oid.hash, old_sha1)) {
 +      if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) {
                strbuf_addf(err, "ref %s is at %s but expected %s",
                            lock->ref_name,
                            sha1_to_hex(lock->old_oid.hash),
@@@ -1905,8 -1892,7 +1897,8 @@@ static struct ref_lock *lock_ref_sha1_b
        const char *orig_refname = refname;
        struct ref_lock *lock;
        int last_errno = 0;
 -      int type, lflags;
 +      int type;
 +      int lflags = 0;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
        int resolve_flags = 0;
        int attempts_remaining = 3;
  
        if (mustexist)
                resolve_flags |= RESOLVE_REF_READING;
 -      if (flags & REF_DELETING) {
 +      if (flags & REF_DELETING)
                resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
 -              if (flags & REF_NODEREF)
 -                      resolve_flags |= RESOLVE_REF_NO_RECURSE;
 +      if (flags & REF_NODEREF) {
 +              resolve_flags |= RESOLVE_REF_NO_RECURSE;
 +              lflags |= LOCK_NO_DEREF;
        }
  
        refname = resolve_ref_unsafe(refname, resolve_flags,
  
                goto error_return;
        }
 +
 +      if (flags & REF_NODEREF)
 +              refname = orig_refname;
 +
        /*
         * If the ref did not exist and we are creating it, make sure
         * there is no existing packed ref whose name begins with our
  
        lock->lk = xcalloc(1, sizeof(struct lock_file));
  
 -      lflags = 0;
 -      if (flags & REF_NODEREF) {
 -              refname = orig_refname;
 -              lflags |= LOCK_NO_DEREF;
 -      }
        lock->ref_name = xstrdup(refname);
        lock->orig_ref_name = xstrdup(orig_refname);
        strbuf_git_path(&ref_file, "%s", refname);
                        goto error_return;
                }
        }
 -      if (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) {
 +      if (verify_lock(lock, old_sha1, mustexist, err)) {
                last_errno = errno;
                goto error_return;
        }
@@@ -2197,10 -2183,9 +2189,9 @@@ static int pack_if_possible_fn(struct r
  
        /* Schedule the loose reference for pruning if requested. */
        if ((cb->flags & PACK_REFS_PRUNE)) {
-               int namelen = strlen(entry->name) + 1;
-               struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
+               struct ref_to_prune *n;
+               FLEX_ALLOC_STR(n, name, entry->name);
                hashcpy(n->sha1, entry->u.value.oid.hash);
-               memcpy(n->name, entry->name, namelen); /* includes NUL */
                n->next = cb->ref_to_prune;
                cb->ref_to_prune = n;
        }
@@@ -2835,72 -2820,73 +2826,72 @@@ static int commit_ref_update(struct ref
        return 0;
  }
  
 -int create_symref(const char *ref_target, const char *refs_heads_master,
 -                const char *logmsg)
 +static int create_ref_symlink(struct ref_lock *lock, const char *target)
  {
 -      char *lockpath = NULL;
 -      char ref[1000];
 -      int fd, len, written;
 -      char *git_HEAD = git_pathdup("%s", ref_target);
 -      unsigned char old_sha1[20], new_sha1[20];
 -      struct strbuf err = STRBUF_INIT;
 -
 -      if (logmsg && read_ref(ref_target, old_sha1))
 -              hashclr(old_sha1);
 -
 -      if (safe_create_leading_directories(git_HEAD) < 0)
 -              return error("unable to create directory for %s", git_HEAD);
 -
 +      int ret = -1;
  #ifndef NO_SYMLINK_HEAD
 -      if (prefer_symlink_refs) {
 -              unlink(git_HEAD);
 -              if (!symlink(refs_heads_master, git_HEAD))
 -                      goto done;
 +      char *ref_path = get_locked_file_path(lock->lk);
 +      unlink(ref_path);
 +      ret = symlink(target, ref_path);
 +      free(ref_path);
 +
 +      if (ret)
                fprintf(stderr, "no symlink - falling back to symbolic ref\n");
 -      }
  #endif
 +      return ret;
 +}
  
 -      len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
 -      if (sizeof(ref) <= len) {
 -              error("refname too long: %s", refs_heads_master);
 -              goto error_free_return;
 -      }
 -      lockpath = mkpathdup("%s.lock", git_HEAD);
 -      fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
 -      if (fd < 0) {
 -              error("Unable to open %s for writing", lockpath);
 -              goto error_free_return;
 -      }
 -      written = write_in_full(fd, ref, len);
 -      if (close(fd) != 0 || written != len) {
 -              error("Unable to write to %s", lockpath);
 -              goto error_unlink_return;
 -      }
 -      if (rename(lockpath, git_HEAD) < 0) {
 -              error("Unable to create %s", git_HEAD);
 -              goto error_unlink_return;
 -      }
 -      if (adjust_shared_perm(git_HEAD)) {
 -              error("Unable to fix permissions on %s", lockpath);
 -      error_unlink_return:
 -              unlink_or_warn(lockpath);
 -      error_free_return:
 -              free(lockpath);
 -              free(git_HEAD);
 -              return -1;
 +static void update_symref_reflog(struct ref_lock *lock, const char *refname,
 +                               const char *target, const char *logmsg)
 +{
 +      struct strbuf err = STRBUF_INIT;
 +      unsigned char new_sha1[20];
 +      if (logmsg && !read_ref(target, new_sha1) &&
 +          log_ref_write(refname, lock->old_oid.hash, new_sha1, logmsg, 0, &err)) {
 +              error("%s", err.buf);
 +              strbuf_release(&err);
        }
 -      free(lockpath);
 +}
  
 -#ifndef NO_SYMLINK_HEAD
 -      done:
 -#endif
 -      if (logmsg && !read_ref(refs_heads_master, new_sha1) &&
 -              log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) {
 +static int create_symref_locked(struct ref_lock *lock, const char *refname,
 +                              const char *target, const char *logmsg)
 +{
 +      if (prefer_symlink_refs && !create_ref_symlink(lock, target)) {
 +              update_symref_reflog(lock, refname, target, logmsg);
 +              return 0;
 +      }
 +
 +      if (!fdopen_lock_file(lock->lk, "w"))
 +              return error("unable to fdopen %s: %s",
 +                           lock->lk->tempfile.filename.buf, strerror(errno));
 +
 +      update_symref_reflog(lock, refname, target, logmsg);
 +
 +      /* no error check; commit_ref will check ferror */
 +      fprintf(lock->lk->tempfile.fp, "ref: %s\n", target);
 +      if (commit_ref(lock) < 0)
 +              return error("unable to write symref for %s: %s", refname,
 +                           strerror(errno));
 +      return 0;
 +}
 +
 +int create_symref(const char *refname, const char *target, const char *logmsg)
 +{
 +      struct strbuf err = STRBUF_INIT;
 +      struct ref_lock *lock;
 +      int ret;
 +
 +      lock = lock_ref_sha1_basic(refname, NULL, NULL, NULL, REF_NODEREF, NULL,
 +                                 &err);
 +      if (!lock) {
                error("%s", err.buf);
                strbuf_release(&err);
 +              return -1;
        }
  
 -      free(git_HEAD);
 -      return 0;
 +      ret = create_symref_locked(lock, refname, target, logmsg);
 +      unlock_ref(lock);
 +      return ret;
  }
  
  int reflog_exists(const char *refname)
diff --combined remote-curl.c
index 2e2266b856afa920d798f9ae9ee3238c681f9380,e85333a51b0b6a88bfcb212d40d39802eca74a1d..15e48e25fb9fb8cd2e9e3e7a63cf08d2f9483ea2
@@@ -119,19 -119,6 +119,19 @@@ static int set_option(const char *name
                else
                        return -1;
                return 0;
 +
 +#if LIBCURL_VERSION_NUM >= 0x070a08
 +      } else if (!strcmp(name, "family")) {
 +              if (!strcmp(value, "ipv4"))
 +                      git_curl_ipresolve = CURL_IPRESOLVE_V4;
 +              else if (!strcmp(value, "ipv6"))
 +                      git_curl_ipresolve = CURL_IPRESOLVE_V6;
 +              else if (!strcmp(value, "all"))
 +                      git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
 +              else
 +                      return -1;
 +              return 0;
 +#endif /* LIBCURL_VERSION_NUM >= 0x070a08 */
        } else {
                return 1 /* unsupported */;
        }
@@@ -452,20 -439,8 +452,20 @@@ static int run_slot(struct active_reque
        err = run_one_slot(slot, results);
  
        if (err != HTTP_OK && err != HTTP_REAUTH) {
 -              error("RPC failed; result=%d, HTTP code = %ld",
 -                    results->curl_result, results->http_code);
 +              struct strbuf msg = STRBUF_INIT;
 +              if (results->http_code && results->http_code != 200)
 +                      strbuf_addf(&msg, "HTTP %ld", results->http_code);
 +              if (results->curl_result != CURLE_OK) {
 +                      if (msg.len)
 +                              strbuf_addch(&msg, ' ');
 +                      strbuf_addf(&msg, "curl %d", results->curl_result);
 +                      if (curl_errorstr[0]) {
 +                              strbuf_addch(&msg, ' ');
 +                              strbuf_addstr(&msg, curl_errorstr);
 +                      }
 +              }
 +              error("RPC failed; %s", msg.buf);
 +              strbuf_release(&msg);
        }
  
        return err;
@@@ -721,9 -696,10 +721,10 @@@ static int rpc_service(struct rpc_stat
  static int fetch_dumb(int nr_heads, struct ref **to_fetch)
  {
        struct walker *walker;
-       char **targets = xmalloc(nr_heads * sizeof(char*));
+       char **targets;
        int ret, i;
  
+       ALLOC_ARRAY(targets, nr_heads);
        if (options.depth)
                die("dumb http transport does not support --depth");
        for (i = 0; i < nr_heads; i++)
@@@ -852,7 -828,7 +853,7 @@@ static void parse_fetch(struct strbuf *
                        die("http transport does not support %s", buf->buf);
  
                strbuf_reset(buf);
 -              if (strbuf_getline(buf, stdin, '\n') == EOF)
 +              if (strbuf_getline_lf(buf, stdin) == EOF)
                        return;
                if (!*buf->buf)
                        break;
  
  static int push_dav(int nr_spec, char **specs)
  {
-       const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
-       int argc = 0, i;
+       struct child_process child = CHILD_PROCESS_INIT;
+       size_t i;
  
-       argv[argc++] = "http-push";
-       argv[argc++] = "--helper-status";
+       child.git_cmd = 1;
+       argv_array_push(&child.args, "http-push");
+       argv_array_push(&child.args, "--helper-status");
        if (options.dry_run)
-               argv[argc++] = "--dry-run";
+               argv_array_push(&child.args, "--dry-run");
        if (options.verbosity > 1)
-               argv[argc++] = "--verbose";
-       argv[argc++] = url.buf;
+               argv_array_push(&child.args, "--verbose");
+       argv_array_push(&child.args, url.buf);
        for (i = 0; i < nr_spec; i++)
-               argv[argc++] = specs[i];
-       argv[argc++] = NULL;
+               argv_array_push(&child.args, specs[i]);
  
-       if (run_command_v_opt(argv, RUN_GIT_CMD))
-               die("git-%s failed", argv[0]);
-       free(argv);
+       if (run_command(&child))
+               die("git-http-push failed");
        return 0;
  }
  
@@@ -965,7 -940,7 +965,7 @@@ static void parse_push(struct strbuf *b
                        die("http transport does not support %s", buf->buf);
  
                strbuf_reset(buf);
 -              if (strbuf_getline(buf, stdin, '\n') == EOF)
 +              if (strbuf_getline_lf(buf, stdin) == EOF)
                        goto free_specs;
                if (!*buf->buf)
                        break;
@@@ -1015,7 -990,7 +1015,7 @@@ int main(int argc, const char **argv
        do {
                const char *arg;
  
 -              if (strbuf_getline(&buf, stdin, '\n') == EOF) {
 +              if (strbuf_getline_lf(&buf, stdin) == EOF) {
                        if (ferror(stdin))
                                error("remote-curl: error reading command stream from git");
                        return 1;
diff --combined remote.c
index f0016811059b60d6d1a683b8d94874e8599329f6,f182382c834ee93f8ddc4b78750b6ce2ac5fa4f7..fc02698587c61d230200272ce4704403e1a6741f
+++ b/remote.c
@@@ -256,7 -256,7 +256,7 @@@ static void read_remotes_file(struct re
        if (!f)
                return;
        remote->origin = REMOTE_REMOTES;
 -      while (strbuf_getline(&buf, f, '\n') != EOF) {
 +      while (strbuf_getline(&buf, f) != EOF) {
                const char *v;
  
                strbuf_rtrim(&buf);
@@@ -281,7 -281,7 +281,7 @@@ static void read_branches_file(struct r
        if (!f)
                return;
  
 -      strbuf_getline(&buf, f, '\n');
 +      strbuf_getline_lf(&buf, f);
        fclose(f);
        strbuf_trim(&buf);
        if (!buf.len) {
  static int handle_config(const char *key, const char *value, void *cb)
  {
        const char *name;
 +      int namelen;
        const char *subkey;
        struct remote *remote;
        struct branch *branch;
 -      if (starts_with(key, "branch.")) {
 -              name = key + 7;
 -              subkey = strrchr(name, '.');
 -              if (!subkey)
 +      if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
 +              if (!name)
                        return 0;
 -              branch = make_branch(name, subkey - name);
 -              if (!strcmp(subkey, ".remote")) {
 +              branch = make_branch(name, namelen);
 +              if (!strcmp(subkey, "remote")) {
                        return git_config_string(&branch->remote_name, key, value);
 -              } else if (!strcmp(subkey, ".pushremote")) {
 +              } else if (!strcmp(subkey, "pushremote")) {
                        return git_config_string(&branch->pushremote_name, key, value);
 -              } else if (!strcmp(subkey, ".merge")) {
 +              } else if (!strcmp(subkey, "merge")) {
                        if (!value)
                                return config_error_nonbool(key);
                        add_merge(branch, xstrdup(value));
                }
                return 0;
        }
 -      if (starts_with(key, "url.")) {
 +      if (parse_config_key(key, "url", &name, &namelen, &subkey) >= 0) {
                struct rewrite *rewrite;
 -              name = key + 4;
 -              subkey = strrchr(name, '.');
 -              if (!subkey)
 +              if (!name)
                        return 0;
 -              if (!strcmp(subkey, ".insteadof")) {
 -                      rewrite = make_rewrite(&rewrites, name, subkey - name);
 +              if (!strcmp(subkey, "insteadof")) {
 +                      rewrite = make_rewrite(&rewrites, name, namelen);
                        if (!value)
                                return config_error_nonbool(key);
                        add_instead_of(rewrite, xstrdup(value));
 -              } else if (!strcmp(subkey, ".pushinsteadof")) {
 -                      rewrite = make_rewrite(&rewrites_push, name, subkey - name);
 +              } else if (!strcmp(subkey, "pushinsteadof")) {
 +                      rewrite = make_rewrite(&rewrites_push, name, namelen);
                        if (!value)
                                return config_error_nonbool(key);
                        add_instead_of(rewrite, xstrdup(value));
                }
        }
  
 -      if (!starts_with(key,  "remote."))
 +      if (parse_config_key(key, "remote", &name, &namelen, &subkey) < 0)
                return 0;
 -      name = key + 7;
  
        /* Handle remote.* variables */
 -      if (!strcmp(name, "pushdefault"))
 +      if (!name && !strcmp(subkey, "pushdefault"))
                return git_config_string(&pushremote_name, key, value);
  
 +      if (!name)
 +              return 0;
        /* Handle remote.<name>.* variables */
        if (*name == '/') {
                warning("Config remote shorthand cannot begin with '/': %s",
                        name);
                return 0;
        }
 -      subkey = strrchr(name, '.');
 -      if (!subkey)
 -              return 0;
 -      remote = make_remote(name, subkey - name);
 +      remote = make_remote(name, namelen);
        remote->origin = REMOTE_CONFIG;
 -      if (!strcmp(subkey, ".mirror"))
 +      if (!strcmp(subkey, "mirror"))
                remote->mirror = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".skipdefaultupdate"))
 +      else if (!strcmp(subkey, "skipdefaultupdate"))
                remote->skip_default_update = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".skipfetchall"))
 +      else if (!strcmp(subkey, "skipfetchall"))
                remote->skip_default_update = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".prune"))
 +      else if (!strcmp(subkey, "prune"))
                remote->prune = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".url")) {
 +      else if (!strcmp(subkey, "url")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_url(remote, v);
 -      } else if (!strcmp(subkey, ".pushurl")) {
 +      } else if (!strcmp(subkey, "pushurl")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_pushurl(remote, v);
 -      } else if (!strcmp(subkey, ".push")) {
 +      } else if (!strcmp(subkey, "push")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_push_refspec(remote, v);
 -      } else if (!strcmp(subkey, ".fetch")) {
 +      } else if (!strcmp(subkey, "fetch")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_fetch_refspec(remote, v);
 -      } else if (!strcmp(subkey, ".receivepack")) {
 +      } else if (!strcmp(subkey, "receivepack")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                        remote->receivepack = v;
                else
                        error("more than one receivepack given, using the first");
 -      } else if (!strcmp(subkey, ".uploadpack")) {
 +      } else if (!strcmp(subkey, "uploadpack")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                        remote->uploadpack = v;
                else
                        error("more than one uploadpack given, using the first");
 -      } else if (!strcmp(subkey, ".tagopt")) {
 +      } else if (!strcmp(subkey, "tagopt")) {
                if (!strcmp(value, "--no-tags"))
                        remote->fetch_tags = -1;
                else if (!strcmp(value, "--tags"))
                        remote->fetch_tags = 2;
 -      } else if (!strcmp(subkey, ".proxy")) {
 +      } else if (!strcmp(subkey, "proxy")) {
                return git_config_string((const char **)&remote->http_proxy,
                                         key, value);
 -      } else if (!strcmp(subkey, ".vcs")) {
 +      } else if (!strcmp(subkey, "proxyauthmethod")) {
 +              return git_config_string((const char **)&remote->http_proxy_authmethod,
 +                                       key, value);
 +      } else if (!strcmp(subkey, "vcs")) {
                return git_config_string(&remote->foreign_vcs, key, value);
        }
        return 0;
@@@ -713,9 -715,18 +713,9 @@@ struct remote *pushremote_get(const cha
        return remote_get_1(name, pushremote_for_branch);
  }
  
 -int remote_is_configured(const char *name)
 +int remote_is_configured(struct remote *remote)
  {
 -      struct remotes_hash_key lookup;
 -      struct hashmap_entry lookup_entry;
 -      read_config();
 -
 -      init_remotes_hash();
 -      lookup.str = name;
 -      lookup.len = strlen(name);
 -      hashmap_entry_init(&lookup_entry, memhash(name, lookup.len));
 -
 -      return hashmap_get(&remotes_hash, &lookup_entry, &lookup) != NULL;
 +      return remote && remote->origin;
  }
  
  int for_each_remote(each_remote_fn fn, void *priv)
@@@ -917,7 -928,7 +917,7 @@@ static struct ref *alloc_ref_with_prefi
                const char *name)
  {
        size_t len = strlen(name);
-       struct ref *ref = xcalloc(1, sizeof(struct ref) + prefixlen + len + 1);
+       struct ref *ref = xcalloc(1, st_add4(sizeof(*ref), prefixlen, len, 1));
        memcpy(ref->name, prefix, prefixlen);
        memcpy(ref->name + prefixlen, name, len);
        return ref;
@@@ -934,9 -945,9 +934,9 @@@ struct ref *copy_ref(const struct ref *
        size_t len;
        if (!ref)
                return NULL;
-       len = strlen(ref->name);
-       cpy = xmalloc(sizeof(struct ref) + len + 1);
-       memcpy(cpy, ref, sizeof(struct ref) + len + 1);
+       len = st_add3(sizeof(struct ref), strlen(ref->name), 1);
+       cpy = xmalloc(len);
+       memcpy(cpy, ref, len);
        cpy->next = NULL;
        cpy->symref = xstrdup_or_null(ref->symref);
        cpy->remote_status = xstrdup_or_null(ref->remote_status);
@@@ -1534,8 -1545,11 +1534,8 @@@ void set_ref_status_for_push(struct re
                }
  
                /*
 -               * Bypass the usual "must fast-forward" check but
 -               * replace it with a weaker "the old value must be
 -               * this value we observed".  If the remote ref has
 -               * moved and is now different from what we expect,
 -               * reject any push.
 +               * If the remote ref has moved and is now different
 +               * from what we expect, reject any push.
                 *
                 * It also is an error if the user told us to check
                 * with the remote-tracking branch to find the value
                        if (ref->expect_old_no_trackback ||
                            oidcmp(&ref->old_oid, &ref->old_oid_expect))
                                reject_reason = REF_STATUS_REJECT_STALE;
 +                      else
 +                              /* If the ref isn't stale then force the update. */
 +                              force_ref_update = 1;
                }
  
                /*
 -               * The usual "must fast-forward" rules.
 +               * If the update isn't already rejected then check
 +               * the usual "must fast-forward" rules.
                 *
                 * Decide whether an individual refspec A:B can be
                 * pushed.  The push will succeed if any of the
                 *     passing the --force argument
                 */
  
 -              else if (!ref->deletion && !is_null_oid(&ref->old_oid)) {
 +              if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) {
                        if (starts_with(ref->name, "refs/tags/"))
                                reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
                        else if (!has_object_file(&ref->old_oid))
@@@ -2122,16 -2132,13 +2122,13 @@@ static int one_local_ref(const char *re
  {
        struct ref ***local_tail = cb_data;
        struct ref *ref;
-       int len;
  
        /* we already know it starts with refs/ to get here */
        if (check_refname_format(refname + 5, 0))
                return 0;
  
-       len = strlen(refname) + 1;
-       ref = xcalloc(1, sizeof(*ref) + len);
+       ref = alloc_ref(refname);
        oidcpy(&ref->new_oid, oid);
-       memcpy(ref->name, refname, len);
        **local_tail = ref;
        *local_tail = &ref->next;
        return 0;
diff --combined revision.c
index 82f3ca44b3e3dba8bdd487be04bd66b862fefa4c,df56fcea0e12a11a35414fc5e1c14d1675436671..8b2dfe3160784f9780cf541a674fe89100d38a93
@@@ -25,13 -25,69 +25,13 @@@ volatile show_early_output_fn_t show_ea
  static const char *term_bad;
  static const char *term_good;
  
 -char *path_name(const struct name_path *path, const char *name)
 +void show_object_with_name(FILE *out, struct object *obj, const char *name)
  {
 -      const struct name_path *p;
 -      char *n, *m;
 -      int nlen = strlen(name);
 -      int len = nlen + 1;
 -
 -      for (p = path; p; p = p->up) {
 -              if (p->elem_len)
 -                      len += p->elem_len + 1;
 -      }
 -      n = xmalloc(len);
 -      m = n + len - (nlen + 1);
 -      memcpy(m, name, nlen + 1);
 -      for (p = path; p; p = p->up) {
 -              if (p->elem_len) {
 -                      m -= p->elem_len + 1;
 -                      memcpy(m, p->elem, p->elem_len);
 -                      m[p->elem_len] = '/';
 -              }
 -      }
 -      return n;
 -}
 -
 -static int show_path_component_truncated(FILE *out, const char *name, int len)
 -{
 -      int cnt;
 -      for (cnt = 0; cnt < len; cnt++) {
 -              int ch = name[cnt];
 -              if (!ch || ch == '\n')
 -                      return -1;
 -              fputc(ch, out);
 -      }
 -      return len;
 -}
 -
 -static int show_path_truncated(FILE *out, const struct name_path *path)
 -{
 -      int emitted, ours;
 -
 -      if (!path)
 -              return 0;
 -      emitted = show_path_truncated(out, path->up);
 -      if (emitted < 0)
 -              return emitted;
 -      if (emitted)
 -              fputc('/', out);
 -      ours = show_path_component_truncated(out, path->elem, path->elem_len);
 -      if (ours < 0)
 -              return ours;
 -      return ours || emitted;
 -}
 -
 -void show_object_with_name(FILE *out, struct object *obj,
 -                         const struct name_path *path, const char *component)
 -{
 -      struct name_path leaf;
 -      leaf.up = (struct name_path *)path;
 -      leaf.elem = component;
 -      leaf.elem_len = strlen(component);
 +      const char *p;
  
        fprintf(out, "%s ", oid_to_hex(&obj->oid));
 -      show_path_truncated(out, &leaf);
 +      for (p = name; *p && *p != '\n'; p++)
 +              fputc(*p, out);
        fputc('\n', out);
  }
  
@@@ -484,7 -540,7 +484,7 @@@ struct treesame_state 
  static struct treesame_state *initialise_treesame(struct rev_info *revs, struct commit *commit)
  {
        unsigned n = commit_list_count(commit->parents);
-       struct treesame_state *st = xcalloc(1, sizeof(*st) + n);
+       struct treesame_state *st = xcalloc(1, st_add(sizeof(*st), n));
        st->nparents = n;
        add_decoration(&revs->treesame, &commit->object, st);
        return st;
@@@ -1579,7 -1635,10 +1579,7 @@@ static void append_prune_data(struct cm
  static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb,
                                     struct cmdline_pathspec *prune)
  {
 -      while (strbuf_getwholeline(sb, stdin, '\n') != EOF) {
 -              int len = sb->len;
 -              if (len && sb->buf[len - 1] == '\n')
 -                      sb->buf[--len] = '\0';
 +      while (strbuf_getline(sb, stdin) != EOF) {
                ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc);
                prune->path[prune->nr++] = xstrdup(sb->buf);
        }
@@@ -1596,8 -1655,10 +1596,8 @@@ static void read_revisions_from_stdin(s
        warn_on_object_refname_ambiguity = 0;
  
        strbuf_init(&sb, 1000);
 -      while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
 +      while (strbuf_getline(&sb, stdin) != EOF) {
                int len = sb.len;
 -              if (len && sb.buf[len - 1] == '\n')
 -                      sb.buf[--len] = '\0';
                if (!len)
                        break;
                if (sb.buf[0] == '-') {
diff --combined run-command.c
index cdf01845790849f863aef4195a68e12fa19ca543,171cbaa944adc0352eac635029cd22c78144a65e..019f6d19a5a10718bd4aa94cf1076312e0297016
@@@ -3,8 -3,6 +3,8 @@@
  #include "exec_cmd.h"
  #include "sigchain.h"
  #include "argv-array.h"
 +#include "thread-utils.h"
 +#include "strbuf.h"
  
  void child_process_init(struct child_process *child)
  {
@@@ -160,50 -158,41 +160,41 @@@ int sane_execvp(const char *file, char 
        return -1;
  }
  
- static const char **prepare_shell_cmd(const char **argv)
+ static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
  {
-       int argc, nargc = 0;
-       const char **nargv;
-       for (argc = 0; argv[argc]; argc++)
-               ; /* just counting */
-       /* +1 for NULL, +3 for "sh -c" plus extra $0 */
-       nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3));
-       if (argc < 1)
+       if (!argv[0])
                die("BUG: shell command is empty");
  
        if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
  #ifndef GIT_WINDOWS_NATIVE
-               nargv[nargc++] = SHELL_PATH;
+               argv_array_push(out, SHELL_PATH);
  #else
-               nargv[nargc++] = "sh";
+               argv_array_push(out, "sh");
  #endif
-               nargv[nargc++] = "-c";
-               if (argc < 2)
-                       nargv[nargc++] = argv[0];
-               else {
-                       struct strbuf arg0 = STRBUF_INIT;
-                       strbuf_addf(&arg0, "%s \"$@\"", argv[0]);
-                       nargv[nargc++] = strbuf_detach(&arg0, NULL);
-               }
-       }
+               argv_array_push(out, "-c");
  
-       for (argc = 0; argv[argc]; argc++)
-               nargv[nargc++] = argv[argc];
-       nargv[nargc] = NULL;
+               /*
+                * If we have no extra arguments, we do not even need to
+                * bother with the "$@" magic.
+                */
+               if (!argv[1])
+                       argv_array_push(out, argv[0]);
+               else
+                       argv_array_pushf(out, "%s \"$@\"", argv[0]);
+       }
  
-       return nargv;
+       argv_array_pushv(out, argv);
+       return out->argv;
  }
  
  #ifndef GIT_WINDOWS_NATIVE
  static int execv_shell_cmd(const char **argv)
  {
-       const char **nargv = prepare_shell_cmd(argv);
-       trace_argv_printf(nargv, "trace: exec:");
-       sane_execvp(nargv[0], (char **)nargv);
-       free(nargv);
+       struct argv_array nargv = ARGV_ARRAY_INIT;
+       prepare_shell_cmd(&nargv, argv);
+       trace_argv_printf(nargv.argv, "trace: exec:");
+       sane_execvp(nargv.argv[0], (char **)nargv.argv);
+       argv_array_clear(&nargv);
        return -1;
  }
  #endif
@@@ -247,7 -236,7 +238,7 @@@ static int wait_or_whine(pid_t pid, con
                error("waitpid is confused (%s)", argv0);
        } else if (WIFSIGNALED(status)) {
                code = WTERMSIG(status);
 -              if (code != SIGINT && code != SIGQUIT)
 +              if (code != SIGINT && code != SIGQUIT && code != SIGPIPE)
                        error("%s died of signal %d", argv0, code);
                /*
                 * This return value is chosen so that code & 0xff
@@@ -457,6 -446,7 +448,7 @@@ fail_pipe
  {
        int fhin = 0, fhout = 1, fherr = 2;
        const char **sargv = cmd->argv;
+       struct argv_array nargv = ARGV_ARRAY_INIT;
  
        if (cmd->no_stdin)
                fhin = open("/dev/null", O_RDWR);
                fhout = dup(cmd->out);
  
        if (cmd->git_cmd)
-               cmd->argv = prepare_git_cmd(cmd->argv);
+               cmd->argv = prepare_git_cmd(&nargv, cmd->argv);
        else if (cmd->use_shell)
-               cmd->argv = prepare_shell_cmd(cmd->argv);
+               cmd->argv = prepare_shell_cmd(&nargv, cmd->argv);
  
        cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, (char**) cmd->env,
                        cmd->dir, fhin, fhout, fherr);
        if (cmd->clean_on_exit && cmd->pid >= 0)
                mark_child_for_cleanup(cmd->pid);
  
-       if (cmd->git_cmd)
-               free(cmd->argv);
+       argv_array_clear(&nargv);
        cmd->argv = sargv;
        if (fhin != 0)
                close(fhin);
@@@ -867,336 -855,3 +857,336 @@@ int capture_command(struct child_proces
        close(cmd->out);
        return finish_command(cmd);
  }
 +
 +enum child_state {
 +      GIT_CP_FREE,
 +      GIT_CP_WORKING,
 +      GIT_CP_WAIT_CLEANUP,
 +};
 +
 +struct parallel_processes {
 +      void *data;
 +
 +      int max_processes;
 +      int nr_processes;
 +
 +      get_next_task_fn get_next_task;
 +      start_failure_fn start_failure;
 +      task_finished_fn task_finished;
 +
 +      struct {
 +              enum child_state state;
 +              struct child_process process;
 +              struct strbuf err;
 +              void *data;
 +      } *children;
 +      /*
 +       * The struct pollfd is logically part of *children,
 +       * but the system call expects it as its own array.
 +       */
 +      struct pollfd *pfd;
 +
 +      unsigned shutdown : 1;
 +
 +      int output_owner;
 +      struct strbuf buffered_output; /* of finished children */
 +};
 +
 +static int default_start_failure(struct child_process *cp,
 +                               struct strbuf *err,
 +                               void *pp_cb,
 +                               void *pp_task_cb)
 +{
 +      int i;
 +
 +      strbuf_addstr(err, "Starting a child failed:");
 +      for (i = 0; cp->argv[i]; i++)
 +              strbuf_addf(err, " %s", cp->argv[i]);
 +
 +      return 0;
 +}
 +
 +static int default_task_finished(int result,
 +                               struct child_process *cp,
 +                               struct strbuf *err,
 +                               void *pp_cb,
 +                               void *pp_task_cb)
 +{
 +      int i;
 +
 +      if (!result)
 +              return 0;
 +
 +      strbuf_addf(err, "A child failed with return code %d:", result);
 +      for (i = 0; cp->argv[i]; i++)
 +              strbuf_addf(err, " %s", cp->argv[i]);
 +
 +      return 0;
 +}
 +
 +static void kill_children(struct parallel_processes *pp, int signo)
 +{
 +      int i, n = pp->max_processes;
 +
 +      for (i = 0; i < n; i++)
 +              if (pp->children[i].state == GIT_CP_WORKING)
 +                      kill(pp->children[i].process.pid, signo);
 +}
 +
 +static struct parallel_processes *pp_for_signal;
 +
 +static void handle_children_on_signal(int signo)
 +{
 +      kill_children(pp_for_signal, signo);
 +      sigchain_pop(signo);
 +      raise(signo);
 +}
 +
 +static void pp_init(struct parallel_processes *pp,
 +                  int n,
 +                  get_next_task_fn get_next_task,
 +                  start_failure_fn start_failure,
 +                  task_finished_fn task_finished,
 +                  void *data)
 +{
 +      int i;
 +
 +      if (n < 1)
 +              n = online_cpus();
 +
 +      pp->max_processes = n;
 +
 +      trace_printf("run_processes_parallel: preparing to run up to %d tasks", n);
 +
 +      pp->data = data;
 +      if (!get_next_task)
 +              die("BUG: you need to specify a get_next_task function");
 +      pp->get_next_task = get_next_task;
 +
 +      pp->start_failure = start_failure ? start_failure : default_start_failure;
 +      pp->task_finished = task_finished ? task_finished : default_task_finished;
 +
 +      pp->nr_processes = 0;
 +      pp->output_owner = 0;
 +      pp->shutdown = 0;
 +      pp->children = xcalloc(n, sizeof(*pp->children));
 +      pp->pfd = xcalloc(n, sizeof(*pp->pfd));
 +      strbuf_init(&pp->buffered_output, 0);
 +
 +      for (i = 0; i < n; i++) {
 +              strbuf_init(&pp->children[i].err, 0);
 +              child_process_init(&pp->children[i].process);
 +              pp->pfd[i].events = POLLIN | POLLHUP;
 +              pp->pfd[i].fd = -1;
 +      }
 +
 +      pp_for_signal = pp;
 +      sigchain_push_common(handle_children_on_signal);
 +}
 +
 +static void pp_cleanup(struct parallel_processes *pp)
 +{
 +      int i;
 +
 +      trace_printf("run_processes_parallel: done");
 +      for (i = 0; i < pp->max_processes; i++) {
 +              strbuf_release(&pp->children[i].err);
 +              child_process_clear(&pp->children[i].process);
 +      }
 +
 +      free(pp->children);
 +      free(pp->pfd);
 +
 +      /*
 +       * When get_next_task added messages to the buffer in its last
 +       * iteration, the buffered output is non empty.
 +       */
 +      fputs(pp->buffered_output.buf, stderr);
 +      strbuf_release(&pp->buffered_output);
 +
 +      sigchain_pop_common();
 +}
 +
 +/* returns
 + *  0 if a new task was started.
 + *  1 if no new jobs was started (get_next_task ran out of work, non critical
 + *    problem with starting a new command)
 + * <0 no new job was started, user wishes to shutdown early. Use negative code
 + *    to signal the children.
 + */
 +static int pp_start_one(struct parallel_processes *pp)
 +{
 +      int i, code;
 +
 +      for (i = 0; i < pp->max_processes; i++)
 +              if (pp->children[i].state == GIT_CP_FREE)
 +                      break;
 +      if (i == pp->max_processes)
 +              die("BUG: bookkeeping is hard");
 +
 +      code = pp->get_next_task(&pp->children[i].process,
 +                               &pp->children[i].err,
 +                               pp->data,
 +                               &pp->children[i].data);
 +      if (!code) {
 +              strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
 +              strbuf_reset(&pp->children[i].err);
 +              return 1;
 +      }
 +      pp->children[i].process.err = -1;
 +      pp->children[i].process.stdout_to_stderr = 1;
 +      pp->children[i].process.no_stdin = 1;
 +
 +      if (start_command(&pp->children[i].process)) {
 +              code = pp->start_failure(&pp->children[i].process,
 +                                       &pp->children[i].err,
 +                                       pp->data,
 +                                       &pp->children[i].data);
 +              strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
 +              strbuf_reset(&pp->children[i].err);
 +              if (code)
 +                      pp->shutdown = 1;
 +              return code;
 +      }
 +
 +      pp->nr_processes++;
 +      pp->children[i].state = GIT_CP_WORKING;
 +      pp->pfd[i].fd = pp->children[i].process.err;
 +      return 0;
 +}
 +
 +static void pp_buffer_stderr(struct parallel_processes *pp, int output_timeout)
 +{
 +      int i;
 +
 +      while ((i = poll(pp->pfd, pp->max_processes, output_timeout)) < 0) {
 +              if (errno == EINTR)
 +                      continue;
 +              pp_cleanup(pp);
 +              die_errno("poll");
 +      }
 +
 +      /* Buffer output from all pipes. */
 +      for (i = 0; i < pp->max_processes; i++) {
 +              if (pp->children[i].state == GIT_CP_WORKING &&
 +                  pp->pfd[i].revents & (POLLIN | POLLHUP)) {
 +                      int n = strbuf_read_once(&pp->children[i].err,
 +                                               pp->children[i].process.err, 0);
 +                      if (n == 0) {
 +                              close(pp->children[i].process.err);
 +                              pp->children[i].state = GIT_CP_WAIT_CLEANUP;
 +                      } else if (n < 0)
 +                              if (errno != EAGAIN)
 +                                      die_errno("read");
 +              }
 +      }
 +}
 +
 +static void pp_output(struct parallel_processes *pp)
 +{
 +      int i = pp->output_owner;
 +      if (pp->children[i].state == GIT_CP_WORKING &&
 +          pp->children[i].err.len) {
 +              fputs(pp->children[i].err.buf, stderr);
 +              strbuf_reset(&pp->children[i].err);
 +      }
 +}
 +
 +static int pp_collect_finished(struct parallel_processes *pp)
 +{
 +      int i, code;
 +      int n = pp->max_processes;
 +      int result = 0;
 +
 +      while (pp->nr_processes > 0) {
 +              for (i = 0; i < pp->max_processes; i++)
 +                      if (pp->children[i].state == GIT_CP_WAIT_CLEANUP)
 +                              break;
 +              if (i == pp->max_processes)
 +                      break;
 +
 +              code = finish_command(&pp->children[i].process);
 +
 +              code = pp->task_finished(code, &pp->children[i].process,
 +                                       &pp->children[i].err, pp->data,
 +                                       &pp->children[i].data);
 +
 +              if (code)
 +                      result = code;
 +              if (code < 0)
 +                      break;
 +
 +              pp->nr_processes--;
 +              pp->children[i].state = GIT_CP_FREE;
 +              pp->pfd[i].fd = -1;
 +              child_process_init(&pp->children[i].process);
 +
 +              if (i != pp->output_owner) {
 +                      strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
 +                      strbuf_reset(&pp->children[i].err);
 +              } else {
 +                      fputs(pp->children[i].err.buf, stderr);
 +                      strbuf_reset(&pp->children[i].err);
 +
 +                      /* Output all other finished child processes */
 +                      fputs(pp->buffered_output.buf, stderr);
 +                      strbuf_reset(&pp->buffered_output);
 +
 +                      /*
 +                       * Pick next process to output live.
 +                       * NEEDSWORK:
 +                       * For now we pick it randomly by doing a round
 +                       * robin. Later we may want to pick the one with
 +                       * the most output or the longest or shortest
 +                       * running process time.
 +                       */
 +                      for (i = 0; i < n; i++)
 +                              if (pp->children[(pp->output_owner + i) % n].state == GIT_CP_WORKING)
 +                                      break;
 +                      pp->output_owner = (pp->output_owner + i) % n;
 +              }
 +      }
 +      return result;
 +}
 +
 +int run_processes_parallel(int n,
 +                         get_next_task_fn get_next_task,
 +                         start_failure_fn start_failure,
 +                         task_finished_fn task_finished,
 +                         void *pp_cb)
 +{
 +      int i, code;
 +      int output_timeout = 100;
 +      int spawn_cap = 4;
 +      struct parallel_processes pp;
 +
 +      pp_init(&pp, n, get_next_task, start_failure, task_finished, pp_cb);
 +      while (1) {
 +              for (i = 0;
 +                  i < spawn_cap && !pp.shutdown &&
 +                  pp.nr_processes < pp.max_processes;
 +                  i++) {
 +                      code = pp_start_one(&pp);
 +                      if (!code)
 +                              continue;
 +                      if (code < 0) {
 +                              pp.shutdown = 1;
 +                              kill_children(&pp, -code);
 +                      }
 +                      break;
 +              }
 +              if (!pp.nr_processes)
 +                      break;
 +              pp_buffer_stderr(&pp, output_timeout);
 +              pp_output(&pp);
 +              code = pp_collect_finished(&pp);
 +              if (code) {
 +                      pp.shutdown = 1;
 +                      if (code < 0)
 +                              kill_children(&pp, -code);
 +              }
 +      }
 +
 +      pp_cleanup(&pp);
 +      return 0;
 +}
diff --combined sequencer.c
index 80487860c8fc5c79e3ffd4fbe21a0a0ea7e385ca,e60e75ad749a18a3c12d95475f95e67e455ab371..e66f2fe0f0409cccf1647f7851044f91d68252a6
@@@ -124,42 -124,33 +124,33 @@@ static const char *action_name(const st
  
  struct commit_message {
        char *parent_label;
-       const char *label;
-       const char *subject;
+       char *label;
+       char *subject;
        const char *message;
  };
  
  static int get_message(struct commit *commit, struct commit_message *out)
  {
        const char *abbrev, *subject;
-       int abbrev_len, subject_len;
-       char *q;
+       int subject_len;
  
-       if (!git_commit_encoding)
-               git_commit_encoding = "UTF-8";
-       out->message = logmsg_reencode(commit, NULL, git_commit_encoding);
+       out->message = logmsg_reencode(commit, NULL, get_commit_output_encoding());
        abbrev = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
-       abbrev_len = strlen(abbrev);
  
        subject_len = find_commit_subject(out->message, &subject);
  
-       out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
-                             strlen("... ") + subject_len + 1);
-       q = out->parent_label;
-       q = mempcpy(q, "parent of ", strlen("parent of "));
-       out->label = q;
-       q = mempcpy(q, abbrev, abbrev_len);
-       q = mempcpy(q, "... ", strlen("... "));
-       out->subject = q;
-       q = mempcpy(q, subject, subject_len);
-       *q = '\0';
+       out->subject = xmemdupz(subject, subject_len);
+       out->label = xstrfmt("%s... %s", abbrev, out->subject);
+       out->parent_label = xstrfmt("parent of %s", out->label);
        return 0;
  }
  
  static void free_message(struct commit *commit, struct commit_message *msg)
  {
        free(msg->parent_label);
+       free(msg->label);
+       free(msg->subject);
        unuse_commit_buffer(commit, msg->message);
  }
  
@@@ -886,7 -877,7 +877,7 @@@ static int sequencer_rollback(struct re
        if (!f)
                return error(_("cannot open %s: %s"), git_path_head_file(),
                                                strerror(errno));
 -      if (strbuf_getline(&buf, f, '\n')) {
 +      if (strbuf_getline_lf(&buf, f)) {
                error(_("cannot read %s: %s"), git_path_head_file(),
                      ferror(f) ?  strerror(errno) : _("unexpected end of file"));
                fclose(f);
diff --combined setup.c
index 59ec6587aa7e54b72d7a2f9fe0af51d66b2c8901,669062a0906ce7a78810582332a2b6bb72c3d0c5..de1a2a7ea5973fef256328a26730117466c15172
+++ b/setup.c
@@@ -88,7 -88,7 +88,7 @@@ char *prefix_path_gently(const char *pr
        const char *orig = path;
        char *sanitized;
        if (is_absolute_path(orig)) {
-               sanitized = xmalloc(strlen(path) + 1);
+               sanitized = xmallocz(strlen(path));
                if (remaining_prefix)
                        *remaining_prefix = 0;
                if (normalize_path_copy_len(sanitized, path, remaining_prefix)) {
@@@ -139,7 -139,9 +139,7 @@@ int check_filename(const char *prefix, 
                if (arg[2] == '\0') /* ":/" is root dir, always exists */
                        return 1;
                name = arg + 2;
 -      } else if (!no_wildcard(arg))
 -              return 1;
 -      else if (prefix)
 +      } else if (prefix)
                name = prefix_filename(prefix, strlen(prefix), arg);
        else
                name = arg;
@@@ -200,7 -202,7 +200,7 @@@ void verify_filename(const char *prefix
  {
        if (*arg == '-')
                die("bad flag '%s' used after filename", arg);
 -      if (check_filename(prefix, arg))
 +      if (check_filename(prefix, arg) || !no_wildcard(arg))
                return;
        die_verify_filename(prefix, arg, diagnose_misspelt_rev);
  }
@@@ -449,6 -451,17 +449,6 @@@ static int check_repository_format_gent
        return ret;
  }
  
 -static void update_linked_gitdir(const char *gitfile, const char *gitdir)
 -{
 -      struct strbuf path = STRBUF_INIT;
 -      struct stat st;
 -
 -      strbuf_addf(&path, "%s/gitdir", gitdir);
 -      if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL))
 -              write_file(path.buf, "%s", gitfile);
 -      strbuf_release(&path);
 -}
 -
  /*
   * Try to read the location of the git directory from the .git file,
   * return path to git directory if found.
@@@ -486,14 -499,13 +486,13 @@@ const char *read_gitfile_gently(const c
                error_code = READ_GITFILE_ERR_OPEN_FAILED;
                goto cleanup_return;
        }
-       buf = xmalloc(st.st_size + 1);
+       buf = xmallocz(st.st_size);
        len = read_in_full(fd, buf, st.st_size);
        close(fd);
        if (len != st.st_size) {
                error_code = READ_GITFILE_ERR_READ_FAILED;
                goto cleanup_return;
        }
-       buf[len] = '\0';
        if (!starts_with(buf, "gitdir: ")) {
                error_code = READ_GITFILE_ERR_INVALID_FORMAT;
                goto cleanup_return;
                error_code = READ_GITFILE_ERR_NOT_A_REPO;
                goto cleanup_return;
        }
 -      update_linked_gitdir(path, dir);
        path = real_path(dir);
  
  cleanup_return:
diff --combined sha1_file.c
index aab1872b4c95416fc7c8ae9c93cd25f341e2d4c1,ab16c7b98cbd72bc79e8c0866417889626e2e71a..02517009c15ffe5ef8bc8174805a9746e809eeab
@@@ -253,7 -253,7 +253,7 @@@ static int link_alt_odb_entry(const cha
  {
        struct alternate_object_database *ent;
        struct alternate_object_database *alt;
-       int pfxlen, entlen;
+       size_t pfxlen, entlen;
        struct strbuf pathbuf = STRBUF_INIT;
  
        if (!is_absolute_path(entry) && relative_base) {
        while (pfxlen && pathbuf.buf[pfxlen-1] == '/')
                pfxlen -= 1;
  
-       entlen = pfxlen + 43; /* '/' + 2 hex + '/' + 38 hex + NUL */
-       ent = xmalloc(sizeof(*ent) + entlen);
+       entlen = st_add(pfxlen, 43); /* '/' + 2 hex + '/' + 38 hex + NUL */
+       ent = xmalloc(st_add(sizeof(*ent), entlen));
        memcpy(ent->base, pathbuf.buf, pfxlen);
        strbuf_release(&pathbuf);
  
@@@ -396,7 -396,7 +396,7 @@@ void add_to_alternates_file(const char 
                struct strbuf line = STRBUF_INIT;
                int found = 0;
  
 -              while (strbuf_getline(&line, in, '\n') != EOF) {
 +              while (strbuf_getline(&line, in) != EOF) {
                        if (!strcmp(reference, line.buf)) {
                                found = 1;
                                break;
@@@ -1134,7 -1134,7 +1134,7 @@@ unsigned char *use_pack(struct packed_g
  
  static struct packed_git *alloc_packed_git(int extra)
  {
-       struct packed_git *p = xmalloc(sizeof(*p) + extra);
+       struct packed_git *p = xmalloc(st_add(sizeof(*p), extra));
        memset(p, 0, sizeof(*p));
        p->pack_fd = -1;
        return p;
@@@ -1168,7 -1168,7 +1168,7 @@@ struct packed_git *add_packed_git(cons
         * ".pack" is long enough to hold any suffix we're adding (and
         * the use xsnprintf double-checks that)
         */
-       alloc = path_len + strlen(".pack") + 1;
+       alloc = st_add3(path_len, strlen(".pack"), 1);
        p = alloc_packed_git(alloc);
        memcpy(p->pack_name, path, path_len);
  
  struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
  {
        const char *path = sha1_pack_name(sha1);
-       int alloc = strlen(path) + 1;
+       size_t alloc = st_add(strlen(path), 1);
        struct packed_git *p = alloc_packed_git(alloc);
  
        memcpy(p->pack_name, path, alloc); /* includes NUL */
@@@ -1413,10 -1413,12 +1413,12 @@@ static void mark_bad_packed_object(stru
  {
        unsigned i;
        for (i = 0; i < p->num_bad_objects; i++)
-               if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+               if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
                        return;
-       p->bad_object_sha1 = xrealloc(p->bad_object_sha1, 20 * (p->num_bad_objects + 1));
-       hashcpy(p->bad_object_sha1 + 20 * p->num_bad_objects, sha1);
+       p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
+                                     st_mult(GIT_SHA1_RAWSZ,
+                                             st_add(p->num_bad_objects, 1)));
+       hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
        p->num_bad_objects++;
  }
  
@@@ -1942,7 -1944,7 +1944,7 @@@ static enum object_type packed_to_objec
                /* Push the object we're going to leave behind */
                if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
                        poi_stack_alloc = alloc_nr(poi_stack_nr);
-                       poi_stack = xmalloc(sizeof(off_t)*poi_stack_alloc);
+                       ALLOC_ARRAY(poi_stack, poi_stack_alloc);
                        memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
                } else {
                        ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
@@@ -2308,7 -2310,7 +2310,7 @@@ void *unpack_entry(struct packed_git *p
                if (delta_stack_nr >= delta_stack_alloc
                    && delta_stack == small_delta_stack) {
                        delta_stack_alloc = alloc_nr(delta_stack_nr);
-                       delta_stack = xmalloc(sizeof(*delta_stack)*delta_stack_alloc);
+                       ALLOC_ARRAY(delta_stack, delta_stack_alloc);
                        memcpy(delta_stack, small_delta_stack,
                               sizeof(*delta_stack)*delta_stack_nr);
                } else {
diff --combined sha1_name.c
index d0f844db897751adca937578185c7421095bbc1e,532db4fdc6dcf905e99470e773a6166be2f903ed..3acf221f92f7a0857f17c0210044cacc325c8ce0
@@@ -87,9 -87,8 +87,8 @@@ static void find_short_object_filename(
                 * object databases including our own.
                 */
                const char *objdir = get_object_directory();
-               int objdir_len = strlen(objdir);
-               int entlen = objdir_len + 43;
-               fakeent = xmalloc(sizeof(*fakeent) + entlen);
+               size_t objdir_len = strlen(objdir);
+               fakeent = xmalloc(st_add3(sizeof(*fakeent), objdir_len, 43));
                memcpy(fakeent->base, objdir, objdir_len);
                fakeent->name = fakeent->base + objdir_len + 1;
                fakeent->name[-1] = '/';
@@@ -848,12 -847,8 +847,12 @@@ static int get_sha1_1(const char *name
   * through history and returning the first commit whose message starts
   * the given regular expression.
   *
 - * For future extension, ':/!' is reserved. If you want to match a message
 - * beginning with a '!', you have to repeat the exclamation mark.
 + * For negative-matching, prefix the pattern-part with '!-', like: ':/!-WIP'.
 + *
 + * For a literal '!' character at the beginning of a pattern, you have to repeat
 + * that, like: ':/!!foo'
 + *
 + * For future extension, all other sequences beginning with ':/!' are reserved.
   */
  
  /* Remember to update object flag allocation in object.h */
@@@ -882,22 -877,16 +881,22 @@@ static int get_sha1_oneline(const char 
  {
        struct commit_list *backup = NULL, *l;
        int found = 0;
 +      int negative = 0;
        regex_t regex;
  
        if (prefix[0] == '!') {
 -              if (prefix[1] != '!')
 -                      die ("Invalid search pattern: %s", prefix);
                prefix++;
 +
 +              if (prefix[0] == '-') {
 +                      prefix++;
 +                      negative = 1;
 +              } else if (prefix[0] != '!') {
 +                      return -1;
 +              }
        }
  
        if (regcomp(&regex, prefix, REG_EXTENDED))
 -              die("Invalid search pattern: %s", prefix);
 +              return -1;
  
        for (l = list; l; l = l->next) {
                l->item->object.flags |= ONELINE_SEEN;
                        continue;
                buf = get_commit_buffer(commit, NULL);
                p = strstr(buf, "\n\n");
 -              matches = p && !regexec(&regex, p + 2, 0, NULL, 0);
 +              matches = negative ^ (p && !regexec(&regex, p + 2, 0, NULL, 0));
                unuse_commit_buffer(commit, buf);
  
                if (matches) {
diff --combined strbuf.c
index bab316dda8f4946efb9a908f17c98b4db3d21e98,de7a7c273095e4f5907260ddda50bef375381783..f60e2ee72ba86cbd6c66622366af4a7faf25e1a0
+++ b/strbuf.c
@@@ -384,17 -384,6 +384,17 @@@ ssize_t strbuf_read(struct strbuf *sb, 
        return sb->len - oldlen;
  }
  
 +ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint)
 +{
 +      ssize_t cnt;
 +
 +      strbuf_grow(sb, hint ? hint : 8192);
 +      cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
 +      if (cnt > 0)
 +              strbuf_setlen(sb, sb->len + cnt);
 +      return cnt;
 +}
 +
  #define STRBUF_MAXLINK (2*PATH_MAX)
  
  int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
@@@ -512,37 -501,15 +512,37 @@@ int strbuf_getwholeline(struct strbuf *
  }
  #endif
  
 -int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
 +static int strbuf_getdelim(struct strbuf *sb, FILE *fp, int term)
  {
        if (strbuf_getwholeline(sb, fp, term))
                return EOF;
 -      if (sb->buf[sb->len-1] == term)
 -              strbuf_setlen(sb, sb->len-1);
 +      if (sb->buf[sb->len - 1] == term)
 +              strbuf_setlen(sb, sb->len - 1);
        return 0;
  }
  
 +int strbuf_getline(struct strbuf *sb, FILE *fp)
 +{
 +      if (strbuf_getwholeline(sb, fp, '\n'))
 +              return EOF;
 +      if (sb->buf[sb->len - 1] == '\n') {
 +              strbuf_setlen(sb, sb->len - 1);
 +              if (sb->len && sb->buf[sb->len - 1] == '\r')
 +                      strbuf_setlen(sb, sb->len - 1);
 +      }
 +      return 0;
 +}
 +
 +int strbuf_getline_lf(struct strbuf *sb, FILE *fp)
 +{
 +      return strbuf_getdelim(sb, fp, '\n');
 +}
 +
 +int strbuf_getline_nul(struct strbuf *sb, FILE *fp)
 +{
 +      return strbuf_getdelim(sb, fp, '\0');
 +}
 +
  int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term)
  {
        strbuf_reset(sb);
@@@ -718,7 -685,7 +718,7 @@@ char *xstrdup_tolower(const char *strin
        size_t len, i;
  
        len = strlen(string);
-       result = xmalloc(len + 1);
+       result = xmallocz(len);
        for (i = 0; i < len; i++)
                result[i] = tolower(string[i]);
        result[i] = '\0';
diff --combined submodule.c
index b83939c294782ac59e5c78c8882064a1e272e309,bd97b15a9284c45f8a64a43a32deaf190cd243ea..ac61c656115b65c45aad971024678e9353bc5ed3
@@@ -12,7 -12,6 +12,7 @@@
  #include "sha1-array.h"
  #include "argv-array.h"
  #include "blob.h"
 +#include "thread-utils.h"
  
  static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
  static struct string_list changed_submodule_paths;
@@@ -123,7 -122,7 +123,7 @@@ static int add_submodule_odb(const cha
        struct strbuf objects_directory = STRBUF_INIT;
        struct alternate_object_database *alt_odb;
        int ret = 0;
-       int alloc;
+       size_t alloc;
  
        strbuf_git_path_submodule(&objects_directory, path, "objects/");
        if (!is_directory(objects_directory.buf)) {
                                        objects_directory.len))
                        goto done;
  
-       alloc = objects_directory.len + 42; /* for "12/345..." sha1 */
-       alt_odb = xmalloc(sizeof(*alt_odb) + alloc);
+       alloc = st_add(objects_directory.len, 42); /* for "12/345..." sha1 */
+       alt_odb = xmalloc(st_add(sizeof(*alt_odb), alloc));
        alt_odb->next = alt_odb_list;
        xsnprintf(alt_odb->base, alloc, "%s", objects_directory.buf);
        alt_odb->name = alt_odb->base + objects_directory.len;
@@@ -611,28 -610,37 +611,28 @@@ static void calculate_changed_submodule
        initialized_fetch_ref_tips = 0;
  }
  
 -int fetch_populated_submodules(const struct argv_array *options,
 -                             const char *prefix, int command_line_option,
 -                             int quiet)
 +struct submodule_parallel_fetch {
 +      int count;
 +      struct argv_array args;
 +      const char *work_tree;
 +      const char *prefix;
 +      int command_line_option;
 +      int quiet;
 +      int result;
 +};
 +#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0}
 +
 +static int get_next_submodule(struct child_process *cp,
 +                            struct strbuf *err, void *data, void **task_cb)
  {
 -      int i, result = 0;
 -      struct child_process cp = CHILD_PROCESS_INIT;
 -      struct argv_array argv = ARGV_ARRAY_INIT;
 -      const char *work_tree = get_git_work_tree();
 -      if (!work_tree)
 -              goto out;
 -
 -      if (read_cache() < 0)
 -              die("index file corrupt");
 -
 -      argv_array_push(&argv, "fetch");
 -      for (i = 0; i < options->argc; i++)
 -              argv_array_push(&argv, options->argv[i]);
 -      argv_array_push(&argv, "--recurse-submodules-default");
 -      /* default value, "--submodule-prefix" and its value are added later */
 -
 -      cp.env = local_repo_env;
 -      cp.git_cmd = 1;
 -      cp.no_stdin = 1;
 -
 -      calculate_changed_submodule_paths();
 +      int ret = 0;
 +      struct submodule_parallel_fetch *spf = data;
  
 -      for (i = 0; i < active_nr; i++) {
 +      for (; spf->count < active_nr; spf->count++) {
                struct strbuf submodule_path = STRBUF_INIT;
                struct strbuf submodule_git_dir = STRBUF_INIT;
                struct strbuf submodule_prefix = STRBUF_INIT;
 -              const struct cache_entry *ce = active_cache[i];
 +              const struct cache_entry *ce = active_cache[spf->count];
                const char *git_dir, *default_argv;
                const struct submodule *submodule;
  
                        submodule = submodule_from_name(null_sha1, ce->name);
  
                default_argv = "yes";
 -              if (command_line_option == RECURSE_SUBMODULES_DEFAULT) {
 +              if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
                        if (submodule &&
                            submodule->fetch_recurse !=
                                                RECURSE_SUBMODULES_NONE) {
                                        default_argv = "on-demand";
                                }
                        }
 -              } else if (command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
 +              } else if (spf->command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
                        if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
                                continue;
                        default_argv = "on-demand";
                }
  
 -              strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name);
 +              strbuf_addf(&submodule_path, "%s/%s", spf->work_tree, ce->name);
                strbuf_addf(&submodule_git_dir, "%s/.git", submodule_path.buf);
 -              strbuf_addf(&submodule_prefix, "%s%s/", prefix, ce->name);
 +              strbuf_addf(&submodule_prefix, "%s%s/", spf->prefix, ce->name);
                git_dir = read_gitfile(submodule_git_dir.buf);
                if (!git_dir)
                        git_dir = submodule_git_dir.buf;
                if (is_directory(git_dir)) {
 -                      if (!quiet)
 -                              printf("Fetching submodule %s%s\n", prefix, ce->name);
 -                      cp.dir = submodule_path.buf;
 -                      argv_array_push(&argv, default_argv);
 -                      argv_array_push(&argv, "--submodule-prefix");
 -                      argv_array_push(&argv, submodule_prefix.buf);
 -                      cp.argv = argv.argv;
 -                      if (run_command(&cp))
 -                              result = 1;
 -                      argv_array_pop(&argv);
 -                      argv_array_pop(&argv);
 -                      argv_array_pop(&argv);
 +                      child_process_init(cp);
 +                      cp->dir = strbuf_detach(&submodule_path, NULL);
 +                      cp->env = local_repo_env;
 +                      cp->git_cmd = 1;
 +                      if (!spf->quiet)
 +                              strbuf_addf(err, "Fetching submodule %s%s\n",
 +                                          spf->prefix, ce->name);
 +                      argv_array_init(&cp->args);
 +                      argv_array_pushv(&cp->args, spf->args.argv);
 +                      argv_array_push(&cp->args, default_argv);
 +                      argv_array_push(&cp->args, "--submodule-prefix");
 +                      argv_array_push(&cp->args, submodule_prefix.buf);
 +                      ret = 1;
                }
                strbuf_release(&submodule_path);
                strbuf_release(&submodule_git_dir);
                strbuf_release(&submodule_prefix);
 +              if (ret) {
 +                      spf->count++;
 +                      return 1;
 +              }
        }
 -      argv_array_clear(&argv);
 +      return 0;
 +}
 +
 +static int fetch_start_failure(struct child_process *cp,
 +                             struct strbuf *err,
 +                             void *cb, void *task_cb)
 +{
 +      struct submodule_parallel_fetch *spf = cb;
 +
 +      spf->result = 1;
 +
 +      return 0;
 +}
 +
 +static int fetch_finish(int retvalue, struct child_process *cp,
 +                      struct strbuf *err, void *cb, void *task_cb)
 +{
 +      struct submodule_parallel_fetch *spf = cb;
 +
 +      if (retvalue)
 +              spf->result = 1;
 +
 +      return 0;
 +}
 +
 +int fetch_populated_submodules(const struct argv_array *options,
 +                             const char *prefix, int command_line_option,
 +                             int quiet, int max_parallel_jobs)
 +{
 +      int i;
 +      struct submodule_parallel_fetch spf = SPF_INIT;
 +
 +      spf.work_tree = get_git_work_tree();
 +      spf.command_line_option = command_line_option;
 +      spf.quiet = quiet;
 +      spf.prefix = prefix;
 +
 +      if (!spf.work_tree)
 +              goto out;
 +
 +      if (read_cache() < 0)
 +              die("index file corrupt");
 +
 +      argv_array_push(&spf.args, "fetch");
 +      for (i = 0; i < options->argc; i++)
 +              argv_array_push(&spf.args, options->argv[i]);
 +      argv_array_push(&spf.args, "--recurse-submodules-default");
 +      /* default value, "--submodule-prefix" and its value are added later */
 +
 +      calculate_changed_submodule_paths();
 +      run_processes_parallel(max_parallel_jobs,
 +                             get_next_submodule,
 +                             fetch_start_failure,
 +                             fetch_finish,
 +                             &spf);
 +
 +      argv_array_clear(&spf.args);
  out:
        string_list_clear(&changed_submodule_paths, 1);
 -      return result;
 +      return spf.result;
  }
  
  unsigned is_submodule_modified(const char *path, int ignore_untracked)
diff --combined test-path-utils.c
index 6232dfe661a53d1eaf17ead5133da5792ae2413f,0c15f1821f5bdfbe41b462e89bd63240fc4afc70..ba805b374c57a4e5ad2e6e4a4b9071a11c165afa
@@@ -8,21 -8,14 +8,14 @@@
   */
  static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
  {
-       const char *ceil = item->string;
-       int len = strlen(ceil);
-       char buf[PATH_MAX+1];
+       char *ceil = item->string;
  
-       if (len == 0)
+       if (!*ceil)
                die("Empty path is not supported");
-       if (len > PATH_MAX)
-               die("Path \"%s\" is too long", ceil);
        if (!is_absolute_path(ceil))
                die("Path \"%s\" is not absolute", ceil);
-       if (normalize_path_copy(buf, ceil) < 0)
+       if (normalize_path_copy(ceil, ceil) < 0)
                die("Path \"%s\" could not be normalized", ceil);
-       len = strlen(buf);
-       free(item->string);
-       item->string = xstrdup(buf);
        return 1;
  }
  
@@@ -56,7 -49,7 +49,7 @@@ static int test_function(struct test_da
                if (!data[i].from)
                        to = func(NULL);
                else {
 -                      strcpy(buffer, data[i].from);
 +                      xsnprintf(buffer, sizeof(buffer), "%s", data[i].from);
                        to = func(buffer);
                }
                if (!strcmp(to, data[i].to))
@@@ -166,7 -159,7 +159,7 @@@ static struct test_data dirname_data[] 
  int main(int argc, char **argv)
  {
        if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
-               char *buf = xmalloc(PATH_MAX + 1);
+               char *buf = xmallocz(strlen(argv[2]));
                int rv = normalize_path_copy(buf, argv[2]);
                if (rv)
                        buf = "++failed++";
diff --combined transport.c
index e20bb771faaa56aa876f14f73123e14a71731a1b,988047b12e380f94cb1a88cc5da3bb72d1ee217f..ca3cfa4b00d857603e6c72536fa6905a7ee2905f
  #include "sha1-array.h"
  #include "sigchain.h"
  
 -/* rsync support */
 -
 -/*
 - * We copy packed-refs and refs/ into a temporary file, then read the
 - * loose refs recursively (sorting whenever possible), and then inserting
 - * those packed refs that are not yet in the list (not validating, but
 - * assuming that the file is sorted).
 - *
 - * Appears refactoring this from refs.c is too cumbersome.
 - */
 -
 -static int str_cmp(const void *a, const void *b)
 -{
 -      const char *s1 = a;
 -      const char *s2 = b;
 -
 -      return strcmp(s1, s2);
 -}
 -
 -/* path->buf + name_offset is expected to point to "refs/" */
 -
 -static int read_loose_refs(struct strbuf *path, int name_offset,
 -              struct ref **tail)
 -{
 -      DIR *dir = opendir(path->buf);
 -      struct dirent *de;
 -      struct {
 -              char **entries;
 -              int nr, alloc;
 -      } list;
 -      int i, pathlen;
 -
 -      if (!dir)
 -              return -1;
 -
 -      memset (&list, 0, sizeof(list));
 -
 -      while ((de = readdir(dir))) {
 -              if (is_dot_or_dotdot(de->d_name))
 -                      continue;
 -              ALLOC_GROW(list.entries, list.nr + 1, list.alloc);
 -              list.entries[list.nr++] = xstrdup(de->d_name);
 -      }
 -      closedir(dir);
 -
 -      /* sort the list */
 -
 -      qsort(list.entries, list.nr, sizeof(char *), str_cmp);
 -
 -      pathlen = path->len;
 -      strbuf_addch(path, '/');
 -
 -      for (i = 0; i < list.nr; i++, strbuf_setlen(path, pathlen + 1)) {
 -              strbuf_addstr(path, list.entries[i]);
 -              if (read_loose_refs(path, name_offset, tail)) {
 -                      int fd = open(path->buf, O_RDONLY);
 -                      char buffer[40];
 -                      struct ref *next;
 -
 -                      if (fd < 0)
 -                              continue;
 -                      next = alloc_ref(path->buf + name_offset);
 -                      if (read_in_full(fd, buffer, 40) != 40 ||
 -                                      get_oid_hex(buffer, &next->old_oid)) {
 -                              close(fd);
 -                              free(next);
 -                              continue;
 -                      }
 -                      close(fd);
 -                      (*tail)->next = next;
 -                      *tail = next;
 -              }
 -      }
 -      strbuf_setlen(path, pathlen);
 -
 -      for (i = 0; i < list.nr; i++)
 -              free(list.entries[i]);
 -      free(list.entries);
 -
 -      return 0;
 -}
 -
 -/* insert the packed refs for which no loose refs were found */
 -
 -static void insert_packed_refs(const char *packed_refs, struct ref **list)
 -{
 -      FILE *f = fopen(packed_refs, "r");
 -      static char buffer[PATH_MAX];
 -
 -      if (!f)
 -              return;
 -
 -      for (;;) {
 -              int cmp = 0; /* assigned before used */
 -              int len;
 -
 -              if (!fgets(buffer, sizeof(buffer), f)) {
 -                      fclose(f);
 -                      return;
 -              }
 -
 -              if (!isxdigit(buffer[0]))
 -                      continue;
 -              len = strlen(buffer);
 -              if (len && buffer[len - 1] == '\n')
 -                      buffer[--len] = '\0';
 -              if (len < 41)
 -                      continue;
 -              while ((*list)->next &&
 -                              (cmp = strcmp(buffer + 41,
 -                                    (*list)->next->name)) > 0)
 -                      list = &(*list)->next;
 -              if (!(*list)->next || cmp < 0) {
 -                      struct ref *next = alloc_ref(buffer + 41);
 -                      buffer[40] = '\0';
 -                      if (get_oid_hex(buffer, &next->old_oid)) {
 -                              warning ("invalid SHA-1: %s", buffer);
 -                              free(next);
 -                              continue;
 -                      }
 -                      next->next = (*list)->next;
 -                      (*list)->next = next;
 -                      list = &(*list)->next;
 -              }
 -      }
 -}
 -
  static void set_upstreams(struct transport *transport, struct ref *refs,
        int pretend)
  {
        }
  }
  
 -static const char *rsync_url(const char *url)
 -{
 -      if (!starts_with(url, "rsync://"))
 -              skip_prefix(url, "rsync:", &url);
 -      return url;
 -}
 -
 -static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 -{
 -      struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
 -      struct ref dummy = {NULL}, *tail = &dummy;
 -      struct child_process rsync = CHILD_PROCESS_INIT;
 -      const char *args[5];
 -      int temp_dir_len;
 -
 -      if (for_push)
 -              return NULL;
 -
 -      /* copy the refs to the temporary directory */
 -
 -      strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
 -      if (!mkdtemp(temp_dir.buf))
 -              die_errno ("Could not make temporary directory");
 -      temp_dir_len = temp_dir.len;
 -
 -      strbuf_addstr(&buf, rsync_url(transport->url));
 -      strbuf_addstr(&buf, "/refs");
 -
 -      rsync.argv = args;
 -      rsync.stdout_to_stderr = 1;
 -      args[0] = "rsync";
 -      args[1] = (transport->verbose > 1) ? "-rv" : "-r";
 -      args[2] = buf.buf;
 -      args[3] = temp_dir.buf;
 -      args[4] = NULL;
 -
 -      if (run_command(&rsync))
 -              die ("Could not run rsync to get refs");
 -
 -      strbuf_reset(&buf);
 -      strbuf_addstr(&buf, rsync_url(transport->url));
 -      strbuf_addstr(&buf, "/packed-refs");
 -
 -      args[2] = buf.buf;
 -
 -      if (run_command(&rsync))
 -              die ("Could not run rsync to get refs");
 -
 -      /* read the copied refs */
 -
 -      strbuf_addstr(&temp_dir, "/refs");
 -      read_loose_refs(&temp_dir, temp_dir_len + 1, &tail);
 -      strbuf_setlen(&temp_dir, temp_dir_len);
 -
 -      tail = &dummy;
 -      strbuf_addstr(&temp_dir, "/packed-refs");
 -      insert_packed_refs(temp_dir.buf, &tail);
 -      strbuf_setlen(&temp_dir, temp_dir_len);
 -
 -      if (remove_dir_recursively(&temp_dir, 0))
 -              warning ("Error removing temporary directory %s.",
 -                              temp_dir.buf);
 -
 -      strbuf_release(&buf);
 -      strbuf_release(&temp_dir);
 -
 -      return dummy.next;
 -}
 -
 -static int fetch_objs_via_rsync(struct transport *transport,
 -                              int nr_objs, struct ref **to_fetch)
 -{
 -      struct child_process rsync = CHILD_PROCESS_INIT;
 -
 -      rsync.stdout_to_stderr = 1;
 -      argv_array_push(&rsync.args, "rsync");
 -      argv_array_push(&rsync.args, (transport->verbose > 1) ? "-rv" : "-r");
 -      argv_array_push(&rsync.args, "--ignore-existing");
 -      argv_array_push(&rsync.args, "--exclude");
 -      argv_array_push(&rsync.args, "info");
 -      argv_array_pushf(&rsync.args, "%s/objects/", rsync_url(transport->url));
 -      argv_array_push(&rsync.args, get_object_directory());
 -
 -      /* NEEDSWORK: handle one level of alternates */
 -      return run_command(&rsync);
 -}
 -
 -static int write_one_ref(const char *name, const struct object_id *oid,
 -                       int flags, void *data)
 -{
 -      struct strbuf *buf = data;
 -      int len = buf->len;
 -
 -      /* when called via for_each_ref(), flags is non-zero */
 -      if (flags && !starts_with(name, "refs/heads/") &&
 -                      !starts_with(name, "refs/tags/"))
 -              return 0;
 -
 -      strbuf_addstr(buf, name);
 -      if (safe_create_leading_directories(buf->buf) ||
 -          write_file_gently(buf->buf, "%s", oid_to_hex(oid)))
 -              return error("problems writing temporary file %s: %s",
 -                           buf->buf, strerror(errno));
 -      strbuf_setlen(buf, len);
 -      return 0;
 -}
 -
 -static int write_refs_to_temp_dir(struct strbuf *temp_dir,
 -                                int refspec_nr, const char **refspec)
 -{
 -      int i;
 -
 -      for (i = 0; i < refspec_nr; i++) {
 -              struct object_id oid;
 -              char *ref;
 -
 -              if (dwim_ref(refspec[i], strlen(refspec[i]), oid.hash, &ref) != 1)
 -                      return error("Could not get ref %s", refspec[i]);
 -
 -              if (write_one_ref(ref, &oid, 0, temp_dir)) {
 -                      free(ref);
 -                      return -1;
 -              }
 -              free(ref);
 -      }
 -      return 0;
 -}
 -
 -static int rsync_transport_push(struct transport *transport,
 -              int refspec_nr, const char **refspec, int flags)
 -{
 -      struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
 -      int result = 0, i;
 -      struct child_process rsync = CHILD_PROCESS_INIT;
 -      const char *args[10];
 -
 -      if (flags & TRANSPORT_PUSH_MIRROR)
 -              return error("rsync transport does not support mirror mode");
 -
 -      /* first push the objects */
 -
 -      strbuf_addstr(&buf, rsync_url(transport->url));
 -      strbuf_addch(&buf, '/');
 -
 -      rsync.argv = args;
 -      rsync.stdout_to_stderr = 1;
 -      i = 0;
 -      args[i++] = "rsync";
 -      args[i++] = "-a";
 -      if (flags & TRANSPORT_PUSH_DRY_RUN)
 -              args[i++] = "--dry-run";
 -      if (transport->verbose > 1)
 -              args[i++] = "-v";
 -      args[i++] = "--ignore-existing";
 -      args[i++] = "--exclude";
 -      args[i++] = "info";
 -      args[i++] = get_object_directory();
 -      args[i++] = buf.buf;
 -      args[i++] = NULL;
 -
 -      if (run_command(&rsync))
 -              return error("Could not push objects to %s",
 -                              rsync_url(transport->url));
 -
 -      /* copy the refs to the temporary directory; they could be packed. */
 -
 -      strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
 -      if (!mkdtemp(temp_dir.buf))
 -              die_errno ("Could not make temporary directory");
 -      strbuf_addch(&temp_dir, '/');
 -
 -      if (flags & TRANSPORT_PUSH_ALL) {
 -              if (for_each_ref(write_one_ref, &temp_dir))
 -                      return -1;
 -      } else if (write_refs_to_temp_dir(&temp_dir, refspec_nr, refspec))
 -              return -1;
 -
 -      i = 2;
 -      if (flags & TRANSPORT_PUSH_DRY_RUN)
 -              args[i++] = "--dry-run";
 -      if (!(flags & TRANSPORT_PUSH_FORCE))
 -              args[i++] = "--ignore-existing";
 -      args[i++] = temp_dir.buf;
 -      args[i++] = rsync_url(transport->url);
 -      args[i++] = NULL;
 -      if (run_command(&rsync))
 -              result = error("Could not push to %s",
 -                              rsync_url(transport->url));
 -
 -      if (remove_dir_recursively(&temp_dir, 0))
 -              warning ("Could not remove temporary directory %s.",
 -                              temp_dir.buf);
 -
 -      strbuf_release(&buf);
 -      strbuf_release(&temp_dir);
 -
 -      return result;
 -}
 -
  struct bundle_transport_data {
        int fd;
        struct bundle_header header;
@@@ -155,24 -481,17 +155,24 @@@ static int set_git_option(struct git_tr
        return 1;
  }
  
 -static int connect_setup(struct transport *transport, int for_push, int verbose)
 +static int connect_setup(struct transport *transport, int for_push)
  {
        struct git_transport_data *data = transport->data;
 +      int flags = transport->verbose > 0 ? CONNECT_VERBOSE : 0;
  
        if (data->conn)
                return 0;
  
 +      switch (transport->family) {
 +      case TRANSPORT_FAMILY_ALL: break;
 +      case TRANSPORT_FAMILY_IPV4: flags |= CONNECT_IPV4; break;
 +      case TRANSPORT_FAMILY_IPV6: flags |= CONNECT_IPV6; break;
 +      }
 +
        data->conn = git_connect(data->fd, transport->url,
                                 for_push ? data->options.receivepack :
                                 data->options.uploadpack,
 -                               verbose ? CONNECT_VERBOSE : 0);
 +                               flags);
  
        return 0;
  }
@@@ -182,7 -501,7 +182,7 @@@ static struct ref *get_refs_via_connect
        struct git_transport_data *data = transport->data;
        struct ref *refs;
  
 -      connect_setup(transport, for_push, 0);
 +      connect_setup(transport, for_push);
        get_remote_heads(data->fd[0], NULL, 0, &refs,
                         for_push ? REF_NORMAL : 0,
                         &data->extra_have,
@@@ -217,7 -536,7 +217,7 @@@ static int fetch_refs_via_pack(struct t
        args.update_shallow = data->options.update_shallow;
  
        if (!data->got_remote_heads) {
 -              connect_setup(transport, 0, 0);
 +              connect_setup(transport, 0);
                get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0,
                                 NULL, &data->shallow);
                data->got_remote_heads = 1;
@@@ -493,7 -812,7 +493,7 @@@ static int git_transport_push(struct tr
  
        if (!data->got_remote_heads) {
                struct ref *tmp_refs;
 -              connect_setup(transport, 1, 0);
 +              connect_setup(transport, 1);
  
                get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL,
                                 NULL, &data->shallow);
@@@ -665,7 -984,11 +665,7 @@@ struct transport *transport_get(struct 
        if (helper) {
                transport_helper_init(ret, helper);
        } else if (starts_with(url, "rsync:")) {
 -              transport_check_allowed("rsync");
 -              ret->get_refs_list = get_refs_via_rsync;
 -              ret->fetch = fetch_objs_via_rsync;
 -              ret->push = rsync_transport_push;
 -              ret->smart_options = NULL;
 +              die("git-over-rsync is no longer supported");
        } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {
                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
                transport_check_allowed("file");
@@@ -983,7 -1306,7 +983,7 @@@ int transport_fetch_refs(struct transpo
                 * This condition shouldn't be met in a non-deepening fetch
                 * (see builtin/fetch.c:quickfetch()).
                 */
-               heads = xmalloc(nr_refs * sizeof(*heads));
+               ALLOC_ARRAY(heads, nr_refs);
                for (rm = refs; rm; rm = rm->next)
                        heads[nr_heads++] = rm;
        }
@@@ -1027,7 -1350,7 +1027,7 @@@ int transport_disconnect(struct transpo
   */
  char *transport_anonymize_url(const char *url)
  {
-       char *anon_url, *scheme_prefix, *anon_part;
+       char *scheme_prefix, *anon_part;
        size_t anon_len, prefix_len = 0;
  
        anon_part = strchr(url, '@');
                        goto literal_copy;
                prefix_len = scheme_prefix - url + 3;
        }
-       anon_url = xcalloc(1, 1 + prefix_len + anon_len);
-       memcpy(anon_url, url, prefix_len);
-       memcpy(anon_url + prefix_len, anon_part, anon_len);
-       return anon_url;
+       return xstrfmt("%.*s%.*s", (int)prefix_len, url,
+                      (int)anon_len, anon_part);
  literal_copy:
        return xstrdup(url);
  }
diff --combined wrapper.c
index 29a45d2654e982dbbb3fc328e16c01713d8a7fb5,32c6c609da0fbc3a40d17b34de61bffa7c43700c..9afc1a021c224d4ff2230fffeb0e4f0882d4a99e
+++ b/wrapper.c
@@@ -152,6 -152,9 +152,9 @@@ void *xcalloc(size_t nmemb, size_t size
  {
        void *ret;
  
+       if (unsigned_mult_overflows(nmemb, size))
+               die("data too large to fit into virtual memory space");
        memory_limit_check(size * nmemb, 0);
        ret = calloc(nmemb, size);
        if (!ret && (!nmemb || !size))
@@@ -236,24 -239,8 +239,24 @@@ ssize_t xread(int fd, void *buf, size_
            len = MAX_IO_SIZE;
        while (1) {
                nr = read(fd, buf, len);
 -              if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
 -                      continue;
 +              if (nr < 0) {
 +                      if (errno == EINTR)
 +                              continue;
 +                      if (errno == EAGAIN || errno == EWOULDBLOCK) {
 +                              struct pollfd pfd;
 +                              pfd.events = POLLIN;
 +                              pfd.fd = fd;
 +                              /*
 +                               * it is OK if this poll() failed; we
 +                               * want to leave this infinite loop
 +                               * only when read() returns with
 +                               * success, or an expected failure,
 +                               * which would be checked by the next
 +                               * call to read(2).
 +                               */
 +                              poll(&pfd, 1, -1);
 +                      }
 +              }
                return nr;
        }
  }