Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Mon, 21 Jul 2014 19:35:39 +0000 (12:35 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 21 Jul 2014 19:35:39 +0000 (12:35 -0700)
* maint:
use xmemdupz() to allocate copies of strings given by start and length
use xcalloc() to allocate zero-initialized memory

1  2 
builtin/apply.c
builtin/blame.c
builtin/index-pack.c
compat/mingw.c
connect.c
http-backend.c
path.c
pathspec.c
diff --combined builtin/apply.c
index 5fd099ed40859ddfd838ea99191921d63e6eed12,622ee1674af592f4f843577f319c6883a1665f02..9f8f5bac072661adf9b911b091ab4202796e9a9e
@@@ -1281,7 -1281,9 +1281,7 @@@ static int parse_git_header(const char 
         */
        patch->def_name = git_header_name(line, len);
        if (patch->def_name && root) {
 -              char *s = xmalloc(root_len + strlen(patch->def_name) + 1);
 -              strcpy(s, root);
 -              strcpy(s + root_len, patch->def_name);
 +              char *s = xstrfmt("%s%s", root, patch->def_name);
                free(patch->def_name);
                patch->def_name = s;
        }
@@@ -2867,9 -2869,7 +2867,7 @@@ static int apply_binary_fragment(struc
        case BINARY_LITERAL_DEFLATED:
                clear_image(img);
                img->len = fragment->size;
-               img->buf = xmalloc(img->len+1);
-               memcpy(img->buf, fragment->patch, img->len);
-               img->buf[img->len] = '\0';
+               img->buf = xmemdupz(fragment->patch, img->len);
                return 0;
        }
        return -1;
@@@ -3084,15 -3084,13 +3082,15 @@@ static void prepare_fn_table(struct pat
        }
  }
  
 -static int checkout_target(struct cache_entry *ce, struct stat *st)
 +static int checkout_target(struct index_state *istate,
 +                         struct cache_entry *ce, struct stat *st)
  {
        struct checkout costate;
  
        memset(&costate, 0, sizeof(costate));
        costate.base_dir = "";
        costate.refresh_cache = 1;
 +      costate.istate = istate;
        if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
                return error(_("cannot checkout %s"), ce->name);
        return 0;
@@@ -3259,7 -3257,7 +3257,7 @@@ static int load_current(struct image *i
        if (lstat(name, &st)) {
                if (errno != ENOENT)
                        return error(_("%s: %s"), name, strerror(errno));
 -              if (checkout_target(ce, &st))
 +              if (checkout_target(&the_index, ce, &st))
                        return -1;
        }
        if (verify_index_match(ce, &st))
@@@ -3413,7 -3411,7 +3411,7 @@@ static int check_preimage(struct patch 
                }
                *ce = active_cache[pos];
                if (stat_ret < 0) {
 -                      if (checkout_target(*ce, st))
 +                      if (checkout_target(&the_index, *ce, st))
                                return -1;
                }
                if (!cached && verify_index_match(*ce, st))
@@@ -3646,7 -3644,7 +3644,7 @@@ static void build_fake_ancestor(struct 
  {
        struct patch *patch;
        struct index_state result = { NULL };
 -      int fd;
 +      static struct lock_file lock;
  
        /* Once we start supporting the reverse patch, it may be
         * worth showing the new sha1 prefix, but until then...
                        die ("Could not add %s to temporary index", name);
        }
  
 -      fd = open(filename, O_WRONLY | O_CREAT, 0666);
 -      if (fd < 0 || write_index(&result, fd) || close(fd))
 +      hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
 +      if (write_locked_index(&result, &lock, COMMIT_LOCK))
                die ("Could not write temporary index to %s", filename);
  
        discard_index(&result);
@@@ -3847,10 -3845,9 +3845,10 @@@ static void add_index_file(const char *
        ce->ce_flags = create_ce_flags(0);
        ce->ce_namelen = namelen;
        if (S_ISGITLINK(mode)) {
 -              const char *s = buf;
 +              const char *s;
  
 -              if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
 +              if (!skip_prefix(buf, "Subproject commit ", &s) ||
 +                  get_sha1_hex(s, ce->sha1))
                        die(_("corrupt patch for submodule %s"), path);
        } else {
                if (!cached) {
@@@ -4504,7 -4501,8 +4502,7 @@@ int cmd_apply(int argc, const char **ar
        }
  
        if (update_index) {
 -              if (write_cache(newfd, active_cache, active_nr) ||
 -                  commit_locked_index(&lock_file))
 +              if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die(_("Unable to write new index file"));
        }
  
diff --combined builtin/blame.c
index c59e70202175fc89992c23b48ea5f0738664a2f6,6a284ce46b4f3b46a47d033fca607cae710806bc..32ce05f6159bb9a282e20f314a7d4aa079eb892e
@@@ -1,8 -1,7 +1,8 @@@
  /*
   * Blame
   *
 - * Copyright (c) 2006, Junio C Hamano
 + * Copyright (c) 2006, 2014 by its authors
 + * See COPYING for licensing conditions
   */
  
  #include "cache.h"
@@@ -19,9 -18,7 +19,9 @@@
  #include "cache-tree.h"
  #include "string-list.h"
  #include "mailmap.h"
 +#include "mergesort.h"
  #include "parse-options.h"
 +#include "prio-queue.h"
  #include "utf8.h"
  #include "userdiff.h"
  #include "line-range.h"
@@@ -86,42 -83,11 +86,42 @@@ static unsigned blame_copy_score
   */
  struct origin {
        int refcnt;
 +      /* Record preceding blame record for this blob */
        struct origin *previous;
 +      /* origins are put in a list linked via `next' hanging off the
 +       * corresponding commit's util field in order to make finding
 +       * them fast.  The presence in this chain does not count
 +       * towards the origin's reference count.  It is tempting to
 +       * let it count as long as the commit is pending examination,
 +       * but even under circumstances where the commit will be
 +       * present multiple times in the priority queue of unexamined
 +       * commits, processing the first instance will not leave any
 +       * work requiring the origin data for the second instance.  An
 +       * interspersed commit changing that would have to be
 +       * preexisting with a different ancestry and with the same
 +       * commit date in order to wedge itself between two instances
 +       * of the same commit in the priority queue _and_ produce
 +       * blame entries relevant for it.  While we don't want to let
 +       * us get tripped up by this case, it certainly does not seem
 +       * worth optimizing for.
 +       */
 +      struct origin *next;
        struct commit *commit;
 +      /* `suspects' contains blame entries that may be attributed to
 +       * this origin's commit or to parent commits.  When a commit
 +       * is being processed, all suspects will be moved, either by
 +       * assigning them to an origin in a different commit, or by
 +       * shipping them to the scoreboard's ent list because they
 +       * cannot be attributed to a different commit.
 +       */
 +      struct blame_entry *suspects;
        mmfile_t file;
        unsigned char blob_sha1[20];
        unsigned mode;
 +      /* guilty gets set when shipping any suspects to the final
 +       * blame list instead of other commits
 +       */
 +      char guilty;
        char path[FLEX_ARRAY];
  };
  
@@@ -210,22 -176,10 +210,22 @@@ static inline struct origin *origin_inc
  static void origin_decref(struct origin *o)
  {
        if (o && --o->refcnt <= 0) {
 +              struct origin *p, *l = NULL;
                if (o->previous)
                        origin_decref(o->previous);
                free(o->file.ptr);
 -              free(o);
 +              /* Should be present exactly once in commit chain */
 +              for (p = o->commit->util; p; l = p, p = p->next) {
 +                      if (p == o) {
 +                              if (l)
 +                                      l->next = p->next;
 +                              else
 +                                      o->commit->util = p->next;
 +                              free(o);
 +                              return;
 +                      }
 +              }
 +              die("internal error in blame::origin_decref");
        }
  }
  
@@@ -239,12 -193,8 +239,12 @@@ static void drop_origin_blob(struct ori
  
  /*
   * Each group of lines is described by a blame_entry; it can be split
 - * as we pass blame to the parents.  They form a linked list in the
 - * scoreboard structure, sorted by the target line number.
 + * as we pass blame to the parents.  They are arranged in linked lists
 + * kept as `suspects' of some unprocessed origin, or entered (when the
 + * blame origin has been finalized) into the scoreboard structure.
 + * While the scoreboard structure is only sorted at the end of
 + * processing (according to final image line number), the lists
 + * attached to an origin are sorted by the target line number.
   */
  struct blame_entry {
        struct blame_entry *next;
        /* the commit that introduced this group into the final image */
        struct origin *suspect;
  
 -      /* true if the suspect is truly guilty; false while we have not
 -       * checked if the group came from one of its parents.
 -       */
 -      char guilty;
 -
 -      /* true if the entry has been scanned for copies in the current parent
 -       */
 -      char scanned;
 -
        /* the line number of the first line of this group in the
         * suspect's file; internally all line numbers are 0 based.
         */
        unsigned score;
  };
  
 +/*
 + * Any merge of blames happens on lists of blames that arrived via
 + * different parents in a single suspect.  In this case, we want to
 + * sort according to the suspect line numbers as opposed to the final
 + * image line numbers.  The function body is somewhat longish because
 + * it avoids unnecessary writes.
 + */
 +
 +static struct blame_entry *blame_merge(struct blame_entry *list1,
 +                                     struct blame_entry *list2)
 +{
 +      struct blame_entry *p1 = list1, *p2 = list2,
 +              **tail = &list1;
 +
 +      if (!p1)
 +              return p2;
 +      if (!p2)
 +              return p1;
 +
 +      if (p1->s_lno <= p2->s_lno) {
 +              do {
 +                      tail = &p1->next;
 +                      if ((p1 = *tail) == NULL) {
 +                              *tail = p2;
 +                              return list1;
 +                      }
 +              } while (p1->s_lno <= p2->s_lno);
 +      }
 +      for (;;) {
 +              *tail = p2;
 +              do {
 +                      tail = &p2->next;
 +                      if ((p2 = *tail) == NULL)  {
 +                              *tail = p1;
 +                              return list1;
 +                      }
 +              } while (p1->s_lno > p2->s_lno);
 +              *tail = p1;
 +              do {
 +                      tail = &p1->next;
 +                      if ((p1 = *tail) == NULL) {
 +                              *tail = p2;
 +                              return list1;
 +                      }
 +              } while (p1->s_lno <= p2->s_lno);
 +      }
 +}
 +
 +static void *get_next_blame(const void *p)
 +{
 +      return ((struct blame_entry *)p)->next;
 +}
 +
 +static void set_next_blame(void *p1, void *p2)
 +{
 +      ((struct blame_entry *)p1)->next = p2;
 +}
 +
 +/*
 + * Final image line numbers are all different, so we don't need a
 + * three-way comparison here.
 + */
 +
 +static int compare_blame_final(const void *p1, const void *p2)
 +{
 +      return ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno
 +              ? 1 : -1;
 +}
 +
 +static int compare_blame_suspect(const void *p1, const void *p2)
 +{
 +      const struct blame_entry *s1 = p1, *s2 = p2;
 +      /*
 +       * to allow for collating suspects, we sort according to the
 +       * respective pointer value as the primary sorting criterion.
 +       * The actual relation is pretty unimportant as long as it
 +       * establishes a total order.  Comparing as integers gives us
 +       * that.
 +       */
 +      if (s1->suspect != s2->suspect)
 +              return (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;
 +      if (s1->s_lno == s2->s_lno)
 +              return 0;
 +      return s1->s_lno > s2->s_lno ? 1 : -1;
 +}
 +
 +static struct blame_entry *blame_sort(struct blame_entry *head,
 +                                    int (*compare_fn)(const void *, const void *))
 +{
 +      return llist_mergesort (head, get_next_blame, set_next_blame, compare_fn);
 +}
 +
 +static int compare_commits_by_reverse_commit_date(const void *a,
 +                                                const void *b,
 +                                                void *c)
 +{
 +      return -compare_commits_by_commit_date(a, b, c);
 +}
 +
  /*
   * The current state of the blame assignment.
   */
  struct scoreboard {
        /* the final commit (i.e. where we started digging from) */
        struct commit *final;
 +      /* Priority queue for commits with unassigned blame records */
 +      struct prio_queue commits;
        struct rev_info *revs;
        const char *path;
  
@@@ -410,6 -268,7 +410,6 @@@ static void coalesce(struct scoreboard 
  
        for (ent = sb->ent; ent && (next = ent->next); ent = next) {
                if (ent->suspect == next->suspect &&
 -                  ent->guilty == next->guilty &&
                    ent->s_lno + ent->num_lines == next->s_lno) {
                        ent->num_lines += next->num_lines;
                        ent->next = next->next;
                sanity_check_refcnt(sb);
  }
  
 +/*
 + * Merge the given sorted list of blames into a preexisting origin.
 + * If there were no previous blames to that commit, it is entered into
 + * the commit priority queue of the score board.
 + */
 +
 +static void queue_blames(struct scoreboard *sb, struct origin *porigin,
 +                       struct blame_entry *sorted)
 +{
 +      if (porigin->suspects)
 +              porigin->suspects = blame_merge(porigin->suspects, sorted);
 +      else {
 +              struct origin *o;
 +              for (o = porigin->commit->util; o; o = o->next) {
 +                      if (o->suspects) {
 +                              porigin->suspects = sorted;
 +                              return;
 +                      }
 +              }
 +              porigin->suspects = sorted;
 +              prio_queue_put(&sb->commits, porigin->commit);
 +      }
 +}
 +
  /*
   * Given a commit and a path in it, create a new origin structure.
   * The callers that add blame to the scoreboard should use
@@@ -460,32 -295,23 +460,32 @@@ static struct origin *make_origin(struc
        o = xcalloc(1, sizeof(*o) + strlen(path) + 1);
        o->commit = commit;
        o->refcnt = 1;
 +      o->next = commit->util;
 +      commit->util = o;
        strcpy(o->path, path);
        return o;
  }
  
  /*
   * Locate an existing origin or create a new one.
 + * This moves the origin to front position in the commit util list.
   */
  static struct origin *get_origin(struct scoreboard *sb,
                                 struct commit *commit,
                                 const char *path)
  {
 -      struct blame_entry *e;
 +      struct origin *o, *l;
  
 -      for (e = sb->ent; e; e = e->next) {
 -              if (e->suspect->commit == commit &&
 -                  !strcmp(e->suspect->path, path))
 -                      return origin_incref(e->suspect);
 +      for (o = commit->util, l = NULL; o; l = o, o = o->next) {
 +              if (!strcmp(o->path, path)) {
 +                      /* bump to front */
 +                      if (l) {
 +                              l->next = o->next;
 +                              o->next = commit->util;
 +                              commit->util = o;
 +                      }
 +                      return origin_incref(o);
 +              }
        }
        return make_origin(commit, path);
  }
@@@ -524,19 -350,41 +524,19 @@@ static struct origin *find_origin(struc
                                  struct commit *parent,
                                  struct origin *origin)
  {
 -      struct origin *porigin = NULL;
 +      struct origin *porigin;
        struct diff_options diff_opts;
        const char *paths[2];
  
 -      if (parent->util) {
 -              /*
 -               * Each commit object can cache one origin in that
 -               * commit.  This is a freestanding copy of origin and
 -               * not refcounted.
 -               */
 -              struct origin *cached = parent->util;
 -              if (!strcmp(cached->path, origin->path)) {
 +      /* First check any existing origins */
 +      for (porigin = parent->util; porigin; porigin = porigin->next)
 +              if (!strcmp(porigin->path, origin->path)) {
                        /*
                         * The same path between origin and its parent
                         * without renaming -- the most common case.
                         */
 -                      porigin = get_origin(sb, parent, cached->path);
 -
 -                      /*
 -                       * If the origin was newly created (i.e. get_origin
 -                       * would call make_origin if none is found in the
 -                       * scoreboard), it does not know the blob_sha1/mode,
 -                       * so copy it.  Otherwise porigin was in the
 -                       * scoreboard and already knows blob_sha1/mode.
 -                       */
 -                      if (porigin->refcnt == 1) {
 -                              hashcpy(porigin->blob_sha1, cached->blob_sha1);
 -                              porigin->mode = cached->mode;
 -                      }
 -                      return porigin;
 +                      return origin_incref (porigin);
                }
 -              /* otherwise it was not very useful; free it */
 -              free(parent->util);
 -              parent->util = NULL;
 -      }
  
        /* See if the origin->path is different between parent
         * and origin first.  Most of the time they are the
        }
        diff_flush(&diff_opts);
        free_pathspec(&diff_opts.pathspec);
 -      if (porigin) {
 -              /*
 -               * Create a freestanding copy that is not part of
 -               * the refcounted origin found in the scoreboard, and
 -               * cache it in the commit.
 -               */
 -              struct origin *cached;
 -
 -              cached = make_origin(porigin->commit, porigin->path);
 -              hashcpy(cached->blob_sha1, porigin->blob_sha1);
 -              cached->mode = porigin->mode;
 -              parent->util = cached;
 -      }
        return porigin;
  }
  
@@@ -648,31 -509,46 +648,31 @@@ static struct origin *find_rename(struc
  }
  
  /*
 - * Link in a new blame entry to the scoreboard.  Entries that cover the
 - * same line range have been removed from the scoreboard previously.
 + * Append a new blame entry to a given output queue.
   */
 -static void add_blame_entry(struct scoreboard *sb, struct blame_entry *e)
 +static void add_blame_entry(struct blame_entry ***queue, struct blame_entry *e)
  {
 -      struct blame_entry *ent, *prev = NULL;
 -
        origin_incref(e->suspect);
  
 -      for (ent = sb->ent; ent && ent->lno < e->lno; ent = ent->next)
 -              prev = ent;
 -
 -      /* prev, if not NULL, is the last one that is below e */
 -
 -      if (prev) {
 -              e->next = prev->next;
 -              prev->next = e;
 -      }
 -      else {
 -              e->next = sb->ent;
 -              sb->ent = e;
 -      }
 +      e->next = **queue;
 +      **queue = e;
 +      *queue = &e->next;
  }
  
  /*
   * src typically is on-stack; we want to copy the information in it to
 - * a malloced blame_entry that is already on the linked list of the
 - * scoreboard.  The origin of dst loses a refcnt while the origin of src
 - * gains one.
 + * a malloced blame_entry that gets added to the given queue.  The
 + * origin of dst loses a refcnt.
   */
 -static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
 +static void dup_entry(struct blame_entry ***queue,
 +                    struct blame_entry *dst, struct blame_entry *src)
  {
 -      struct blame_entry *n;
 -
 -      n = dst->next;
        origin_incref(src->suspect);
        origin_decref(dst->suspect);
        memcpy(dst, src, sizeof(*src));
 -      dst->next = n;
 -      dst->score = 0;
 +      dst->next = **queue;
 +      **queue = dst;
 +      *queue = &dst->next;
  }
  
  static const char *nth_line(struct scoreboard *sb, long lno)
@@@ -744,11 -620,10 +744,11 @@@ static void split_overlap(struct blame_
  
  /*
   * split_overlap() divided an existing blame e into up to three parts
 - * in split.  Adjust the linked list of blames in the scoreboard to
 + * in split.  Any assigned blame is moved to queue to
   * reflect the split.
   */
 -static void split_blame(struct scoreboard *sb,
 +static void split_blame(struct blame_entry ***blamed,
 +                      struct blame_entry ***unblamed,
                        struct blame_entry *split,
                        struct blame_entry *e)
  {
  
        if (split[0].suspect && split[2].suspect) {
                /* The first part (reuse storage for the existing entry e) */
 -              dup_entry(e, &split[0]);
 +              dup_entry(unblamed, e, &split[0]);
  
                /* The last part -- me */
                new_entry = xmalloc(sizeof(*new_entry));
                memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
 -              add_blame_entry(sb, new_entry);
 +              add_blame_entry(unblamed, new_entry);
  
                /* ... and the middle part -- parent */
                new_entry = xmalloc(sizeof(*new_entry));
                memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
 -              add_blame_entry(sb, new_entry);
 +              add_blame_entry(blamed, new_entry);
        }
        else if (!split[0].suspect && !split[2].suspect)
                /*
                 * The parent covers the entire area; reuse storage for
                 * e and replace it with the parent.
                 */
 -              dup_entry(e, &split[1]);
 +              dup_entry(blamed, e, &split[1]);
        else if (split[0].suspect) {
                /* me and then parent */
 -              dup_entry(e, &split[0]);
 +              dup_entry(unblamed, e, &split[0]);
  
                new_entry = xmalloc(sizeof(*new_entry));
                memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
 -              add_blame_entry(sb, new_entry);
 +              add_blame_entry(blamed, new_entry);
        }
        else {
                /* parent and then me */
 -              dup_entry(e, &split[1]);
 +              dup_entry(blamed, e, &split[1]);
  
                new_entry = xmalloc(sizeof(*new_entry));
                memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
 -              add_blame_entry(sb, new_entry);
 -      }
 -
 -      if (DEBUG) { /* sanity */
 -              struct blame_entry *ent;
 -              int lno = sb->ent->lno, corrupt = 0;
 -
 -              for (ent = sb->ent; ent; ent = ent->next) {
 -                      if (lno != ent->lno)
 -                              corrupt = 1;
 -                      if (ent->s_lno < 0)
 -                              corrupt = 1;
 -                      lno += ent->num_lines;
 -              }
 -              if (corrupt) {
 -                      lno = sb->ent->lno;
 -                      for (ent = sb->ent; ent; ent = ent->next) {
 -                              printf("L %8d l %8d n %8d\n",
 -                                     lno, ent->lno, ent->num_lines);
 -                              lno = ent->lno + ent->num_lines;
 -                      }
 -                      die("oops");
 -              }
 +              add_blame_entry(unblamed, new_entry);
        }
  }
  
@@@ -805,146 -702,74 +805,146 @@@ static void decref_split(struct blame_e
  }
  
  /*
 - * Helper for blame_chunk().  blame_entry e is known to overlap with
 - * the patch hunk; split it and pass blame to the parent.
 + * reverse_blame reverses the list given in head, appending tail.
 + * That allows us to build lists in reverse order, then reverse them
 + * afterwards.  This can be faster than building the list in proper
 + * order right away.  The reason is that building in proper order
 + * requires writing a link in the _previous_ element, while building
 + * in reverse order just requires placing the list head into the
 + * _current_ element.
   */
 -static void blame_overlap(struct scoreboard *sb, struct blame_entry *e,
 -                        int tlno, int plno, int same,
 -                        struct origin *parent)
 -{
 -      struct blame_entry split[3];
  
 -      split_overlap(split, e, tlno, plno, same, parent);
 -      if (split[1].suspect)
 -              split_blame(sb, split, e);
 -      decref_split(split);
 -}
 -
 -/*
 - * Find the line number of the last line the target is suspected for.
 - */
 -static int find_last_in_target(struct scoreboard *sb, struct origin *target)
 +static struct blame_entry *reverse_blame(struct blame_entry *head,
 +                                       struct blame_entry *tail)
  {
 -      struct blame_entry *e;
 -      int last_in_target = -1;
 -
 -      for (e = sb->ent; e; e = e->next) {
 -              if (e->guilty || e->suspect != target)
 -                      continue;
 -              if (last_in_target < e->s_lno + e->num_lines)
 -                      last_in_target = e->s_lno + e->num_lines;
 +      while (head) {
 +              struct blame_entry *next = head->next;
 +              head->next = tail;
 +              tail = head;
 +              head = next;
        }
 -      return last_in_target;
 +      return tail;
  }
  
  /*
   * Process one hunk from the patch between the current suspect for
 - * blame_entry e and its parent.  Find and split the overlap, and
 - * pass blame to the overlapping part to the parent.
 + * blame_entry e and its parent.  This first blames any unfinished
 + * entries before the chunk (which is where target and parent start
 + * differing) on the parent, and then splits blame entries at the
 + * start and at the end of the difference region.  Since use of -M and
 + * -C options may lead to overlapping/duplicate source line number
 + * ranges, all we can rely on from sorting/merging is the order of the
 + * first suspect line number.
   */
 -static void blame_chunk(struct scoreboard *sb,
 -                      int tlno, int plno, int same,
 -                      struct origin *target, struct origin *parent)
 +static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
 +                      int tlno, int offset, int same,
 +                      struct origin *parent)
  {
 -      struct blame_entry *e;
 +      struct blame_entry *e = **srcq;
 +      struct blame_entry *samep = NULL, *diffp = NULL;
  
 -      for (e = sb->ent; e; e = e->next) {
 -              if (e->guilty || e->suspect != target)
 -                      continue;
 -              if (same <= e->s_lno)
 -                      continue;
 -              if (tlno < e->s_lno + e->num_lines)
 -                      blame_overlap(sb, e, tlno, plno, same, parent);
 +      while (e && e->s_lno < tlno) {
 +              struct blame_entry *next = e->next;
 +              /*
 +               * current record starts before differing portion.  If
 +               * it reaches into it, we need to split it up and
 +               * examine the second part separately.
 +               */
 +              if (e->s_lno + e->num_lines > tlno) {
 +                      /* Move second half to a new record */
 +                      int len = tlno - e->s_lno;
 +                      struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
 +                      n->suspect = e->suspect;
 +                      n->lno = e->lno + len;
 +                      n->s_lno = e->s_lno + len;
 +                      n->num_lines = e->num_lines - len;
 +                      e->num_lines = len;
 +                      e->score = 0;
 +                      /* Push new record to diffp */
 +                      n->next = diffp;
 +                      diffp = n;
 +              } else
 +                      origin_decref(e->suspect);
 +              /* Pass blame for everything before the differing
 +               * chunk to the parent */
 +              e->suspect = origin_incref(parent);
 +              e->s_lno += offset;
 +              e->next = samep;
 +              samep = e;
 +              e = next;
        }
 +      /*
 +       * As we don't know how much of a common stretch after this
 +       * diff will occur, the currently blamed parts are all that we
 +       * can assign to the parent for now.
 +       */
 +
 +      if (samep) {
 +              **dstq = reverse_blame(samep, **dstq);
 +              *dstq = &samep->next;
 +      }
 +      /*
 +       * Prepend the split off portions: everything after e starts
 +       * after the blameable portion.
 +       */
 +      e = reverse_blame(diffp, e);
 +
 +      /*
 +       * Now retain records on the target while parts are different
 +       * from the parent.
 +       */
 +      samep = NULL;
 +      diffp = NULL;
 +      while (e && e->s_lno < same) {
 +              struct blame_entry *next = e->next;
 +
 +              /*
 +               * If current record extends into sameness, need to split.
 +               */
 +              if (e->s_lno + e->num_lines > same) {
 +                      /*
 +                       * Move second half to a new record to be
 +                       * processed by later chunks
 +                       */
 +                      int len = same - e->s_lno;
 +                      struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
 +                      n->suspect = origin_incref(e->suspect);
 +                      n->lno = e->lno + len;
 +                      n->s_lno = e->s_lno + len;
 +                      n->num_lines = e->num_lines - len;
 +                      e->num_lines = len;
 +                      e->score = 0;
 +                      /* Push new record to samep */
 +                      n->next = samep;
 +                      samep = n;
 +              }
 +              e->next = diffp;
 +              diffp = e;
 +              e = next;
 +      }
 +      **srcq = reverse_blame(diffp, reverse_blame(samep, e));
 +      /* Move across elements that are in the unblamable portion */
 +      if (diffp)
 +              *srcq = &diffp->next;
  }
  
  struct blame_chunk_cb_data {
 -      struct scoreboard *sb;
 -      struct origin *target;
        struct origin *parent;
 -      long plno;
 -      long tlno;
 +      long offset;
 +      struct blame_entry **dstq;
 +      struct blame_entry **srcq;
  };
  
 +/* diff chunks are from parent to target */
  static int blame_chunk_cb(long start_a, long count_a,
                          long start_b, long count_b, void *data)
  {
        struct blame_chunk_cb_data *d = data;
 -      blame_chunk(d->sb, d->tlno, d->plno, start_b, d->target, d->parent);
 -      d->plno = start_a + count_a;
 -      d->tlno = start_b + count_b;
 +      if (start_a - start_b != d->offset)
 +              die("internal error in blame::blame_chunk_cb");
 +      blame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,
 +                  start_b + count_b, d->parent);
 +      d->offset = start_a + count_a - (start_b + count_b);
        return 0;
  }
  
   * for the lines it is suspected to its parent.  Run diff to find
   * which lines came from parent and pass blame for them.
   */
 -static int pass_blame_to_parent(struct scoreboard *sb,
 -                              struct origin *target,
 -                              struct origin *parent)
 +static void pass_blame_to_parent(struct scoreboard *sb,
 +                               struct origin *target,
 +                               struct origin *parent)
  {
 -      int last_in_target;
        mmfile_t file_p, file_o;
        struct blame_chunk_cb_data d;
 +      struct blame_entry *newdest = NULL;
  
 -      memset(&d, 0, sizeof(d));
 -      d.sb = sb; d.target = target; d.parent = parent;
 -      last_in_target = find_last_in_target(sb, target);
 -      if (last_in_target < 0)
 -              return 1; /* nothing remains for this target */
 +      if (!target->suspects)
 +              return; /* nothing remains for this target */
 +
 +      d.parent = parent;
 +      d.offset = 0;
 +      d.dstq = &newdest; d.srcq = &target->suspects;
  
        fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
        fill_origin_blob(&sb->revs->diffopt, target, &file_o);
        num_get_patch++;
  
        diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d);
 -      /* The rest (i.e. anything after tlno) are the same as the parent */
 -      blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
 +      /* The rest are the same as the parent */
 +      blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);
 +      *d.dstq = NULL;
 +      queue_blames(sb, parent, newdest);
  
 -      return 0;
 +      return;
  }
  
  /*
@@@ -1123,80 -945,43 +1123,80 @@@ static void find_copy_in_blob(struct sc
        handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
  }
  
 +/* Move all blame entries from list *source that have a score smaller
 + * than score_min to the front of list *small.
 + * Returns a pointer to the link pointing to the old head of the small list.
 + */
 +
 +static struct blame_entry **filter_small(struct scoreboard *sb,
 +                                       struct blame_entry **small,
 +                                       struct blame_entry **source,
 +                                       unsigned score_min)
 +{
 +      struct blame_entry *p = *source;
 +      struct blame_entry *oldsmall = *small;
 +      while (p) {
 +              if (ent_score(sb, p) <= score_min) {
 +                      *small = p;
 +                      small = &p->next;
 +                      p = *small;
 +              } else {
 +                      *source = p;
 +                      source = &p->next;
 +                      p = *source;
 +              }
 +      }
 +      *small = oldsmall;
 +      *source = NULL;
 +      return small;
 +}
 +
  /*
   * See if lines currently target is suspected for can be attributed to
   * parent.
   */
 -static int find_move_in_parent(struct scoreboard *sb,
 -                             struct origin *target,
 -                             struct origin *parent)
 +static void find_move_in_parent(struct scoreboard *sb,
 +                              struct blame_entry ***blamed,
 +                              struct blame_entry **toosmall,
 +                              struct origin *target,
 +                              struct origin *parent)
  {
 -      int last_in_target, made_progress;
        struct blame_entry *e, split[3];
 +      struct blame_entry *unblamed = target->suspects;
 +      struct blame_entry *leftover = NULL;
        mmfile_t file_p;
  
 -      last_in_target = find_last_in_target(sb, target);
 -      if (last_in_target < 0)
 -              return 1; /* nothing remains for this target */
 +      if (!unblamed)
 +              return; /* nothing remains for this target */
  
        fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
        if (!file_p.ptr)
 -              return 0;
 +              return;
  
 -      made_progress = 1;
 -      while (made_progress) {
 -              made_progress = 0;
 -              for (e = sb->ent; e; e = e->next) {
 -                      if (e->guilty || e->suspect != target ||
 -                          ent_score(sb, e) < blame_move_score)
 -                              continue;
 +      /* At each iteration, unblamed has a NULL-terminated list of
 +       * entries that have not yet been tested for blame.  leftover
 +       * contains the reversed list of entries that have been tested
 +       * without being assignable to the parent.
 +       */
 +      do {
 +              struct blame_entry **unblamedtail = &unblamed;
 +              struct blame_entry *next;
 +              for (e = unblamed; e; e = next) {
 +                      next = e->next;
                        find_copy_in_blob(sb, e, parent, split, &file_p);
                        if (split[1].suspect &&
                            blame_move_score < ent_score(sb, &split[1])) {
 -                              split_blame(sb, split, e);
 -                              made_progress = 1;
 +                              split_blame(blamed, &unblamedtail, split, e);
 +                      } else {
 +                              e->next = leftover;
 +                              leftover = e;
                        }
                        decref_split(split);
                }
 -      }
 -      return 0;
 +              *unblamedtail = NULL;
 +              toosmall = filter_small(sb, toosmall, &unblamed, blame_move_score);
 +      } while (unblamed);
 +      target->suspects = reverse_blame(leftover, NULL);
  }
  
  struct blame_list {
   * Count the number of entries the target is suspected for,
   * and prepare a list of entry and the best split.
   */
 -static struct blame_list *setup_blame_list(struct scoreboard *sb,
 -                                         struct origin *target,
 -                                         int min_score,
 +static struct blame_list *setup_blame_list(struct blame_entry *unblamed,
                                           int *num_ents_p)
  {
        struct blame_entry *e;
        int num_ents, i;
        struct blame_list *blame_list = NULL;
  
 -      for (e = sb->ent, num_ents = 0; e; e = e->next)
 -              if (!e->scanned && !e->guilty &&
 -                  e->suspect == target &&
 -                  min_score < ent_score(sb, e))
 -                      num_ents++;
 +      for (e = unblamed, num_ents = 0; e; e = e->next)
 +              num_ents++;
        if (num_ents) {
                blame_list = xcalloc(num_ents, sizeof(struct blame_list));
 -              for (e = sb->ent, i = 0; e; e = e->next)
 -                      if (!e->scanned && !e->guilty &&
 -                          e->suspect == target &&
 -                          min_score < ent_score(sb, e))
 -                              blame_list[i++].ent = e;
 +              for (e = unblamed, i = 0; e; e = e->next)
 +                      blame_list[i++].ent = e;
        }
        *num_ents_p = num_ents;
        return blame_list;
  }
  
 -/*
 - * Reset the scanned status on all entries.
 - */
 -static void reset_scanned_flag(struct scoreboard *sb)
 -{
 -      struct blame_entry *e;
 -      for (e = sb->ent; e; e = e->next)
 -              e->scanned = 0;
 -}
 -
  /*
   * For lines target is suspected for, see if we can find code movement
   * across file boundary from the parent commit.  porigin is the path
   * in the parent we already tried.
   */
 -static int find_copy_in_parent(struct scoreboard *sb,
 -                             struct origin *target,
 -                             struct commit *parent,
 -                             struct origin *porigin,
 -                             int opt)
 +static void find_copy_in_parent(struct scoreboard *sb,
 +                              struct blame_entry ***blamed,
 +                              struct blame_entry **toosmall,
 +                              struct origin *target,
 +                              struct commit *parent,
 +                              struct origin *porigin,
 +                              int opt)
  {
        struct diff_options diff_opts;
        int i, j;
 -      int retval;
        struct blame_list *blame_list;
        int num_ents;
 +      struct blame_entry *unblamed = target->suspects;
 +      struct blame_entry *leftover = NULL;
  
 -      blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
 -      if (!blame_list)
 -              return 1; /* nothing remains for this target */
 +      if (!unblamed)
 +              return; /* nothing remains for this target */
  
        diff_setup(&diff_opts);
        DIFF_OPT_SET(&diff_opts, RECURSIVE);
        if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
                diffcore_std(&diff_opts);
  
 -      retval = 0;
 -      while (1) {
 -              int made_progress = 0;
 +      do {
 +              struct blame_entry **unblamedtail = &unblamed;
 +              blame_list = setup_blame_list(unblamed, &num_ents);
  
                for (i = 0; i < diff_queued_diff.nr; i++) {
                        struct diff_filepair *p = diff_queued_diff.queue[i];
                        struct blame_entry *split = blame_list[j].split;
                        if (split[1].suspect &&
                            blame_copy_score < ent_score(sb, &split[1])) {
 -                              split_blame(sb, split, blame_list[j].ent);
 -                              made_progress = 1;
 +                              split_blame(blamed, &unblamedtail, split,
 +                                          blame_list[j].ent);
 +                      } else {
 +                              blame_list[j].ent->next = leftover;
 +                              leftover = blame_list[j].ent;
                        }
 -                      else
 -                              blame_list[j].ent->scanned = 1;
                        decref_split(split);
                }
                free(blame_list);
 -
 -              if (!made_progress)
 -                      break;
 -              blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
 -              if (!blame_list) {
 -                      retval = 1;
 -                      break;
 -              }
 -      }
 -      reset_scanned_flag(sb);
 +              *unblamedtail = NULL;
 +              toosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score);
 +      } while (unblamed);
 +      target->suspects = reverse_blame(leftover, NULL);
        diff_flush(&diff_opts);
        free_pathspec(&diff_opts.pathspec);
 -      return retval;
  }
  
  /*
  static void pass_whole_blame(struct scoreboard *sb,
                             struct origin *origin, struct origin *porigin)
  {
 -      struct blame_entry *e;
 +      struct blame_entry *e, *suspects;
  
        if (!porigin->file.ptr && origin->file.ptr) {
                /* Steal its file */
                porigin->file = origin->file;
                origin->file.ptr = NULL;
        }
 -      for (e = sb->ent; e; e = e->next) {
 -              if (e->suspect != origin)
 -                      continue;
 +      suspects = origin->suspects;
 +      origin->suspects = NULL;
 +      for (e = suspects; e; e = e->next) {
                origin_incref(porigin);
                origin_decref(e->suspect);
                e->suspect = porigin;
        }
 +      queue_blames(sb, porigin, suspects);
  }
  
  /*
@@@ -1378,27 -1184,6 +1378,27 @@@ static int num_scapegoats(struct rev_in
        return cnt;
  }
  
 +/* Distribute collected unsorted blames to the respected sorted lists
 + * in the various origins.
 + */
 +static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)
 +{
 +      blamed = blame_sort(blamed, compare_blame_suspect);
 +      while (blamed)
 +      {
 +              struct origin *porigin = blamed->suspect;
 +              struct blame_entry *suspects = NULL;
 +              do {
 +                      struct blame_entry *next = blamed->next;
 +                      blamed->next = suspects;
 +                      suspects = blamed;
 +                      blamed = next;
 +              } while (blamed && blamed->suspect == porigin);
 +              suspects = reverse_blame(suspects, NULL);
 +              queue_blames(sb, porigin, suspects);
 +      }
 +}
 +
  #define MAXSG 16
  
  static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
        struct commit_list *sg;
        struct origin *sg_buf[MAXSG];
        struct origin *porigin, **sg_origin = sg_buf;
 +      struct blame_entry *toosmall = NULL;
 +      struct blame_entry *blames, **blametail = &blames;
  
        num_sg = num_scapegoats(revs, commit);
        if (!num_sg)
                        origin_incref(porigin);
                        origin->previous = porigin;
                }
 -              if (pass_blame_to_parent(sb, origin, porigin))
 +              pass_blame_to_parent(sb, origin, porigin);
 +              if (!origin->suspects)
                        goto finish;
        }
  
        /*
         * Optionally find moves in parents' files.
         */
 -      if (opt & PICKAXE_BLAME_MOVE)
 -              for (i = 0, sg = first_scapegoat(revs, commit);
 -                   i < num_sg && sg;
 -                   sg = sg->next, i++) {
 -                      struct origin *porigin = sg_origin[i];
 -                      if (!porigin)
 -                              continue;
 -                      if (find_move_in_parent(sb, origin, porigin))
 -                              goto finish;
 +      if (opt & PICKAXE_BLAME_MOVE) {
 +              filter_small(sb, &toosmall, &origin->suspects, blame_move_score);
 +              if (origin->suspects) {
 +                      for (i = 0, sg = first_scapegoat(revs, commit);
 +                           i < num_sg && sg;
 +                           sg = sg->next, i++) {
 +                              struct origin *porigin = sg_origin[i];
 +                              if (!porigin)
 +                                      continue;
 +                              find_move_in_parent(sb, &blametail, &toosmall, origin, porigin);
 +                              if (!origin->suspects)
 +                                      break;
 +                      }
                }
 +      }
  
        /*
         * Optionally find copies from parents' files.
         */
 -      if (opt & PICKAXE_BLAME_COPY)
 +      if (opt & PICKAXE_BLAME_COPY) {
 +              if (blame_copy_score > blame_move_score)
 +                      filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
 +              else if (blame_copy_score < blame_move_score) {
 +                      origin->suspects = blame_merge(origin->suspects, toosmall);
 +                      toosmall = NULL;
 +                      filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
 +              }
 +              if (!origin->suspects)
 +                      goto finish;
 +
                for (i = 0, sg = first_scapegoat(revs, commit);
                     i < num_sg && sg;
                     sg = sg->next, i++) {
                        struct origin *porigin = sg_origin[i];
 -                      if (find_copy_in_parent(sb, origin, sg->item,
 -                                              porigin, opt))
 +                      find_copy_in_parent(sb, &blametail, &toosmall,
 +                                          origin, sg->item, porigin, opt);
 +                      if (!origin->suspects)
                                goto finish;
                }
 +      }
  
 - finish:
 +finish:
 +      *blametail = NULL;
 +      distribute_blame(sb, blames);
 +      /*
 +       * prepend toosmall to origin->suspects
 +       *
 +       * There is no point in sorting: this ends up on a big
 +       * unsorted list in the caller anyway.
 +       */
 +      if (toosmall) {
 +              struct blame_entry **tail = &toosmall;
 +              while (*tail)
 +                      tail = &(*tail)->next;
 +              *tail = origin->suspects;
 +              origin->suspects = toosmall;
 +      }
        for (i = 0; i < num_sg; i++) {
                if (sg_origin[i]) {
                        drop_origin_blob(sg_origin[i]);
@@@ -1731,11 -1481,14 +1731,11 @@@ static int emit_one_suspect_detail(stru
  }
  
  /*
 - * The blame_entry is found to be guilty for the range.  Mark it
 - * as such, and show it in incremental output.
 + * 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)
  {
 -      if (ent->guilty)
 -              return;
 -      ent->guilty = 1;
        if (incremental) {
                struct origin *suspect = ent->suspect;
  
  }
  
  /*
 - * The main loop -- while the scoreboard has lines whose true origin
 - * is still unknown, pick one blame_entry, and allow its current
 - * suspect to pass blames to its parents.
 - */
 + * The main loop -- while we have blobs with lines whose true origin
 + * is still unknown, pick one blob, and allow its lines to pass blames
 + * to its parents. */
  static void assign_blame(struct scoreboard *sb, int opt)
  {
        struct rev_info *revs = sb->revs;
 +      struct commit *commit = prio_queue_get(&sb->commits);
  
 -      while (1) {
 +      while (commit) {
                struct blame_entry *ent;
 -              struct commit *commit;
 -              struct origin *suspect = NULL;
 +              struct origin *suspect = commit->util;
  
                /* find one suspect to break down */
 -              for (ent = sb->ent; !suspect && ent; ent = ent->next)
 -                      if (!ent->guilty)
 -                              suspect = ent->suspect;
 -              if (!suspect)
 -                      return; /* all done */
 +              while (suspect && !suspect->suspects)
 +                      suspect = suspect->next;
 +
 +              if (!suspect) {
 +                      commit = prio_queue_get(&sb->commits);
 +                      continue;
 +              }
 +
 +              assert(commit == suspect->commit);
  
                /*
                 * We will use this suspect later in the loop,
                 * so hold onto it in the meantime.
                 */
                origin_incref(suspect);
 -              commit = suspect->commit;
                parse_commit(commit);
                if (reverse ||
                    (!(commit->object.flags & UNINTERESTING) &&
                        commit->object.flags |= UNINTERESTING;
  
                /* Take responsibility for the remaining entries */
 -              for (ent = sb->ent; ent; ent = ent->next)
 -                      if (ent->suspect == suspect)
 +              ent = suspect->suspects;
 +              if (ent) {
 +                      suspect->guilty = 1;
 +                      for (;;) {
 +                              struct blame_entry *next = ent->next;
                                found_guilty_entry(ent);
 +                              if (next) {
 +                                      ent = next;
 +                                      continue;
 +                              }
 +                              ent->next = sb->ent;
 +                              sb->ent = suspect->suspects;
 +                              suspect->suspects = NULL;
 +                              break;
 +                      }
 +              }
                origin_decref(suspect);
  
                if (DEBUG) /* sanity */
@@@ -1871,8 -1609,9 +1871,8 @@@ static void emit_porcelain(struct score
        char hex[41];
  
        strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
 -      printf("%s%c%d %d %d\n",
 +      printf("%s %d %d %d\n",
               hex,
 -             ent->guilty ? ' ' : '*', /* purely for debugging */
               ent->s_lno + 1,
               ent->lno + 1,
               ent->num_lines);
@@@ -1985,16 -1724,17 +1985,16 @@@ static void output(struct scoreboard *s
  
        if (option & OUTPUT_PORCELAIN) {
                for (ent = sb->ent; ent; ent = ent->next) {
 -                      struct blame_entry *oth;
 -                      struct origin *suspect = ent->suspect;
 -                      struct commit *commit = suspect->commit;
 +                      int count = 0;
 +                      struct origin *suspect;
 +                      struct commit *commit = ent->suspect->commit;
                        if (commit->object.flags & MORE_THAN_ONE_PATH)
                                continue;
 -                      for (oth = ent->next; oth; oth = oth->next) {
 -                              if ((oth->suspect->commit != commit) ||
 -                                  !strcmp(oth->suspect->path, suspect->path))
 -                                      continue;
 -                              commit->object.flags |= MORE_THAN_ONE_PATH;
 -                              break;
 +                      for (suspect = commit->util; suspect; suspect = suspect->next) {
 +                              if (suspect->guilty && count++) {
 +                                      commit->object.flags |= MORE_THAN_ONE_PATH;
 +                                      break;
 +                              }
                        }
                }
        }
        }
  }
  
 +static const char *get_next_line(const char *start, const char *end)
 +{
 +      const char *nl = memchr(start, '\n', end - start);
 +      return nl ? nl + 1 : end;
 +}
 +
  /*
   * To allow quick access to the contents of nth line in the
   * final image, prepare an index in the scoreboard.
@@@ -2025,19 -1759,39 +2025,19 @@@ static int prepare_lines(struct scorebo
        const char *end = buf + len;
        const char *p;
        int *lineno;
 -      int num = 0, incomplete = 0;
 -
 -      for (p = buf;;) {
 -              p = memchr(p, '\n', end - p);
 -              if (p) {
 -                      p++;
 -                      num++;
 -                      continue;
 -              }
 -              break;
 -      }
 +      int num = 0;
  
 -      if (len && end[-1] != '\n')
 -              incomplete++; /* incomplete line at the end */
 +      for (p = buf; p < end; p = get_next_line(p, end))
 +              num++;
  
 -      sb->lineno = xmalloc(sizeof(*sb->lineno) * (num + incomplete + 1));
 -      lineno = sb->lineno;
 +      sb->lineno = lineno = xmalloc(sizeof(*sb->lineno) * (num + 1));
  
 -      *lineno++ = 0;
 -      for (p = buf;;) {
 -              p = memchr(p, '\n', end - p);
 -              if (p) {
 -                      p++;
 -                      *lineno++ = p - buf;
 -                      continue;
 -              }
 -              break;
 -      }
 +      for (p = buf; p < end; p = get_next_line(p, end))
 +              *lineno++ = p - buf;
  
 -      if (incomplete)
 -              *lineno++ = len;
 +      *lineno = len;
  
 -      sb->num_lines = num + incomplete;
 +      sb->num_lines = num;
        return sb->num_lines;
  }
  
@@@ -2356,6 -2110,7 +2356,6 @@@ static struct commit *fake_working_tree
        origin->file.ptr = buf.buf;
        origin->file.size = buf.len;
        pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
 -      commit->util = origin;
  
        /*
         * Read the current index, replace the path entry with
         * right now, but someday we might optimize diff-index --cached
         * with cache-tree information.
         */
 -      cache_tree_invalidate_path(active_cache_tree, path);
 +      cache_tree_invalidate_path(&the_index, path);
  
        return commit;
  }
@@@ -2673,16 -2428,12 +2673,16 @@@ parse_done
        memset(&sb, 0, sizeof(sb));
  
        sb.revs = &revs;
 -      if (!reverse)
 +      if (!reverse) {
                final_commit_name = prepare_final(&sb);
 +              sb.commits.compare = compare_commits_by_commit_date;
 +      }
        else if (contents_from)
                die("--contents and --children do not blend well.");
 -      else
 +      else {
                final_commit_name = prepare_initial(&sb);
 +              sb.commits.compare = compare_commits_by_reverse_commit_date;
 +      }
  
        if (!sb.final) {
                /*
                die("revision walk setup failed");
  
        if (is_null_sha1(sb.final->object.sha1)) {
-               char *buf;
                o = sb.final->util;
-               buf = xmalloc(o->file.size + 1);
-               memcpy(buf, o->file.ptr, o->file.size + 1);
-               sb.final_buf = buf;
+               sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
                sb.final_buf_size = o->file.size;
        }
        else {
                ent->next = next;
                origin_incref(o);
        }
 +
 +      o->suspects = ent;
 +      prio_queue_put(&sb.commits, o->commit);
 +
        origin_decref(o);
  
        range_set_release(&ranges);
        string_list_clear(&range_list, 0);
  
 -      sb.ent = ent;
 +      sb.ent = NULL;
        sb.path = path;
  
        read_mailmap(&mailmap, NULL);
        if (incremental)
                return 0;
  
 +      sb.ent = blame_sort(sb.ent, compare_blame_final);
 +
        coalesce(&sb);
  
        if (!(output_option & OUTPUT_PORCELAIN))
diff --combined builtin/index-pack.c
index fc40411892f13da7e739bc4801bbace3021c0e2b,9ca0203922ba41139ad8daa9672acd22602bc1b5..5568a5bc3b69be79f0d5fa09c694fd976c8319cc
@@@ -362,8 -362,7 +362,7 @@@ static void set_thread_data(struct thre
  
  static struct base_data *alloc_base_data(void)
  {
-       struct base_data *base = xmalloc(sizeof(struct base_data));
-       memset(base, 0, sizeof(*base));
+       struct base_data *base = xcalloc(1, sizeof(struct base_data));
        base->ref_last = -1;
        base->ofs_last = -1;
        return base;
@@@ -1506,8 -1505,7 +1505,8 @@@ int cmd_index_pack(int argc, const cha
        const char *curr_index;
        const char *index_name = NULL, *pack_name = NULL;
        const char *keep_name = NULL, *keep_msg = NULL;
 -      char *index_name_buf = NULL, *keep_name_buf = NULL;
 +      struct strbuf index_name_buf = STRBUF_INIT,
 +                    keep_name_buf = STRBUF_INIT;
        struct pack_idx_entry **idx_objects;
        struct pack_idx_option opts;
        unsigned char pack_sha1[20];
        if (fix_thin_pack && !from_stdin)
                die(_("--fix-thin cannot be used without --stdin"));
        if (!index_name && pack_name) {
 -              int len = strlen(pack_name);
 -              if (!has_extension(pack_name, ".pack"))
 +              size_t len;
 +              if (!strip_suffix(pack_name, ".pack", &len))
                        die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
 -              index_name_buf = xmalloc(len);
 -              memcpy(index_name_buf, pack_name, len - 5);
 -              strcpy(index_name_buf + len - 5, ".idx");
 -              index_name = index_name_buf;
 +              strbuf_add(&index_name_buf, pack_name, len);
 +              strbuf_addstr(&index_name_buf, ".idx");
 +              index_name = index_name_buf.buf;
        }
        if (keep_msg && !keep_name && pack_name) {
 -              int len = strlen(pack_name);
 -              if (!has_extension(pack_name, ".pack"))
 +              size_t len;
 +              if (!strip_suffix(pack_name, ".pack", &len))
                        die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
 -              keep_name_buf = xmalloc(len);
 -              memcpy(keep_name_buf, pack_name, len - 5);
 -              strcpy(keep_name_buf + len - 5, ".keep");
 -              keep_name = keep_name_buf;
 +              strbuf_add(&keep_name_buf, pack_name, len);
 +              strbuf_addstr(&keep_name_buf, ".idx");
 +              keep_name = keep_name_buf.buf;
        }
        if (verify) {
                if (!index_name)
        else
                close(input_fd);
        free(objects);
 -      free(index_name_buf);
 -      free(keep_name_buf);
 +      strbuf_release(&index_name_buf);
 +      strbuf_release(&keep_name_buf);
        if (pack_name == NULL)
                free((void *) curr_pack);
        if (index_name == NULL)
diff --combined compat/mingw.c
index c19e3d954bc58b61028873b2e75b0604201531e5,363a957a1d406d222c1099ec01d0aea6fae43add..9d435e2cf4487de66f97c8a829975584bee52c9d
@@@ -1,7 -1,6 +1,7 @@@
  #include "../git-compat-util.h"
  #include "win32.h"
  #include <conio.h>
 +#include <wchar.h>
  #include "../strbuf.h"
  #include "../run-command.h"
  
@@@ -199,16 -198,14 +199,16 @@@ static int ask_yes_no_if_possible(cons
        }
  }
  
 -#undef unlink
  int mingw_unlink(const char *pathname)
  {
        int ret, tries = 0;
 +      wchar_t wpathname[MAX_PATH];
 +      if (xutftowcs_path(wpathname, pathname) < 0)
 +              return -1;
  
        /* read-only files cannot be removed */
 -      chmod(pathname, 0666);
 -      while ((ret = unlink(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
 +      _wchmod(wpathname, 0666);
 +      while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
                if (!is_file_in_use_error(GetLastError()))
                        break;
                /*
        while (ret == -1 && is_file_in_use_error(GetLastError()) &&
               ask_yes_no_if_possible("Unlink of file '%s' failed. "
                        "Should I try again?", pathname))
 -             ret = unlink(pathname);
 +             ret = _wunlink(wpathname);
        return ret;
  }
  
 -static int is_dir_empty(const char *path)
 +static int is_dir_empty(const wchar_t *wpath)
  {
 -      struct strbuf buf = STRBUF_INIT;
 -      WIN32_FIND_DATAA findbuf;
 +      WIN32_FIND_DATAW findbuf;
        HANDLE handle;
 -
 -      strbuf_addf(&buf, "%s\\*", path);
 -      handle = FindFirstFileA(buf.buf, &findbuf);
 -      if (handle == INVALID_HANDLE_VALUE) {
 -              strbuf_release(&buf);
 +      wchar_t wbuf[MAX_PATH + 2];
 +      wcscpy(wbuf, wpath);
 +      wcscat(wbuf, L"\\*");
 +      handle = FindFirstFileW(wbuf, &findbuf);
 +      if (handle == INVALID_HANDLE_VALUE)
                return GetLastError() == ERROR_NO_MORE_FILES;
 -      }
  
 -      while (!strcmp(findbuf.cFileName, ".") ||
 -                      !strcmp(findbuf.cFileName, ".."))
 -              if (!FindNextFile(handle, &findbuf)) {
 -                      strbuf_release(&buf);
 -                      return GetLastError() == ERROR_NO_MORE_FILES;
 +      while (!wcscmp(findbuf.cFileName, L".") ||
 +                      !wcscmp(findbuf.cFileName, L".."))
 +              if (!FindNextFileW(handle, &findbuf)) {
 +                      DWORD err = GetLastError();
 +                      FindClose(handle);
 +                      return err == ERROR_NO_MORE_FILES;
                }
        FindClose(handle);
 -      strbuf_release(&buf);
        return 0;
  }
  
 -#undef rmdir
  int mingw_rmdir(const char *pathname)
  {
        int ret, tries = 0;
 +      wchar_t wpathname[MAX_PATH];
 +      if (xutftowcs_path(wpathname, pathname) < 0)
 +              return -1;
  
 -      while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
 +      while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
                if (!is_file_in_use_error(GetLastError()))
                        errno = err_win_to_posix(GetLastError());
                if (errno != EACCES)
                        break;
 -              if (!is_dir_empty(pathname)) {
 +              if (!is_dir_empty(wpathname)) {
                        errno = ENOTEMPTY;
                        break;
                }
        while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
               ask_yes_no_if_possible("Deletion of directory '%s' failed. "
                        "Should I try again?", pathname))
 -             ret = rmdir(pathname);
 +             ret = _wrmdir(wpathname);
 +      return ret;
 +}
 +
 +int mingw_mkdir(const char *path, int mode)
 +{
 +      int ret;
 +      wchar_t wpath[MAX_PATH];
 +      if (xutftowcs_path(wpath, path) < 0)
 +              return -1;
 +      ret = _wmkdir(wpath);
        return ret;
  }
  
 -#undef open
  int mingw_open (const char *filename, int oflags, ...)
  {
        va_list args;
        unsigned mode;
        int fd;
 +      wchar_t wfilename[MAX_PATH];
  
        va_start(args, oflags);
        mode = va_arg(args, int);
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
  
 -      fd = open(filename, oflags, mode);
 +      if (xutftowcs_path(wfilename, filename) < 0)
 +              return -1;
 +      fd = _wopen(wfilename, oflags, mode);
  
        if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
 -              DWORD attrs = GetFileAttributes(filename);
 +              DWORD attrs = GetFileAttributesW(wfilename);
                if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
                        errno = EISDIR;
        }
@@@ -347,28 -332,17 +347,28 @@@ int mingw_fgetc(FILE *stream
  #undef fopen
  FILE *mingw_fopen (const char *filename, const char *otype)
  {
 +      FILE *file;
 +      wchar_t wfilename[MAX_PATH], wotype[4];
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
 -      return fopen(filename, otype);
 +      if (xutftowcs_path(wfilename, filename) < 0 ||
 +              xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
 +              return NULL;
 +      file = _wfopen(wfilename, wotype);
 +      return file;
  }
  
 -#undef freopen
  FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
  {
 +      FILE *file;
 +      wchar_t wfilename[MAX_PATH], wotype[4];
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
 -      return freopen(filename, otype, stream);
 +      if (xutftowcs_path(wfilename, filename) < 0 ||
 +              xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
 +              return NULL;
 +      file = _wfreopen(wfilename, wotype, stream);
 +      return file;
  }
  
  #undef fflush
@@@ -393,31 -367,6 +393,31 @@@ int mingw_fflush(FILE *stream
        return ret;
  }
  
 +int mingw_access(const char *filename, int mode)
 +{
 +      wchar_t wfilename[MAX_PATH];
 +      if (xutftowcs_path(wfilename, filename) < 0)
 +              return -1;
 +      /* X_OK is not supported by the MSVCRT version */
 +      return _waccess(wfilename, mode & ~X_OK);
 +}
 +
 +int mingw_chdir(const char *dirname)
 +{
 +      wchar_t wdirname[MAX_PATH];
 +      if (xutftowcs_path(wdirname, dirname) < 0)
 +              return -1;
 +      return _wchdir(wdirname);
 +}
 +
 +int mingw_chmod(const char *filename, int mode)
 +{
 +      wchar_t wfilename[MAX_PATH];
 +      if (xutftowcs_path(wfilename, filename) < 0)
 +              return -1;
 +      return _wchmod(wfilename, mode);
 +}
 +
  /*
   * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
   * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
@@@ -443,12 -392,10 +443,12 @@@ static inline time_t filetime_to_time_t
   */
  static int do_lstat(int follow, const char *file_name, struct stat *buf)
  {
 -      int err;
        WIN32_FILE_ATTRIBUTE_DATA fdata;
 +      wchar_t wfilename[MAX_PATH];
 +      if (xutftowcs_path(wfilename, file_name) < 0)
 +              return -1;
  
 -      if (!(err = get_file_attr(file_name, &fdata))) {
 +      if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
                buf->st_ino = 0;
                buf->st_gid = 0;
                buf->st_uid = 0;
                buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
                buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
                if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
 -                      WIN32_FIND_DATAA findbuf;
 -                      HANDLE handle = FindFirstFileA(file_name, &findbuf);
 +                      WIN32_FIND_DATAW findbuf;
 +                      HANDLE handle = FindFirstFileW(wfilename, &findbuf);
                        if (handle != INVALID_HANDLE_VALUE) {
                                if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
                                                (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
                }
                return 0;
        }
 -      errno = err;
 +      switch (GetLastError()) {
 +      case ERROR_ACCESS_DENIED:
 +      case ERROR_SHARING_VIOLATION:
 +      case ERROR_LOCK_VIOLATION:
 +      case ERROR_SHARING_BUFFER_EXCEEDED:
 +              errno = EACCES;
 +              break;
 +      case ERROR_BUFFER_OVERFLOW:
 +              errno = ENAMETOOLONG;
 +              break;
 +      case ERROR_NOT_ENOUGH_MEMORY:
 +              errno = ENOMEM;
 +              break;
 +      default:
 +              errno = ENOENT;
 +              break;
 +      }
        return -1;
  }
  
  static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
  {
        int namelen;
 -      static char alt_name[PATH_MAX];
 +      char alt_name[PATH_MAX];
  
        if (!do_lstat(follow, file_name, buf))
                return 0;
@@@ -585,20 -516,16 +585,20 @@@ int mingw_utime (const char *file_name
  {
        FILETIME mft, aft;
        int fh, rc;
 +      DWORD attrs;
 +      wchar_t wfilename[MAX_PATH];
 +      if (xutftowcs_path(wfilename, file_name) < 0)
 +              return -1;
  
        /* must have write permission */
 -      DWORD attrs = GetFileAttributes(file_name);
 +      attrs = GetFileAttributesW(wfilename);
        if (attrs != INVALID_FILE_ATTRIBUTES &&
            (attrs & FILE_ATTRIBUTE_READONLY)) {
                /* ignore errors here; open() will report them */
 -              SetFileAttributes(file_name, attrs & ~FILE_ATTRIBUTE_READONLY);
 +              SetFileAttributesW(wfilename, attrs & ~FILE_ATTRIBUTE_READONLY);
        }
  
 -      if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0) {
 +      if ((fh = _wopen(wfilename, O_RDWR | O_BINARY)) < 0) {
                rc = -1;
                goto revert_attrs;
        }
@@@ -621,7 -548,7 +621,7 @@@ revert_attrs
        if (attrs != INVALID_FILE_ATTRIBUTES &&
            (attrs & FILE_ATTRIBUTE_READONLY)) {
                /* ignore errors again */
 -              SetFileAttributes(file_name, attrs);
 +              SetFileAttributesW(wfilename, attrs);
        }
        return rc;
  }
@@@ -632,18 -559,6 +632,18 @@@ unsigned int sleep (unsigned int second
        return 0;
  }
  
 +char *mingw_mktemp(char *template)
 +{
 +      wchar_t wtemplate[MAX_PATH];
 +      if (xutftowcs_path(wtemplate, template) < 0)
 +              return NULL;
 +      if (!_wmktemp(wtemplate))
 +              return NULL;
 +      if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0)
 +              return NULL;
 +      return template;
 +}
 +
  int mkstemp(char *template)
  {
        char *filename = mktemp(template);
@@@ -702,18 -617,17 +702,18 @@@ struct tm *localtime_r(const time_t *ti
        return result;
  }
  
 -#undef getcwd
  char *mingw_getcwd(char *pointer, int len)
  {
        int i;
 -      char *ret = getcwd(pointer, len);
 -      if (!ret)
 -              return ret;
 +      wchar_t wpointer[MAX_PATH];
 +      if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
 +              return NULL;
 +      if (xwcstoutf(pointer, wpointer, len) < 0)
 +              return NULL;
        for (i = 0; pointer[i]; i++)
                if (pointer[i] == '\\')
                        pointer[i] = '/';
 -      return ret;
 +      return pointer;
  }
  
  /*
@@@ -917,10 -831,9 +917,10 @@@ static pid_t mingw_spawnve_fd(const cha
                              const char *dir,
                              int prepend_cmd, int fhin, int fhout, int fherr)
  {
 -      STARTUPINFO si;
 +      STARTUPINFOW si;
        PROCESS_INFORMATION pi;
        struct strbuf envblk, args;
 +      wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs;
        unsigned flags;
        BOOL ret;
  
        memset(&si, 0, sizeof(si));
        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESTDHANDLES;
 -      si.hStdInput = (HANDLE) _get_osfhandle(fhin);
 -      si.hStdOutput = (HANDLE) _get_osfhandle(fhout);
 -      si.hStdError = (HANDLE) _get_osfhandle(fherr);
 +      si.hStdInput = winansi_get_osfhandle(fhin);
 +      si.hStdOutput = winansi_get_osfhandle(fhout);
 +      si.hStdError = winansi_get_osfhandle(fherr);
 +
 +      if (xutftowcs_path(wcmd, cmd) < 0)
 +              return -1;
 +      if (dir && xutftowcs_path(wdir, dir) < 0)
 +              return -1;
  
        /* concatenate argv, quoting args as we go */
        strbuf_init(&args, 0);
                        free(quoted);
        }
  
 +      wargs = xmalloc((2 * args.len + 1) * sizeof(wchar_t));
 +      xutftowcs(wargs, args.buf, 2 * args.len + 1);
 +      strbuf_release(&args);
 +
        if (env) {
                int count = 0;
                char **e, **sorted_env;
        }
  
        memset(&pi, 0, sizeof(pi));
 -      ret = CreateProcess(cmd, args.buf, NULL, NULL, TRUE, flags,
 -              env ? envblk.buf : NULL, dir, &si, &pi);
 +      ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags,
 +              env ? envblk.buf : NULL, dir ? wdir : NULL, &si, &pi);
  
        if (env)
                strbuf_release(&envblk);
 -      strbuf_release(&args);
 +      free(wargs);
  
        if (!ret) {
                errno = ENOENT;
        return (pid_t)pi.dwProcessId;
  }
  
 -static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
 -                         int prepend_cmd)
 +static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
  {
 -      return mingw_spawnve_fd(cmd, argv, env, NULL, prepend_cmd, 0, 1, 2);
 +      return mingw_spawnve_fd(cmd, argv, environ, NULL, prepend_cmd, 0, 1, 2);
  }
  
  pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
        return pid;
  }
  
 -static int try_shell_exec(const char *cmd, char *const *argv, char **env)
 +static int try_shell_exec(const char *cmd, char *const *argv)
  {
        const char *interpr = parse_interpreter(cmd);
        char **path;
                argv2 = xmalloc(sizeof(*argv) * (argc+1));
                argv2[0] = (char *)cmd; /* full path to the script file */
                memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
 -              pid = mingw_spawnve(prog, argv2, env, 1);
 +              pid = mingw_spawnv(prog, argv2, 1);
                if (pid >= 0) {
                        int status;
                        if (waitpid(pid, &status, 0) < 0)
        return pid;
  }
  
 -static void mingw_execve(const char *cmd, char *const *argv, char *const *env)
 +int mingw_execv(const char *cmd, char *const *argv)
  {
        /* check if git_command is a shell script */
 -      if (!try_shell_exec(cmd, argv, (char **)env)) {
 +      if (!try_shell_exec(cmd, argv)) {
                int pid, status;
  
 -              pid = mingw_spawnve(cmd, (const char **)argv, (char **)env, 0);
 +              pid = mingw_spawnv(cmd, (const char **)argv, 0);
                if (pid < 0)
 -                      return;
 +                      return -1;
                if (waitpid(pid, &status, 0) < 0)
                        status = 255;
                exit(status);
        }
 +      return -1;
  }
  
  int mingw_execvp(const char *cmd, char *const *argv)
        char *prog = path_lookup(cmd, path, 0);
  
        if (prog) {
 -              mingw_execve(prog, argv, environ);
 +              mingw_execv(prog, argv);
                free(prog);
        } else
                errno = ENOENT;
        return -1;
  }
  
 -int mingw_execv(const char *cmd, char *const *argv)
 -{
 -      mingw_execve(cmd, argv, environ);
 -      return -1;
 -}
 -
  int mingw_kill(pid_t pid, int sig)
  {
        if (pid > 0 && sig == SIGTERM) {
@@@ -1316,8 -1226,7 +1316,7 @@@ static int WSAAPI getaddrinfo_stub(cons
        else
                ai->ai_canonname = NULL;
  
-       sin = xmalloc(ai->ai_addrlen);
-       memset(sin, 0, ai->ai_addrlen);
+       sin = xcalloc(1, ai->ai_addrlen);
        sin->sin_family = AF_INET;
        /* Note: getaddrinfo is supposed to allow service to be a string,
         * which should be looked up using getservbyname. This is
@@@ -1571,36 -1480,33 +1570,36 @@@ int mingw_rename(const char *pold, cons
  {
        DWORD attrs, gle;
        int tries = 0;
 +      wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
 +      if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
 +              return -1;
  
        /*
         * Try native rename() first to get errno right.
         * It is based on MoveFile(), which cannot overwrite existing files.
         */
 -      if (!rename(pold, pnew))
 +      if (!_wrename(wpold, wpnew))
                return 0;
        if (errno != EEXIST)
                return -1;
  repeat:
 -      if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
 +      if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
                return 0;
        /* TODO: translate more errors */
        gle = GetLastError();
        if (gle == ERROR_ACCESS_DENIED &&
 -          (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
 +          (attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
                if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
                        errno = EISDIR;
                        return -1;
                }
                if ((attrs & FILE_ATTRIBUTE_READONLY) &&
 -                  SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
 -                      if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
 +                  SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
 +                      if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
                                return 0;
                        gle = GetLastError();
                        /* revert file attributes on failure */
 -                      SetFileAttributes(pnew, attrs);
 +                      SetFileAttributesW(wpnew, attrs);
                }
        }
        if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
@@@ -1846,16 -1752,11 +1845,16 @@@ void mingw_open_html(const char *unixpa
  
  int link(const char *oldpath, const char *newpath)
  {
 -      typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
 +      typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
        static T create_hard_link = NULL;
 +      wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
 +      if (xutftowcs_path(woldpath, oldpath) < 0 ||
 +              xutftowcs_path(wnewpath, newpath) < 0)
 +              return -1;
 +
        if (!create_hard_link) {
                create_hard_link = (T) GetProcAddress(
 -                      GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
 +                      GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
                if (!create_hard_link)
                        create_hard_link = (T)-1;
        }
                errno = ENOSYS;
                return -1;
        }
 -      if (!create_hard_link(newpath, oldpath, NULL)) {
 +      if (!create_hard_link(wnewpath, woldpath, NULL)) {
                errno = err_win_to_posix(GetLastError());
                return -1;
        }
@@@ -1921,174 -1822,3 +1920,174 @@@ pid_t waitpid(pid_t pid, int *status, i
        errno = EINVAL;
        return -1;
  }
 +
 +int mingw_offset_1st_component(const char *path)
 +{
 +      int offset = 0;
 +      if (has_dos_drive_prefix(path))
 +              offset = 2;
 +
 +      /* unc paths */
 +      else if (is_dir_sep(path[0]) && is_dir_sep(path[1])) {
 +
 +              /* skip server name */
 +              char *pos = strpbrk(path + 2, "\\/");
 +              if (!pos)
 +                      return 0; /* Error: malformed unc path */
 +
 +              do {
 +                      pos++;
 +              } while (*pos && !is_dir_sep(*pos));
 +
 +              offset = pos - path;
 +      }
 +
 +      return offset + is_dir_sep(path[offset]);
 +}
 +
 +int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
 +{
 +      int upos = 0, wpos = 0;
 +      const unsigned char *utf = (const unsigned char*) utfs;
 +      if (!utf || !wcs || wcslen < 1) {
 +              errno = EINVAL;
 +              return -1;
 +      }
 +      /* reserve space for \0 */
 +      wcslen--;
 +      if (utflen < 0)
 +              utflen = INT_MAX;
 +
 +      while (upos < utflen) {
 +              int c = utf[upos++] & 0xff;
 +              if (utflen == INT_MAX && c == 0)
 +                      break;
 +
 +              if (wpos >= wcslen) {
 +                      wcs[wpos] = 0;
 +                      errno = ERANGE;
 +                      return -1;
 +              }
 +
 +              if (c < 0x80) {
 +                      /* ASCII */
 +                      wcs[wpos++] = c;
 +              } else if (c >= 0xc2 && c < 0xe0 && upos < utflen &&
 +                              (utf[upos] & 0xc0) == 0x80) {
 +                      /* 2-byte utf-8 */
 +                      c = ((c & 0x1f) << 6);
 +                      c |= (utf[upos++] & 0x3f);
 +                      wcs[wpos++] = c;
 +              } else if (c >= 0xe0 && c < 0xf0 && upos + 1 < utflen &&
 +                              !(c == 0xe0 && utf[upos] < 0xa0) && /* over-long encoding */
 +                              (utf[upos] & 0xc0) == 0x80 &&
 +                              (utf[upos + 1] & 0xc0) == 0x80) {
 +                      /* 3-byte utf-8 */
 +                      c = ((c & 0x0f) << 12);
 +                      c |= ((utf[upos++] & 0x3f) << 6);
 +                      c |= (utf[upos++] & 0x3f);
 +                      wcs[wpos++] = c;
 +              } else if (c >= 0xf0 && c < 0xf5 && upos + 2 < utflen &&
 +                              wpos + 1 < wcslen &&
 +                              !(c == 0xf0 && utf[upos] < 0x90) && /* over-long encoding */
 +                              !(c == 0xf4 && utf[upos] >= 0x90) && /* > \u10ffff */
 +                              (utf[upos] & 0xc0) == 0x80 &&
 +                              (utf[upos + 1] & 0xc0) == 0x80 &&
 +                              (utf[upos + 2] & 0xc0) == 0x80) {
 +                      /* 4-byte utf-8: convert to \ud8xx \udcxx surrogate pair */
 +                      c = ((c & 0x07) << 18);
 +                      c |= ((utf[upos++] & 0x3f) << 12);
 +                      c |= ((utf[upos++] & 0x3f) << 6);
 +                      c |= (utf[upos++] & 0x3f);
 +                      c -= 0x10000;
 +                      wcs[wpos++] = 0xd800 | (c >> 10);
 +                      wcs[wpos++] = 0xdc00 | (c & 0x3ff);
 +              } else if (c >= 0xa0) {
 +                      /* invalid utf-8 byte, printable unicode char: convert 1:1 */
 +                      wcs[wpos++] = c;
 +              } else {
 +                      /* invalid utf-8 byte, non-printable unicode: convert to hex */
 +                      static const char *hex = "0123456789abcdef";
 +                      wcs[wpos++] = hex[c >> 4];
 +                      if (wpos < wcslen)
 +                              wcs[wpos++] = hex[c & 0x0f];
 +              }
 +      }
 +      wcs[wpos] = 0;
 +      return wpos;
 +}
 +
 +int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
 +{
 +      if (!wcs || !utf || utflen < 1) {
 +              errno = EINVAL;
 +              return -1;
 +      }
 +      utflen = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf, utflen, NULL, NULL);
 +      if (utflen)
 +              return utflen - 1;
 +      errno = ERANGE;
 +      return -1;
 +}
 +
 +/*
 + * Disable MSVCRT command line wildcard expansion (__getmainargs called from
 + * mingw startup code, see init.c in mingw runtime).
 + */
 +int _CRT_glob = 0;
 +
 +typedef struct {
 +      int newmode;
 +} _startupinfo;
 +
 +extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
 +              _startupinfo *si);
 +
 +static NORETURN void die_startup()
 +{
 +      fputs("fatal: not enough memory for initialization", stderr);
 +      exit(128);
 +}
 +
 +void mingw_startup()
 +{
 +      int i, len, maxlen, argc;
 +      char *buffer;
 +      wchar_t **wenv, **wargv;
 +      _startupinfo si;
 +
 +      /* get wide char arguments and environment */
 +      si.newmode = 0;
 +      if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
 +              die_startup();
 +
 +      /* determine size of argv and environ conversion buffer */
 +      maxlen = wcslen(_wpgmptr);
 +      for (i = 1; i < argc; i++)
 +              maxlen = max(maxlen, wcslen(wargv[i]));
 +
 +      /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
 +      maxlen = 3 * maxlen + 1;
 +      buffer = xmalloc(maxlen);
 +
 +      /* convert command line arguments and environment to UTF-8 */
 +      len = xwcstoutf(buffer, _wpgmptr, maxlen);
 +      __argv[0] = xmemdupz(buffer, len);
 +      for (i = 1; i < argc; i++) {
 +              len = xwcstoutf(buffer, wargv[i], maxlen);
 +              __argv[i] = xmemdupz(buffer, len);
 +      }
 +      free(buffer);
 +
 +      /* initialize critical section for waitpid pinfo_t list */
 +      InitializeCriticalSection(&pinfo_cs);
 +
 +      /* set up default file mode and file modes for stdin/out/err */
 +      _fmode = _O_BINARY;
 +      _setmode(_fileno(stdin), _O_BINARY);
 +      _setmode(_fileno(stdout), _O_BINARY);
 +      _setmode(_fileno(stderr), _O_BINARY);
 +
 +      /* initialize Unicode console */
 +      winansi_init();
 +}
diff --combined connect.c
index 37ff018f13f6f8f3137a9aec973b85848a8138eb,ebc3a5be7d63a68bbaa2d3d6e81ea9191199aee1..5047402a1aade7a443f55999550ae4542189ef01
+++ b/connect.c
@@@ -64,9 -64,7 +64,7 @@@ static void parse_one_symref_info(struc
        if (!len)
                return; /* just "symref" */
        /* e.g. "symref=HEAD:refs/heads/master" */
-       sym = xmalloc(len + 1);
-       memcpy(sym, val, len);
-       sym[len] = '\0';
+       sym = xmemdupz(val, len);
        target = strchr(sym, ':');
        if (!target)
                /* just "symref=something" */
@@@ -129,7 -127,6 +127,7 @@@ struct ref **get_remote_heads(int in, c
                char *name;
                int len, name_len;
                char *buffer = packet_buffer;
 +              const char *arg;
  
                len = packet_read(in, &src_buf, &src_len,
                                  packet_buffer, sizeof(packet_buffer),
                if (!len)
                        break;
  
 -              if (len > 4 && starts_with(buffer, "ERR "))
 -                      die("remote error: %s", buffer + 4);
 +              if (len > 4 && skip_prefix(buffer, "ERR ", &arg))
 +                      die("remote error: %s", arg);
  
 -              if (len == 48 && starts_with(buffer, "shallow ")) {
 -                      if (get_sha1_hex(buffer + 8, old_sha1))
 -                              die("protocol error: expected shallow sha-1, got '%s'", buffer + 8);
 +              if (len == 48 && skip_prefix(buffer, "shallow ", &arg)) {
 +                      if (get_sha1_hex(arg, old_sha1))
 +                              die("protocol error: expected shallow sha-1, got '%s'", arg);
                        if (!shallow_points)
                                die("repository on the other end cannot be shallow");
                        sha1_array_append(shallow_points, old_sha1);
@@@ -535,18 -532,22 +533,18 @@@ static int git_use_proxy(const char *ho
  static struct child_process *git_proxy_connect(int fd[2], char *host)
  {
        const char *port = STR(DEFAULT_GIT_PORT);
 -      const char **argv;
        struct child_process *proxy;
  
        get_host_and_port(&host, &port);
  
 -      argv = xmalloc(sizeof(*argv) * 4);
 -      argv[0] = git_proxy_command;
 -      argv[1] = host;
 -      argv[2] = port;
 -      argv[3] = NULL;
        proxy = xcalloc(1, sizeof(*proxy));
 -      proxy->argv = argv;
 +      argv_array_push(&proxy->args, git_proxy_command);
 +      argv_array_push(&proxy->args, host);
 +      argv_array_push(&proxy->args, port);
        proxy->in = -1;
        proxy->out = -1;
        if (start_command(proxy))
 -              die("cannot start proxy %s", argv[0]);
 +              die("cannot start proxy %s", git_proxy_command);
        fd[0] = proxy->out; /* read from proxy stdout */
        fd[1] = proxy->in;  /* write to proxy stdin */
        return proxy;
@@@ -660,6 -661,7 +658,6 @@@ struct child_process *git_connect(int f
        char *hostandport, *path;
        struct child_process *conn = &no_fork;
        enum protocol protocol;
 -      const char **arg;
        struct strbuf cmd = STRBUF_INIT;
  
        /* Without this we cannot rely on waitpid() to tell
                sq_quote_buf(&cmd, path);
  
                conn->in = conn->out = -1;
 -              conn->argv = arg = xcalloc(7, sizeof(*arg));
                if (protocol == PROTO_SSH) {
                        const char *ssh = getenv("GIT_SSH");
                        int putty = ssh && strcasestr(ssh, "plink");
  
                        if (!ssh) ssh = "ssh";
  
 -                      *arg++ = ssh;
 +                      argv_array_push(&conn->args, ssh);
                        if (putty && !strcasestr(ssh, "tortoiseplink"))
 -                              *arg++ = "-batch";
 +                              argv_array_push(&conn->args, "-batch");
                        if (port) {
                                /* P is for PuTTY, p is for OpenSSH */
 -                              *arg++ = putty ? "-P" : "-p";
 -                              *arg++ = port;
 +                              argv_array_push(&conn->args, putty ? "-P" : "-p");
 +                              argv_array_push(&conn->args, port);
                        }
 -                      *arg++ = ssh_host;
 +                      argv_array_push(&conn->args, ssh_host);
                } else {
                        /* remove repo-local variables from the environment */
                        conn->env = local_repo_env;
                        conn->use_shell = 1;
                }
 -              *arg++ = cmd.buf;
 -              *arg = NULL;
 +              argv_array_push(&conn->args, cmd.buf);
  
                if (start_command(conn))
                        die("unable to fork");
@@@ -753,6 -757,7 +751,6 @@@ int finish_connect(struct child_proces
                return 0;
  
        code = finish_command(conn);
 -      free(conn->argv);
        free(conn);
        return code;
  }
diff --combined http-backend.c
index 57290d9bdafc730afaac3ddf8564d85ead39eb31,f6b7a5bae11bddbfc7743efa52b4440449692cb6..80790bbaef95a56ac737c7763e48035e3e0754ee
@@@ -221,19 -221,17 +221,19 @@@ static void get_idx_file(char *name
  
  static int http_config(const char *var, const char *value, void *cb)
  {
 +      const char *p;
 +
        if (!strcmp(var, "http.getanyfile")) {
                getanyfile = git_config_bool(var, value);
                return 0;
        }
  
 -      if (starts_with(var, "http.")) {
 +      if (skip_prefix(var, "http.", &p)) {
                int i;
  
                for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
                        struct rpc_service *svc = &rpc_service[i];
 -                      if (!strcmp(var + 5, svc->config_name)) {
 +                      if (!strcmp(p, svc->config_name)) {
                                svc->enabled = git_config_bool(var, value);
                                return 0;
                        }
  
  static struct rpc_service *select_service(const char *name)
  {
 +      const char *svc_name;
        struct rpc_service *svc = NULL;
        int i;
  
 -      if (!starts_with(name, "git-"))
 +      if (!skip_prefix(name, "git-", &svc_name))
                forbidden("Unsupported service: '%s'", name);
  
        for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
                struct rpc_service *s = &rpc_service[i];
 -              if (!strcmp(s->name, name + 4)) {
 +              if (!strcmp(s->name, svc_name)) {
                        svc = s;
                        break;
                }
@@@ -610,9 -607,7 +610,7 @@@ int main(int argc, char **argv
  
                        cmd = c;
                        n = out[0].rm_eo - out[0].rm_so;
-                       cmd_arg = xmalloc(n);
-                       memcpy(cmd_arg, dir + out[0].rm_so + 1, n-1);
-                       cmd_arg[n-1] = '\0';
+                       cmd_arg = xmemdupz(dir + out[0].rm_so + 1, n - 1);
                        dir[out[0].rm_so] = 0;
                        break;
                }
diff --combined path.c
index bc804a31b3382e689dd6ff5b4a727b109a691f7a,c36f00393007dc2d48a7b7fd765d809fb224d8a4..25c3b8ba7ef0ed5013626a43774f415f18bc19ef
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -249,9 -249,7 +249,7 @@@ int validate_headref(const char *path
  static struct passwd *getpw_str(const char *username, size_t len)
  {
        struct passwd *pw;
-       char *username_z = xmalloc(len + 1);
-       memcpy(username_z, username, len);
-       username_z[len] = '\0';
+       char *username_z = xmemdupz(username, len);
        pw = getpwnam(username_z);
        free(username_z);
        return pw;
@@@ -823,3 -821,10 +821,3 @@@ int daemon_avoid_alias(const char *p
                }
        }
  }
 -
 -int offset_1st_component(const char *path)
 -{
 -      if (has_dos_drive_prefix(path))
 -              return 2 + is_dir_sep(path[2]);
 -      return is_dir_sep(path[0]);
 -}
diff --combined pathspec.c
index 89f2c8ffff703b98f360590bfcecf165a6df4aef,0be5edbd13863690870dbede0dadfdb879a69d28..9304ee33d75ab9de14e4a7b6dc96f5dd8028c464
@@@ -338,7 -338,7 +338,7 @@@ static void NORETURN unsupported_magic(
                if (!(magic & m->bit))
                        continue;
                if (sb.len)
 -                      strbuf_addstr(&sb, " ");
 +                      strbuf_addch(&sb, ' ');
                if (short_magic & m->bit)
                        strbuf_addf(&sb, "'%c'", m->mnemonic);
                else
@@@ -389,8 -389,7 +389,7 @@@ void parse_pathspec(struct pathspec *pa
                if (!(flags & PATHSPEC_PREFER_CWD))
                        die("BUG: PATHSPEC_PREFER_CWD requires arguments");
  
-               pathspec->items = item = xmalloc(sizeof(*item));
-               memset(item, 0, sizeof(*item));
+               pathspec->items = item = xcalloc(1, sizeof(*item));
                item->match = prefix;
                item->original = prefix;
                item->nowildcard_len = item->len = strlen(prefix);