Merge branch 'jk/commit-buffer-length'
authorJunio C Hamano <gitster@pobox.com>
Wed, 2 Jul 2014 19:53:02 +0000 (12:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 2 Jul 2014 19:53:02 +0000 (12:53 -0700)
Move "commit->buffer" out of the in-core commit object and keep
track of their lengths. Use this to optimize the code paths to
validate GPG signatures in commit objects.

* jk/commit-buffer-length:
reuse cached commit buffer when parsing signatures
commit: record buffer length in cache
commit: convert commit->buffer to a slab
commit-slab: provide a static initializer
use get_commit_buffer everywhere
convert logmsg_reencode to get_commit_buffer
use get_commit_buffer to avoid duplicate code
use get_cached_commit_buffer where appropriate
provide helpers to access the commit buffer
provide a helper to set the commit buffer
provide a helper to free commit buffer
sequencer: use logmsg_reencode in get_message
logmsg_reencode: return const buffer
do not create "struct commit" with xcalloc
commit: push commit_index update into alloc_commit_node
alloc: include any-object allocations in alloc_report
replace dangerous uses of strbuf_attach
commit_tree: take a pointer/len pair rather than a const strbuf

14 files changed:
1  2 
builtin/blame.c
builtin/commit.c
builtin/fast-export.c
builtin/index-pack.c
builtin/log.c
builtin/merge.c
builtin/reset.c
commit.c
merge-recursive.c
notes-cache.c
notes-utils.c
pretty.c
revision.c
sequencer.c
diff --combined builtin/blame.c
index 662e3fec44666901de4f8feda8a51385260967e0,b84e375b5c5cdd2c6f0a18f0c57462131cb14ec3..d3b256e545a76b096682223539c4faec8d770b2f
@@@ -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]);
@@@ -1655,7 -1405,7 +1655,7 @@@ static void get_commit_info(struct comm
  {
        int len;
        const char *subject, *encoding;
-       char *message;
+       const char *message;
  
        commit_info_init(ret);
  
                    &ret->author_time, &ret->author_tz);
  
        if (!detailed) {
-               logmsg_free(message, commit);
+               unuse_commit_buffer(commit, message);
                return;
        }
  
        else
                strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
  
-       logmsg_free(message, commit);
+       unuse_commit_buffer(commit, message);
  }
  
  /*
@@@ -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 */
  static const char *format_time(unsigned long time, const char *tz_str,
                               int show_raw_time)
  {
 -      static char time_buf[128];
 +      static struct strbuf time_buf = STRBUF_INIT;
  
 +      strbuf_reset(&time_buf);
        if (show_raw_time) {
 -              snprintf(time_buf, sizeof(time_buf), "%lu %s", time, tz_str);
 +              strbuf_addf(&time_buf, "%lu %s", time, tz_str);
        }
        else {
                const char *time_str;
 -              int time_len;
 +              size_t time_width;
                int tz;
                tz = atoi(tz_str);
                time_str = show_date(time, tz, blame_date_mode);
 -              time_len = strlen(time_str);
 -              memcpy(time_buf, time_str, time_len);
 -              memset(time_buf + time_len, ' ', blame_date_width - time_len);
 +              strbuf_addstr(&time_buf, time_str);
 +              /*
 +               * Add space paddings to time_buf to display a fixed width
 +               * string, and use time_width for display width calibration.
 +               */
 +              for (time_width = utf8_strwidth(time_str);
 +                   time_width < blame_date_width;
 +                   time_width++)
 +                      strbuf_addch(&time_buf, ' ');
        }
 -      return time_buf;
 +      return time_buf.buf;
  }
  
  #define OUTPUT_ANNOTATE_COMPAT        001
@@@ -1871,8 -1602,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 -1717,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 -1752,39 +2025,19 @@@ static int prepare_lines(struct scorebo
        const char *end = buf + len;
        const char *p;
        int *lineno;
 -      int num = 0, incomplete = 0;
 +      int num = 0;
  
 -      for (p = buf;;) {
 -              p = memchr(p, '\n', end - p);
 -              if (p) {
 -                      p++;
 -                      num++;
 -                      continue;
 -              }
 -              break;
 -      }
 +      for (p = buf; p < end; p = get_next_line(p, end))
 +              num++;
  
 -      if (len && end[-1] != '\n')
 -              incomplete++; /* incomplete line at the end */
 +      sb->lineno = lineno = xmalloc(sizeof(*sb->lineno) * (num + 1));
  
 -      sb->lineno = xmalloc(sizeof(*sb->lineno) * (num + incomplete + 1));
 -      lineno = sb->lineno;
 +      for (p = buf; p < end; p = get_next_line(p, end))
 +              *lineno++ = p - buf;
  
 -      *lineno++ = 0;
 -      for (p = buf;;) {
 -              p = memchr(p, '\n', end - p);
 -              if (p) {
 -                      p++;
 -                      *lineno++ = p - buf;
 -                      continue;
 -              }
 -              break;
 -      }
 +      *lineno = len;
  
 -      if (incomplete)
 -              *lineno++ = len;
 -
 -      sb->num_lines = num + incomplete;
 +      sb->num_lines = num;
        return sb->num_lines;
  }
  
@@@ -2251,6 -1998,18 +2251,18 @@@ static void append_merge_parents(struc
        strbuf_release(&line);
  }
  
+ /*
+  * This isn't as simple as passing sb->buf and sb->len, because we
+  * want to transfer ownership of the buffer to the commit (so we
+  * must use detach).
+  */
+ static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
+ {
+       size_t len;
+       void *buf = strbuf_detach(sb, &len);
+       set_commit_buffer(c, buf, len);
+ }
  /*
   * Prepare a dummy commit that represents the work tree (or staged) item.
   * Note that annotating work tree item never works in the reverse.
@@@ -2272,7 -2031,7 +2284,7 @@@ static struct commit *fake_working_tree
        struct strbuf msg = STRBUF_INIT;
  
        time(&now);
-       commit = xcalloc(1, sizeof(*commit));
+       commit = alloc_commit_node();
        commit->object.parsed = 1;
        commit->date = now;
        commit->object.type = OBJ_COMMIT;
                    ident, ident, path,
                    (!contents_from ? path :
                     (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
-       commit->buffer = strbuf_detach(&msg, NULL);
+       set_commit_buffer_from_strbuf(commit, &msg);
  
        if (!contents_from || strcmp("-", contents_from)) {
                struct stat st;
                if (strbuf_read(&buf, 0, 0) < 0)
                        die_errno("failed to read from stdin");
        }
 -      convert_to_git(path, buf.buf, buf.len, &buf, 0);
        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
@@@ -2582,14 -2343,7 +2594,14 @@@ parse_done
                blame_date_width = sizeof("2006-10-19");
                break;
        case DATE_RELATIVE:
 -              /* "normal" is used as the fallback for "relative" */
 +              /* TRANSLATORS: This string is used to tell us the maximum
 +                 display width for a relative timestamp in "git blame"
 +                 output.  For C locale, "4 years, 11 months ago", which
 +                 takes 22 places, is the longest among various forms of
 +                 relative timestamps, but your language may need more or
 +                 fewer display columns. */
 +              blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
 +              break;
        case DATE_LOCAL:
        case DATE_NORMAL:
                blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
        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) {
                /*
                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/commit.c
index 5e2221c8e8c842314cb177d55223e87328b3da26,8b85df475b2e266277aeffc79aaebaefc37f58b2..84cec9a715341fedeabfaedfd82bbb64a4c3c255
@@@ -526,29 -526,10 +526,29 @@@ static int sane_ident_split(struct iden
        return 1;
  }
  
 +static int parse_force_date(const char *in, char *out, int len)
 +{
 +      if (len < 1)
 +              return -1;
 +      *out++ = '@';
 +      len--;
 +
 +      if (parse_date(in, out, len) < 0) {
 +              int errors = 0;
 +              unsigned long t = approxidate_careful(in, &errors);
 +              if (errors)
 +                      return -1;
 +              snprintf(out, len, "%lu", t);
 +      }
 +
 +      return 0;
 +}
 +
  static void determine_author_info(struct strbuf *author_ident)
  {
        char *name, *email, *date;
        struct ident_split author;
 +      char date_buf[64];
  
        name = getenv("GIT_AUTHOR_NAME");
        email = getenv("GIT_AUTHOR_EMAIL");
                email = xstrndup(lb + 2, rb - (lb + 2));
        }
  
 -      if (force_date)
 -              date = force_date;
 +      if (force_date) {
 +              if (parse_force_date(force_date, date_buf, sizeof(date_buf)))
 +                      die(_("invalid date format: %s"), force_date);
 +              date = date_buf;
 +      }
 +
        strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
        if (!split_ident_line(&author, author_ident->buf, author_ident->len) &&
            sane_ident_split(&author)) {
        }
  }
  
 -static char *cut_ident_timestamp_part(char *string)
 +static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf)
 +{
 +      if (split_ident_line(id, buf->buf, buf->len) ||
 +          !sane_ident_split(id))
 +              die(_("Malformed ident string: '%s'"), buf->buf);
 +}
 +
 +static int author_date_is_interesting(void)
  {
 -      char *ket = strrchr(string, '>');
 -      if (!ket || ket[1] != ' ')
 -              die(_("Malformed ident string: '%s'"), string);
 -      *++ket = '\0';
 -      return ket;
 +      return author_message || force_date;
 +}
 +
 +static void adjust_comment_line_char(const struct strbuf *sb)
 +{
 +      char candidates[] = "#;@!$%^&|:";
 +      char *candidate;
 +      const char *p;
 +
 +      comment_line_char = candidates[0];
 +      if (!memchr(sb->buf, comment_line_char, sb->len))
 +              return;
 +
 +      p = sb->buf;
 +      candidate = strchr(candidates, *p);
 +      if (candidate)
 +              *candidate = ' ';
 +      for (p = sb->buf; *p; p++) {
 +              if ((p[0] == '\n' || p[0] == '\r') && p[1]) {
 +                      candidate = strchr(candidates, p[1]);
 +                      if (candidate)
 +                              *candidate = ' ';
 +              }
 +      }
 +
 +      for (p = candidates; *p == ' '; p++)
 +              ;
 +      if (!*p)
 +              die(_("unable to select a comment character that is not used\n"
 +                    "in the current commit message"));
 +      comment_line_char = *p;
  }
  
  static int prepare_to_commit(const char *index_file, const char *prefix,
        } else if (use_message) {
                char *buffer;
                buffer = strstr(use_message_buffer, "\n\n");
 -              if (!use_editor && (!buffer || buffer[2] == '\0'))
 -                      die(_("commit has empty message"));
 -              strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
 +              if (buffer)
 +                      strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
                hook_arg1 = "commit";
                hook_arg2 = use_message;
        } else if (fixup_message) {
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
                die_errno(_("could not write commit template"));
  
 +      if (auto_comment_line_char)
 +              adjust_comment_line_char(&sb);
        strbuf_release(&sb);
  
        /* This checks if committer ident is explicitly given */
        if (use_editor && include_status) {
                int ident_shown = 0;
                int saved_color_setting;
 -              char *ai_tmp, *ci_tmp;
 +              struct ident_split ci, ai;
 +
                if (whence != FROM_COMMIT) {
                        if (cleanup_mode == CLEANUP_SCISSORS)
                                wt_status_add_cut_line(s->fp);
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                                        "%s", only_include_assumed);
  
 -              ai_tmp = cut_ident_timestamp_part(author_ident->buf);
 -              ci_tmp = cut_ident_timestamp_part(committer_ident.buf);
 -              if (strcmp(author_ident->buf, committer_ident.buf))
 +              split_ident_or_die(&ai, author_ident);
 +              split_ident_or_die(&ci, &committer_ident);
 +
 +              if (ident_cmp(&ai, &ci))
 +                      status_printf_ln(s, GIT_COLOR_NORMAL,
 +                              _("%s"
 +                              "Author:    %.*s <%.*s>"),
 +                              ident_shown++ ? "" : "\n",
 +                              (int)(ai.name_end - ai.name_begin), ai.name_begin,
 +                              (int)(ai.mail_end - ai.mail_begin), ai.mail_begin);
 +
 +              if (author_date_is_interesting())
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                                _("%s"
 -                              "Author:    %s"),
 +                              "Date:      %s"),
                                ident_shown++ ? "" : "\n",
 -                              author_ident->buf);
 +                              show_ident_date(&ai, DATE_NORMAL));
  
                if (!committer_ident_sufficiently_given())
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                                _("%s"
 -                              "Committer: %s"),
 +                              "Committer: %.*s <%.*s>"),
                                ident_shown++ ? "" : "\n",
 -                              committer_ident.buf);
 +                              (int)(ci.name_end - ci.name_begin), ci.name_begin,
 +                              (int)(ci.mail_end - ci.mail_begin), ci.mail_begin);
  
                if (ident_shown)
 -                      status_printf_ln(s, GIT_COLOR_NORMAL, "");
 +                      status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
  
                saved_color_setting = s->use_color;
                s->use_color = 0;
                commitable = run_status(s->fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
 -
 -              *ai_tmp = ' ';
 -              *ci_tmp = ' ';
        } else {
                unsigned char sha1[20];
                const char *parent = "HEAD";
  
                if (get_sha1(parent, sha1))
                        commitable = !!active_nr;
 -              else
 -                      commitable = index_differs_from(parent, 0);
 +              else {
 +                      /*
 +                       * Unless the user did explicitly request a submodule
 +                       * ignore mode by passing a command line option we do
 +                       * not ignore any changed submodule SHA-1s when
 +                       * comparing index and parent, no matter what is
 +                       * configured. Otherwise we won't commit any
 +                       * submodules which were manually staged, which would
 +                       * be really confusing.
 +                       */
 +                      int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
 +                      if (ignore_submodule_arg &&
 +                          !strcmp(ignore_submodule_arg, "all"))
 +                              diff_flags |= DIFF_OPT_IGNORE_SUBMODULES;
 +                      commitable = index_differs_from(parent, diff_flags);
 +              }
        }
        strbuf_release(&committer_ident);
  
@@@ -1435,13 -1356,6 +1435,13 @@@ static void print_summary(const char *p
                strbuf_addstr(&format, "\n Author: ");
                strbuf_addbuf_percentquote(&format, &author_ident);
        }
 +      if (author_date_is_interesting()) {
 +              struct strbuf date = STRBUF_INIT;
 +              format_commit_message(commit, "%ad", &date, &pctx);
 +              strbuf_addstr(&format, "\n Date: ");
 +              strbuf_addbuf_percentquote(&format, &date);
 +              strbuf_release(&date);
 +      }
        if (!committer_ident_sufficiently_given()) {
                strbuf_addstr(&format, "\n Committer: ");
                strbuf_addbuf_percentquote(&format, &committer_ident);
@@@ -1745,8 -1659,8 +1745,8 @@@ int cmd_commit(int argc, const char **a
                append_merge_tag_headers(parents, &tail);
        }
  
-       if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1,
-                                author_ident.buf, sign_commit, extra)) {
+       if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
+                        parents, sha1, author_ident.buf, sign_commit, extra)) {
                rollback_index_files();
                die(_("failed to write commit object"));
        }
                                           ? NULL
                                           : current_head->object.sha1,
                                           0, NULL);
 +      if (!ref_lock) {
 +              rollback_index_files();
 +              die(_("cannot lock HEAD ref"));
 +      }
  
        nl = strchr(sb.buf, '\n');
        if (nl)
        strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
        strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
  
 -      if (!ref_lock) {
 -              rollback_index_files();
 -              die(_("cannot lock HEAD ref"));
 -      }
        if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) {
                rollback_index_files();
                die(_("cannot update HEAD ref"));
diff --combined builtin/fast-export.c
index ef4481615fd2f4d60cd3cd6a4bbcceaca93c92d5,05d161f19f138275a6ea995bf4b6b3b11c987693..92b4624a4b93ff50c661e44242d508aed31877eb
@@@ -17,7 -17,6 +17,7 @@@
  #include "utf8.h"
  #include "parse-options.h"
  #include "quote.h"
 +#include "remote.h"
  
  static const char *fast_export_usage[] = {
        N_("git fast-export [rev-list-opts]"),
@@@ -32,8 -31,6 +32,8 @@@ static int use_done_feature
  static int no_data;
  static int full_tree;
  static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
 +static struct refspec *refspecs;
 +static int refspecs_nr;
  
  static int parse_opt_signed_tag_mode(const struct option *opt,
                                     const char *arg, int unset)
@@@ -282,6 -279,7 +282,7 @@@ static const char *find_encoding(const 
  static void handle_commit(struct commit *commit, struct rev_info *rev)
  {
        int saved_output_format = rev->diffopt.output_format;
+       const char *commit_buffer;
        const char *author, *author_end, *committer, *committer_end;
        const char *encoding, *message;
        char *reencoded = NULL;
        rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
  
        parse_commit_or_die(commit);
-       author = strstr(commit->buffer, "\nauthor ");
+       commit_buffer = get_commit_buffer(commit, NULL);
+       author = strstr(commit_buffer, "\nauthor ");
        if (!author)
                die ("Could not find author in commit %s",
                     sha1_to_hex(commit->object.sha1));
                          ? strlen(message) : 0),
               reencoded ? reencoded : message ? message : "");
        free(reencoded);
+       unuse_commit_buffer(commit, commit_buffer);
  
        for (i = 0, p = commit->parents; p; p = p->next) {
                int mark = get_object_mark(&p->item->object);
@@@ -528,15 -528,6 +531,15 @@@ static void get_tags_and_duplicates(str
                if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
                        continue;
  
 +              if (refspecs) {
 +                      char *private;
 +                      private = apply_refspecs(refspecs, refspecs_nr, full_name);
 +                      if (private) {
 +                              free(full_name);
 +                              full_name = private;
 +                      }
 +              }
 +
                commit = get_commit(e, full_name);
                if (!commit) {
                        warning("%s: Unexpected object of type %s, skipping.",
@@@ -673,19 -664,6 +676,19 @@@ static void import_marks(char *input_fi
        fclose(f);
  }
  
 +static void handle_deletes(void)
 +{
 +      int i;
 +      for (i = 0; i < refspecs_nr; i++) {
 +              struct refspec *refspec = &refspecs[i];
 +              if (*refspec->src)
 +                      continue;
 +
 +              printf("reset %s\nfrom %s\n\n",
 +                              refspec->dst, sha1_to_hex(null_sha1));
 +      }
 +}
 +
  int cmd_fast_export(int argc, const char **argv, const char *prefix)
  {
        struct rev_info revs;
        struct commit *commit;
        char *export_filename = NULL, *import_filename = NULL;
        uint32_t lastimportid;
 +      struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
        struct option options[] = {
                OPT_INTEGER(0, "progress", &progress,
                            N_("show progress after <n> objects")),
                OPT_BOOL(0, "use-done-feature", &use_done_feature,
                             N_("Use the done feature to terminate the stream")),
                OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")),
 +              OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"),
 +                           N_("Apply refspec to exported refs")),
                OPT_END()
        };
  
        revs.topo_order = 1;
        revs.show_source = 1;
        revs.rewrite_parents = 1;
 +      argc = parse_options(argc, argv, prefix, options, fast_export_usage,
 +                      PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN);
        argc = setup_revisions(argc, argv, &revs, NULL);
 -      argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0);
        if (argc > 1)
                usage_with_options (fast_export_usage, options);
  
 +      if (refspecs_list.nr) {
 +              const char **refspecs_str;
 +              int i;
 +
 +              refspecs_str = xmalloc(sizeof(*refspecs_str) * refspecs_list.nr);
 +              for (i = 0; i < refspecs_list.nr; i++)
 +                      refspecs_str[i] = refspecs_list.items[i].string;
 +
 +              refspecs_nr = refspecs_list.nr;
 +              refspecs = parse_fetch_refspec(refspecs_nr, refspecs_str);
 +
 +              string_list_clear(&refspecs_list, 1);
 +              free(refspecs_str);
 +      }
 +
        if (use_done_feature)
                printf("feature done\n");
  
        }
  
        handle_tags_and_duplicates();
 +      handle_deletes();
  
        if (export_filename && lastimportid != last_idnum)
                export_marks(export_filename);
        if (use_done_feature)
                printf("done\n");
  
 +      free_refspec(refspecs_nr, refspecs);
 +
        return 0;
  }
diff --combined builtin/index-pack.c
index 18f57de58b02de33909a40ea9720e6602cc9dcb5,459b9f07bb674e92a11df79e0c0445acb20c595a..8b3bd29dbcf22668040c53fffb6e2414ca090f5d
@@@ -40,13 -40,17 +40,13 @@@ struct base_data 
        int ofs_first, ofs_last;
  };
  
 -#if !defined(NO_PTHREADS) && defined(NO_THREAD_SAFE_PREAD)
 -/* pread() emulation is not thread-safe. Disable threading. */
 -#define NO_PTHREADS
 -#endif
 -
  struct thread_local {
  #ifndef NO_PTHREADS
        pthread_t thread;
  #endif
        struct base_data *base_cache;
        size_t base_cache_used;
 +      int pack_fd;
  };
  
  /*
@@@ -87,8 -91,7 +87,8 @@@ static off_t consumed_bytes
  static unsigned deepest_delta;
  static git_SHA_CTX input_ctx;
  static uint32_t input_crc32;
 -static int input_fd, output_fd, pack_fd;
 +static int input_fd, output_fd;
 +static const char *curr_pack;
  
  #ifndef NO_PTHREADS
  
@@@ -131,7 -134,6 +131,7 @@@ static inline void unlock_mutex(pthread
   */
  static void init_thread(void)
  {
 +      int i;
        init_recursive_mutex(&read_mutex);
        pthread_mutex_init(&counter_mutex, NULL);
        pthread_mutex_init(&work_mutex, NULL);
                pthread_mutex_init(&deepest_delta_mutex, NULL);
        pthread_key_create(&key, NULL);
        thread_data = xcalloc(nr_threads, sizeof(*thread_data));
 +      for (i = 0; i < nr_threads; i++) {
 +              thread_data[i].pack_fd = open(curr_pack, O_RDONLY);
 +              if (thread_data[i].pack_fd == -1)
 +                      die_errno(_("unable to open %s"), curr_pack);
 +      }
 +
        threads_active = 1;
  }
  
  static void cleanup_thread(void)
  {
 +      int i;
        if (!threads_active)
                return;
        threads_active = 0;
        pthread_mutex_destroy(&work_mutex);
        if (show_stat)
                pthread_mutex_destroy(&deepest_delta_mutex);
 +      for (i = 0; i < nr_threads; i++)
 +              close(thread_data[i].pack_fd);
        pthread_key_delete(key);
        free(thread_data);
  }
@@@ -207,13 -200,8 +207,13 @@@ static unsigned check_object(struct obj
        if (!(obj->flags & FLAG_CHECKED)) {
                unsigned long size;
                int type = sha1_object_info(obj->sha1, &size);
 -              if (type != obj->type || type <= 0)
 -                      die(_("object of unexpected type"));
 +              if (type <= 0)
 +                      die(_("did not receive expected object %s"),
 +                            sha1_to_hex(obj->sha1));
 +              if (type != obj->type)
 +                      die(_("object %s: expected type %s, found %s"),
 +                          sha1_to_hex(obj->sha1),
 +                          typename(obj->type), typename(type));
                obj->flags |= FLAG_CHECKED;
                return 1;
        }
@@@ -300,13 -288,13 +300,13 @@@ static const char *open_pack_file(cons
                        output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
                if (output_fd < 0)
                        die_errno(_("unable to create '%s'"), pack_name);
 -              pack_fd = output_fd;
 +              nothread_data.pack_fd = output_fd;
        } else {
                input_fd = open(pack_name, O_RDONLY);
                if (input_fd < 0)
                        die_errno(_("cannot open packfile '%s'"), pack_name);
                output_fd = -1;
 -              pack_fd = input_fd;
 +              nothread_data.pack_fd = input_fd;
        }
        git_SHA1_Init(&input_ctx);
        return pack_name;
@@@ -554,7 -542,7 +554,7 @@@ static void *unpack_data(struct object_
  
        do {
                ssize_t n = (len < 64*1024) ? len : 64*1024;
 -              n = pread(pack_fd, inbuf, n, from);
 +              n = xpread(get_thread_data()->pack_fd, inbuf, n, from);
                if (n < 0)
                        die_errno(_("cannot pread pack file"));
                if (!n)
@@@ -786,7 -774,8 +786,8 @@@ static void sha1_object(const void *dat
                        }
                        if (obj->type == OBJ_COMMIT) {
                                struct commit *commit = (struct commit *) obj;
-                               commit->buffer = NULL;
+                               if (detach_commit_buffer(commit, NULL) != data)
+                                       die("BUG: parse_object_buffer transmogrified our buffer");
                        }
                        obj->flags |= FLAG_CHECKED;
                }
@@@ -1502,7 -1491,7 +1503,7 @@@ static void show_pack_info(int stat_onl
  int cmd_index_pack(int argc, const char **argv, const char *prefix)
  {
        int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
 -      const char *curr_pack, *curr_index;
 +      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;
diff --combined builtin/log.c
index a7ba211731ecc02523c4509878679f050c151f0b,c599eacf721124831c07a8e14ea14fe1b951f403..92063dfc48a73c9b5fa4f9694dd4ac78d5575be3
@@@ -63,8 -63,6 +63,8 @@@ static int parse_decoration_style(cons
                return DECORATE_FULL_REFS;
        else if (!strcmp(value, "short"))
                return DECORATE_SHORT_REFS;
 +      else if (!strcmp(value, "auto"))
 +              return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
        return -1;
  }
  
@@@ -160,9 -158,13 +160,9 @@@ static void cmd_log_init_finish(int arg
        if (rev->show_notes)
                init_display_notes(&rev->notes_opt);
  
 -      if (rev->diffopt.pickaxe || rev->diffopt.filter)
 +      if (rev->diffopt.pickaxe || rev->diffopt.filter ||
 +          DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES))
                rev->always_show_header = 0;
 -      if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
 -              rev->always_show_header = 0;
 -              if (rev->diffopt.pathspec.nr != 1)
 -                      usage("git logs can only follow renames on one pathname at a time");
 -      }
  
        if (source)
                rev->show_source = 1;
@@@ -347,8 -349,7 +347,7 @@@ static int cmd_log_walk(struct rev_inf
                        rev->max_count++;
                if (!rev->reflog_info) {
                        /* we allow cycles in reflog ancestry */
-                       free(commit->buffer);
-                       commit->buffer = NULL;
+                       free_commit_buffer(commit);
                }
                free_commit_list(commit->parents);
                commit->parents = NULL;
@@@ -671,7 -672,6 +670,7 @@@ static void add_header(const char *valu
  static int thread;
  static int do_signoff;
  static const char *signature = git_version_string;
 +static const char *signature_file;
  static int config_cover_letter;
  
  enum {
@@@ -741,8 -741,6 +740,8 @@@ static int git_format_config(const cha
        }
        if (!strcmp(var, "format.signature"))
                return git_config_string(&signature, var, value);
 +      if (!strcmp(var, "format.signaturefile"))
 +              return git_config_pathname(&signature_file, var, value);
        if (!strcmp(var, "format.coverletter")) {
                if (value && !strcasecmp(value, "auto")) {
                        config_cover_letter = COVER_AUTO;
@@@ -845,13 -843,8 +844,13 @@@ static void gen_message_id(struct rev_i
  
  static void print_signature(void)
  {
 -      if (signature && *signature)
 -              printf("-- \n%s\n\n", signature);
 +      if (!signature || !*signature)
 +              return;
 +
 +      printf("-- \n%s", signature);
 +      if (signature[strlen(signature)-1] != '\n')
 +              putchar('\n');
 +      putchar('\n');
  }
  
  static void add_branch_description(struct strbuf *buf, const char *branch_name)
@@@ -925,9 -918,12 +924,12 @@@ static void make_cover_letter(struct re
        log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
                                &need_8bit_cte);
  
-       for (i = 0; !need_8bit_cte && i < nr; i++)
-               if (has_non_ascii(list[i]->buffer))
+       for (i = 0; !need_8bit_cte && i < nr; i++) {
+               const char *buf = get_commit_buffer(list[i], NULL);
+               if (has_non_ascii(buf))
                        need_8bit_cte = 1;
+               unuse_commit_buffer(list[i], buf);
+       }
  
        if (!branch_name)
                branch_name = find_branch_name(rev);
@@@ -1236,8 -1232,6 +1238,8 @@@ int cmd_format_patch(int argc, const ch
                            PARSE_OPT_OPTARG, thread_callback },
                OPT_STRING(0, "signature", &signature, N_("signature"),
                            N_("add a signature")),
 +              OPT_FILENAME(0, "signature-file", &signature_file,
 +                              N_("add a signature from a file")),
                OPT__QUIET(&quiet, N_("don't print the patch filenames")),
                OPT_END()
        };
                        cover_letter = (config_cover_letter == COVER_ON);
        }
  
 +      if (!signature) {
 +              ; /* --no-signature inhibits all signatures */
 +      } else if (signature && signature != git_version_string) {
 +              ; /* non-default signature already set */
 +      } else if (signature_file) {
 +              struct strbuf buf = STRBUF_INIT;
 +
 +              if (strbuf_read_file(&buf, signature_file, 128) < 0)
 +                      die_errno(_("unable to read signature file '%s'"), signature_file);
 +              signature = strbuf_detach(&buf, NULL);
 +      }
 +
        if (in_reply_to || thread || cover_letter)
                rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
        if (in_reply_to) {
                    reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
-               free(commit->buffer);
-               commit->buffer = NULL;
+               free_commit_buffer(commit);
  
                /* We put one extra blank line between formatted
                 * patches and this flag is used by log-tree code
diff --combined builtin/merge.c
index 428ca247bd17e0f18776b46d40a09282b0b955de,8763b2efa2d7e3d7af49e9bc160a67239fbd3147..b49c310866f76434c3838484c0bf997a1230c7ed
@@@ -63,7 -63,7 +63,7 @@@ static int verbosity
  static int allow_rerere_auto;
  static int abort_current_merge;
  static int show_progress = -1;
 -static int default_to_upstream;
 +static int default_to_upstream = 1;
  static const char *sign_commit;
  
  static struct strategy all_strategy[] = {
@@@ -398,7 -398,7 +398,7 @@@ static void finish(struct commit *head_
                        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
                        update_ref(reflog_message.buf, "HEAD",
                                new_head, head, 0,
 -                              DIE_ON_ERR);
 +                              UPDATE_REFS_DIE_ON_ERR);
                        /*
                         * We ignore errors in 'gc --auto', since the
                         * user should see them.
@@@ -852,8 -852,8 +852,8 @@@ static int merge_trivial(struct commit 
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
        prepare_to_commit(remoteheads);
-       if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
-                       sign_commit))
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parent,
+                       result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        finish(head, remoteheads, result_commit, "In-index merge");
        drop_save();
@@@ -877,8 -877,8 +877,8 @@@ static int finish_automerge(struct comm
                commit_list_insert(head, &parents);
        strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
-       if (commit_tree(&merge_msg, result_tree, parents, result_commit,
-                       NULL, sign_commit))
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
+                       result_commit, NULL, sign_commit))
                die(_("failed to write commit object"));
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
        finish(head, remoteheads, result_commit, buf.buf);
@@@ -1222,7 -1222,7 +1222,7 @@@ int cmd_merge(int argc, const char **ar
                        die(_("%s - not something we can merge"), argv[0]);
                read_empty(remote_head->object.sha1, 0);
                update_ref("initial pull", "HEAD", remote_head->object.sha1,
 -                         NULL, 0, DIE_ON_ERR);
 +                         NULL, 0, UPDATE_REFS_DIE_ON_ERR);
                goto done;
        } else {
                struct strbuf merge_names = STRBUF_INIT;
        }
  
        update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
 -                 NULL, 0, DIE_ON_ERR);
 +                 NULL, 0, UPDATE_REFS_DIE_ON_ERR);
  
        if (remoteheads && !common)
                ; /* No common ancestors found. We need a real merge. */
diff --combined builtin/reset.c
index f368266762fae1e6c783d7351e289e423e408b3c,6bd6245821046dc593fa4594f3a4227465098231..850d53229a1ac757d294ffca6261c9a28af014df
@@@ -93,7 -93,7 +93,7 @@@ static int reset_index(const unsigned c
  static void print_new_head_line(struct commit *commit)
  {
        const char *hex, *body;
-       char *msg;
+       const char *msg;
  
        hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        printf(_("HEAD is now at %s"), hex);
        }
        else
                printf("\n");
-       logmsg_free(msg, commit);
+       unuse_commit_buffer(commit, msg);
  }
  
  static void update_index_from_diff(struct diff_queue_struct *q,
@@@ -252,13 -252,11 +252,13 @@@ static int reset_refs(const char *rev, 
        if (!get_sha1("HEAD", sha1_orig)) {
                orig = sha1_orig;
                set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
 -              update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
 +              update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
 +                         UPDATE_REFS_MSG_ON_ERR);
        } else if (old_orig)
                delete_ref("ORIG_HEAD", old_orig, 0);
        set_reflog_message(&msg, "updating HEAD", rev);
 -      update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR);
 +      update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0,
 +                                     UPDATE_REFS_MSG_ON_ERR);
        strbuf_release(&msg);
        return update_ref_status;
  }
diff --combined commit.c
index 881be3baa3ccc70afab916331588afb9330258ed,ebd7ad8465672004c56386de686d9ba7ff0eccdb..4ff8077dbfbc6cdd6c14728a3d33a78d075c236f
+++ b/commit.c
@@@ -17,7 -17,6 +17,6 @@@ static struct commit_extra_header *read
  int save_commit_buffer = 1;
  
  const char *commit_type = "commit";
- static int commit_count;
  
  static struct commit *check_commit(struct object *obj,
                                   const unsigned char *sha1,
@@@ -64,7 -63,6 +63,6 @@@ struct commit *lookup_commit(const unsi
        struct object *obj = lookup_object(sha1);
        if (!obj) {
                struct commit *c = alloc_commit_node();
-               c->index = commit_count++;
                return create_object(sha1, OBJ_COMMIT, c);
        }
        if (!obj->type)
@@@ -247,6 -245,76 +245,76 @@@ int unregister_shallow(const unsigned c
        return 0;
  }
  
+ struct commit_buffer {
+       void *buffer;
+       unsigned long size;
+ };
+ define_commit_slab(buffer_slab, struct commit_buffer);
+ static struct buffer_slab buffer_slab = COMMIT_SLAB_INIT(1, buffer_slab);
+ void set_commit_buffer(struct commit *commit, void *buffer, unsigned long size)
+ {
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       v->buffer = buffer;
+       v->size = size;
+ }
+ const void *get_cached_commit_buffer(const struct commit *commit, unsigned long *sizep)
+ {
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       if (sizep)
+               *sizep = v->size;
+       return v->buffer;
+ }
+ const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
+ {
+       const void *ret = get_cached_commit_buffer(commit, sizep);
+       if (!ret) {
+               enum object_type type;
+               unsigned long size;
+               ret = read_sha1_file(commit->object.sha1, &type, &size);
+               if (!ret)
+                       die("cannot read commit object %s",
+                           sha1_to_hex(commit->object.sha1));
+               if (type != OBJ_COMMIT)
+                       die("expected commit for %s, got %s",
+                           sha1_to_hex(commit->object.sha1), typename(type));
+               if (sizep)
+                       *sizep = size;
+       }
+       return ret;
+ }
+ void unuse_commit_buffer(const struct commit *commit, const void *buffer)
+ {
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       if (v->buffer != buffer)
+               free((void *)buffer);
+ }
+ void free_commit_buffer(struct commit *commit)
+ {
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       free(v->buffer);
+       v->buffer = NULL;
+       v->size = 0;
+ }
+ const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
+ {
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       void *ret;
+       ret = v->buffer;
+       if (sizep)
+               *sizep = v->size;
+       v->buffer = NULL;
+       v->size = 0;
+       return ret;
+ }
  int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
  {
        const char *tail = buffer;
@@@ -324,7 -392,7 +392,7 @@@ int parse_commit(struct commit *item
        }
        ret = parse_commit_buffer(item, buffer, size);
        if (save_commit_buffer && !ret) {
-               item->buffer = buffer;
+               set_commit_buffer(item, buffer, size);
                return 0;
        }
        free(buffer);
@@@ -539,22 -607,12 +607,12 @@@ static void record_author_date(struct a
                               struct commit *commit)
  {
        const char *buf, *line_end, *ident_line;
-       char *buffer = NULL;
+       const char *buffer = get_commit_buffer(commit, NULL);
        struct ident_split ident;
        char *date_end;
        unsigned long date;
  
-       if (!commit->buffer) {
-               unsigned long size;
-               enum object_type type;
-               buffer = read_sha1_file(commit->object.sha1, &type, &size);
-               if (!buffer)
-                       return;
-       }
-       for (buf = commit->buffer ? commit->buffer : buffer;
-            buf;
-            buf = line_end + 1) {
+       for (buf = buffer; buf; buf = line_end + 1) {
                line_end = strchrnul(buf, '\n');
                ident_line = skip_prefix(buf, "author ");
                if (!ident_line) {
        *(author_date_slab_at(author_date, commit)) = date;
  
  fail_exit:
-       free(buffer);
+       unuse_commit_buffer(commit, buffer);
  }
  
  static int compare_commits_by_author_date(const void *a_, const void *b_,
@@@ -1031,7 -1089,7 +1089,7 @@@ struct commit_list *reduce_heads(struc
                p->item->object.flags |= STALE;
                num_head++;
        }
 -      array = xcalloc(sizeof(*array), num_head);
 +      array = xcalloc(num_head, sizeof(*array));
        for (p = heads, i = 0; p; p = p->next) {
                if (p->item->object.flags & STALE) {
                        array[i++] = p->item;
@@@ -1080,17 -1138,14 +1138,14 @@@ static int do_sign_commit(struct strbu
        return 0;
  }
  
- int parse_signed_commit(const unsigned char *sha1,
+ int parse_signed_commit(const struct commit *commit,
                        struct strbuf *payload, struct strbuf *signature)
  {
        unsigned long size;
-       enum object_type type;
-       char *buffer = read_sha1_file(sha1, &type, &size);
+       const char *buffer = get_commit_buffer(commit, &size);
        int in_signature, saw_signature = -1;
-       char *line, *tail;
-       if (!buffer || type != OBJ_COMMIT)
-               goto cleanup;
+       const char *line, *tail;
  
        line = buffer;
        tail = buffer + size;
        saw_signature = 0;
        while (line < tail) {
                const char *sig = NULL;
-               char *next = memchr(line, '\n', tail - line);
+               const char *next = memchr(line, '\n', tail - line);
  
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
                }
                line = next;
        }
-  cleanup:
-       free(buffer);
+       unuse_commit_buffer(commit, buffer);
        return saw_signature;
  }
  
@@@ -1211,8 -1265,7 +1265,7 @@@ void check_commit_signature(const struc
  
        sigc->result = 'N';
  
-       if (parse_signed_commit(commit->object.sha1,
-                               &payload, &signature) <= 0)
+       if (parse_signed_commit(commit, &payload, &signature) <= 0)
                goto out;
        status = verify_signed_buffer(payload.buf, payload.len,
                                      signature.buf, signature.len,
@@@ -1257,11 -1310,9 +1310,9 @@@ struct commit_extra_header *read_commit
  {
        struct commit_extra_header *extra = NULL;
        unsigned long size;
-       enum object_type type;
-       char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
-       if (buffer && type == OBJ_COMMIT)
-               extra = read_commit_extra_header_lines(buffer, size, exclude);
-       free(buffer);
+       const char *buffer = get_commit_buffer(commit, &size);
+       extra = read_commit_extra_header_lines(buffer, size, exclude);
+       unuse_commit_buffer(commit, buffer);
        return extra;
  }
  
@@@ -1344,7 -1395,8 +1395,8 @@@ void free_commit_extra_headers(struct c
        }
  }
  
- int commit_tree(const struct strbuf *msg, const unsigned char *tree,
+ int commit_tree(const char *msg, size_t msg_len,
+               const unsigned char *tree,
                struct commit_list *parents, unsigned char *ret,
                const char *author, const char *sign_commit)
  {
        int result;
  
        append_merge_tag_headers(parents, &tail);
-       result = commit_tree_extended(msg, tree, parents, ret,
+       result = commit_tree_extended(msg, msg_len, tree, parents, ret,
                                      author, sign_commit, extra);
        free_commit_extra_headers(extra);
        return result;
@@@ -1473,7 -1525,8 +1525,8 @@@ static const char commit_utf8_warn[] 
  "You may want to amend it after fixing the message, or set the config\n"
  "variable i18n.commitencoding to the encoding your project uses.\n";
  
- int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
+ int commit_tree_extended(const char *msg, size_t msg_len,
+                        const unsigned char *tree,
                         struct commit_list *parents, unsigned char *ret,
                         const char *author, const char *sign_commit,
                         struct commit_extra_header *extra)
  
        assert_sha1_type(tree, OBJ_TREE);
  
-       if (memchr(msg->buf, '\0', msg->len))
+       if (memchr(msg, '\0', msg_len))
                return error("a NUL byte in commit log message not allowed.");
  
        /* Not having i18n.commitencoding is the same as having utf-8 */
        strbuf_addch(&buffer, '\n');
  
        /* And add the comment */
-       strbuf_addbuf(&buffer, msg);
+       strbuf_add(&buffer, msg, msg_len);
  
        /* And check the encoding */
        if (encoding_is_utf8 && !verify_utf8(&buffer))
diff --combined merge-recursive.c
index cab16fafb5c2b7792c78d3a7fbace1f43ab099cc,a9ab328923db3f2b2c945c479386316333dfcd0a..d38a3b2eb5bf4b0940a2aa4d5c32f02b9f35cc15
@@@ -40,7 -40,7 +40,7 @@@ static struct tree *shift_tree_object(s
  
  static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
  {
-       struct commit *commit = xcalloc(1, sizeof(struct commit));
+       struct commit *commit = alloc_commit_node();
        struct merge_remote_desc *desc = xmalloc(sizeof(*desc));
  
        desc->name = comment;
@@@ -190,9 -190,11 +190,11 @@@ static void output_commit_title(struct 
                        printf(_("(bad commit)\n"));
                else {
                        const char *title;
-                       int len = find_commit_subject(commit->buffer, &title);
+                       const char *msg = get_commit_buffer(commit, NULL);
+                       int len = find_commit_subject(msg, &title);
                        if (len)
                                printf("%.*s\n", len, title);
+                       unuse_commit_buffer(commit, msg);
                }
        }
  }
@@@ -589,12 -591,6 +591,12 @@@ static int remove_file(struct merge_opt
                        return -1;
        }
        if (update_working_directory) {
 +              if (ignore_case) {
 +                      struct cache_entry *ce;
 +                      ce = cache_file_exists(path, strlen(path), ignore_case);
 +                      if (ce && ce_stage(ce) == 0)
 +                              return 0;
 +              }
                if (remove_path(path))
                        return -1;
        }
diff --combined notes-cache.c
index 97dfd63c9bf564ac052addc4a3f62a325bbc1303,25b20aa21ff3896111e99c356648e934060b1825..c4e9bb7f6c0bde97c7c6d34094baa5ef7e318157
@@@ -48,7 -48,6 +48,6 @@@ int notes_cache_write(struct notes_cach
  {
        unsigned char tree_sha1[20];
        unsigned char commit_sha1[20];
-       struct strbuf msg = STRBUF_INIT;
  
        if (!c || !c->tree.initialized || !c->tree.ref || !*c->tree.ref)
                return -1;
  
        if (write_notes_tree(&c->tree, tree_sha1))
                return -1;
-       strbuf_attach(&msg, c->validity,
-                     strlen(c->validity), strlen(c->validity) + 1);
-       if (commit_tree(&msg, tree_sha1, NULL, commit_sha1, NULL, NULL) < 0)
+       if (commit_tree(c->validity, strlen(c->validity), tree_sha1, NULL,
+                       commit_sha1, NULL, NULL) < 0)
                return -1;
        if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
 -                     0, QUIET_ON_ERR) < 0)
 +                     0, UPDATE_REFS_QUIET_ON_ERR) < 0)
                return -1;
  
        return 0;
diff --combined notes-utils.c
index a0b1d7be98253368921a6e95653ceb36dbda28b2,f6891f7255a90abc515aa56ee39cb84711d163cf..b64dc1b0219928ac149894c99a63dcc9fd419668
@@@ -4,7 -4,8 +4,8 @@@
  #include "notes-utils.h"
  
  void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
-                        const struct strbuf *msg, unsigned char *result_sha1)
+                        const char *msg, size_t msg_len,
+                        unsigned char *result_sha1)
  {
        unsigned char tree_sha1[20];
  
@@@ -25,7 -26,7 +26,7 @@@
                /* else: t->ref points to nothing, assume root/orphan commit */
        }
  
-       if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
+       if (commit_tree(msg, msg_len, tree_sha1, parents, result_sha1, NULL, NULL))
                die("Failed to commit notes tree to database");
  }
  
@@@ -46,10 -47,9 +47,10 @@@ void commit_notes(struct notes_tree *t
        if (buf.buf[buf.len - 1] != '\n')
                strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
  
-       create_notes_commit(t, NULL, &buf, commit_sha1);
+       create_notes_commit(t, NULL, buf.buf, buf.len, commit_sha1);
        strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
 -      update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
 +      update_ref(buf.buf, t->ref, commit_sha1, NULL, 0,
 +                 UPDATE_REFS_DIE_ON_ERR);
  
        strbuf_release(&buf);
  }
diff --combined pretty.c
index 924bc61004d96d2bba31c6c7ea64560b53d18852,b9fceedbb9ffe090af689178ead7218836208c31..c93c14b080b8f23fc30fa58b85c9d70f063c7225
+++ b/pretty.c
@@@ -274,7 -274,7 +274,7 @@@ static void add_rfc822_quoted(struct st
  
  enum rfc2047_type {
        RFC2047_SUBJECT,
 -      RFC2047_ADDRESS,
 +      RFC2047_ADDRESS
  };
  
  static int is_rfc2047_special(char ch, enum rfc2047_type type)
@@@ -393,8 -393,8 +393,8 @@@ static void add_rfc2047(struct strbuf *
        strbuf_addstr(sb, "?=");
  }
  
 -static const char *show_ident_date(const struct ident_split *ident,
 -                                 enum date_mode mode)
 +const char *show_ident_date(const struct ident_split *ident,
 +                          enum date_mode mode)
  {
        unsigned long date = 0;
        long tz = 0;
@@@ -606,29 -606,16 +606,16 @@@ static char *replace_encoding_header(ch
        return strbuf_detach(&tmp, NULL);
  }
  
- char *logmsg_reencode(const struct commit *commit,
-                     char **commit_encoding,
-                     const char *output_encoding)
+ const char *logmsg_reencode(const struct commit *commit,
+                           char **commit_encoding,
+                           const char *output_encoding)
  {
        static const char *utf8 = "UTF-8";
        const char *use_encoding;
        char *encoding;
-       char *msg = commit->buffer;
+       const char *msg = get_commit_buffer(commit, NULL);
        char *out;
  
-       if (!msg) {
-               enum object_type type;
-               unsigned long size;
-               msg = read_sha1_file(commit->object.sha1, &type, &size);
-               if (!msg)
-                       die("Cannot read commit object %s",
-                           sha1_to_hex(commit->object.sha1));
-               if (type != OBJ_COMMIT)
-                       die("Expected commit for '%s', got %s",
-                           sha1_to_hex(commit->object.sha1), typename(type));
-       }
        if (!output_encoding || !*output_encoding) {
                if (commit_encoding)
                        *commit_encoding =
                 * Otherwise, we still want to munge the encoding header in the
                 * result, which will be done by modifying the buffer. If we
                 * are using a fresh copy, we can reuse it. But if we are using
-                * the cached copy from commit->buffer, we need to duplicate it
-                * to avoid munging commit->buffer.
+                * the cached copy from get_commit_buffer, we need to duplicate it
+                * to avoid munging the cached copy.
                 */
-               out = msg;
-               if (out == commit->buffer)
-                       out = xstrdup(out);
+               if (msg == get_cached_commit_buffer(commit, NULL))
+                       out = xstrdup(msg);
+               else
+                       out = (char *)msg;
        }
        else {
                /*
                 * copy, we can free it.
                 */
                out = reencode_string(msg, output_encoding, use_encoding);
-               if (out && msg != commit->buffer)
-                       free(msg);
+               if (out)
+                       unuse_commit_buffer(commit, msg);
        }
  
        /*
        return out ? out : msg;
  }
  
- void logmsg_free(char *msg, const struct commit *commit)
- {
-       if (msg != commit->buffer)
-               free(msg);
- }
  static int mailmap_name(const char **email, size_t *email_len,
                        const char **name, size_t *name_len)
  {
@@@ -796,7 -778,7 +778,7 @@@ struct format_commit_context 
        struct signature_check signature_check;
        enum flush_type flush_type;
        enum trunc_type truncate;
-       char *message;
+       const char *message;
        char *commit_encoding;
        size_t width, indent1, indent2;
        int auto_color;
@@@ -1506,18 -1488,13 +1488,18 @@@ void format_commit_message(const struc
        context.commit = commit;
        context.pretty_ctx = pretty_ctx;
        context.wrap_start = sb->len;
 +      /*
 +       * convert a commit message to UTF-8 first
 +       * as far as 'format_commit_item' assumes it in UTF-8
 +       */
        context.message = logmsg_reencode(commit,
                                          &context.commit_encoding,
 -                                        output_enc);
 +                                        utf8);
  
        strbuf_expand(sb, format, format_commit_item, &context);
        rewrap_message_tail(sb, &context, 0, 0, 0);
  
 +      /* then convert a commit message to an actual output encoding */
        if (output_enc) {
                if (same_encoding(utf8, output_enc))
                        output_enc = NULL;
        }
  
        free(context.commit_encoding);
-       logmsg_free(context.message, commit);
+       unuse_commit_buffer(commit, context.message);
        free(context.signature_check.gpg_output);
        free(context.signature_check.signer);
  }
@@@ -1705,7 -1682,7 +1687,7 @@@ void pretty_print_commit(struct pretty_
        unsigned long beginning_of_body;
        int indent = 4;
        const char *msg;
-       char *reencoded;
+       const char *reencoded;
        const char *encoding;
        int need_8bit_cte = pp->need_8bit_cte;
  
        if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
  
-       logmsg_free(reencoded, commit);
+       unuse_commit_buffer(commit, reencoded);
  }
  
  void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
diff --combined revision.c
index 8351e794df943ea6ea7acde40c01c97c20be4576,1cc91e591156147c5a7ee6df2830dcbc1ac2d559..2571ada6bf66ce6e945c539be4b0123e5b9648a9
@@@ -1633,7 -1633,6 +1633,7 @@@ static int handle_revision_opt(struct r
            !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
            !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
 +          starts_with(arg, "--exclude=") ||
            starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
            starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
        {
                revs->skip_count = atoi(optarg);
                return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
 -      /* accept -<digit>, like traditional "head" */
 -              revs->max_count = atoi(arg + 1);
 +              /* accept -<digit>, like traditional "head" */
 +              if (strtol_i(arg + 1, 10, &revs->max_count) < 0 ||
 +                  revs->max_count < 0)
 +                      die("'%s': not a non-negative integer", arg + 1);
                revs->no_walk = 0;
        } else if (!strcmp(arg, "-n")) {
                if (argc <= 1)
@@@ -2791,7 -2788,7 +2791,7 @@@ static int commit_match(struct commit *
  {
        int retval;
        const char *encoding;
-       char *message;
+       const char *message;
        struct strbuf buf = STRBUF_INIT;
  
        if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
                format_display_notes(commit->object.sha1, &buf, encoding, 1);
        }
  
-       /* Find either in the original commit message, or in the temporary */
+       /*
+        * Find either in the original commit message, or in the temporary.
+        * Note that we cast away the constness of "message" here. It is
+        * const because it may come from the cached commit buffer. That's OK,
+        * because we know that it is modifiable heap memory, and that while
+        * grep_buffer may modify it for speed, it will restore any
+        * changes before returning.
+        */
        if (buf.len)
                retval = grep_buffer(&opt->grep_filter, buf.buf, buf.len);
        else
                retval = grep_buffer(&opt->grep_filter,
-                                    message, strlen(message));
+                                    (char *)message, strlen(message));
        strbuf_release(&buf);
-       logmsg_free(message, commit);
+       unuse_commit_buffer(commit, message);
        return retval;
  }
  
diff --combined sequencer.c
index 923047459bd3079da74245f2a0185ec437c9e8cd,bbaddcb05af3d720d8efd1d1d7c96ce1f9a35039..c513d7eeb4d747cbc41950e3ff197aca8d81d6d4
@@@ -116,39 -116,23 +116,23 @@@ static const char *action_name(const st
        return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
  }
  
- static char *get_encoding(const char *message);
  struct commit_message {
        char *parent_label;
        const char *label;
        const char *subject;
-       char *reencoded_message;
        const char *message;
  };
  
  static int get_message(struct commit *commit, struct commit_message *out)
  {
-       const char *encoding;
        const char *abbrev, *subject;
        int abbrev_len, subject_len;
        char *q;
  
-       if (!commit->buffer)
-               return -1;
-       encoding = get_encoding(commit->buffer);
-       if (!encoding)
-               encoding = "UTF-8";
        if (!git_commit_encoding)
                git_commit_encoding = "UTF-8";
  
-       out->reencoded_message = NULL;
-       out->message = commit->buffer;
-       if (same_encoding(encoding, git_commit_encoding))
-               out->reencoded_message = reencode_string(commit->buffer,
-                                       git_commit_encoding, encoding);
-       if (out->reencoded_message)
-               out->message = out->reencoded_message;
+       out->message = logmsg_reencode(commit, NULL, git_commit_encoding);
        abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        abbrev_len = strlen(abbrev);
  
        return 0;
  }
  
- static void free_message(struct commit_message *msg)
+ static void free_message(struct commit *commit, struct commit_message *msg)
  {
        free(msg->parent_label);
-       free(msg->reencoded_message);
- }
- static char *get_encoding(const char *message)
- {
-       const char *p = message, *eol;
-       while (*p && *p != '\n') {
-               for (eol = p + 1; *eol && *eol != '\n'; eol++)
-                       ; /* do nothing */
-               if (starts_with(p, "encoding ")) {
-                       char *result = xmalloc(eol - 8 - p);
-                       strlcpy(result, p + 9, eol - 8 - p);
-                       return result;
-               }
-               p = eol;
-               if (*p == '\n')
-                       p++;
-       }
-       return NULL;
+       unuse_commit_buffer(commit, msg->message);
  }
  
  static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
@@@ -278,15 -243,11 +243,15 @@@ static int fast_forward_to(const unsign
  
        read_cache();
        if (checkout_fast_forward(from, to, 1))
 -              exit(1); /* the callee should have complained already */
 +              exit(128); /* the callee should have complained already */
        ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from,
                                           0, NULL);
 +      if (!ref_lock)
 +              return error(_("Failed to lock HEAD during fast_forward_to"));
 +
        strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
        ret = write_ref_sha1(ref_lock, to, sb.buf);
 +
        strbuf_release(&sb);
        return ret;
  }
@@@ -489,7 -450,7 +454,7 @@@ static int do_pick_commit(struct commi
        unsigned char head[20];
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
-       struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+       struct commit_message msg = { NULL, NULL, NULL, NULL };
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
        int res, unborn = 0, allow;
                res = run_git_commit(defmsg, opts, allow);
  
  leave:
-       free_message(&msg);
+       free_message(commit, &msg);
        free(defmsg);
  
        return res;
@@@ -701,10 -662,12 +666,12 @@@ static int format_todo(struct strbuf *b
        int subject_len;
  
        for (cur = todo_list; cur; cur = cur->next) {
+               const char *commit_buffer = get_commit_buffer(cur->item, NULL);
                sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
-               subject_len = find_commit_subject(cur->item->buffer, &subject);
+               subject_len = find_commit_subject(commit_buffer, &subject);
                strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
                        subject_len, subject);
+               unuse_commit_buffer(cur->item, commit_buffer);
        }
        return 0;
  }