Merge branch 'jc/commit-slab'
authorJunio C Hamano <gitster@pobox.com>
Mon, 3 Aug 2015 18:01:21 +0000 (11:01 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 3 Aug 2015 18:01:21 +0000 (11:01 -0700)
Memory use reduction when commit-slab facility is used to annotate
sparsely (which is not recommended in the first place).

* jc/commit-slab:
commit-slab: introduce slabname##_peek() function

1  2 
commit-slab.h
commit.c
diff --combined commit-slab.h
index f37ec3831f60cb9072d6cbf4f3d0126bc0681bf1,9d12ce299cb844bd7bd74d0b5813af2932868773..f84b449413d434b8145426503a98bcd4f9ad995f
   * - int *indegree_at(struct indegree *, struct commit *);
   *
   *   This function locates the data associated with the given commit in
-  *   the indegree slab, and returns the pointer to it.
+  *   the indegree slab, and returns the pointer to it.  The location to
+  *   store the data is allocated as necessary.
+  *
+  * - int *indegree_peek(struct indegree *, struct commit *);
+  *
+  *   This function is similar to indegree_at(), but it will return NULL
+  *   until a call to indegree_at() was made for the commit.
   *
   * - void init_indegree(struct indegree *);
   *   void init_indegree_with_stride(struct indegree *, int);
@@@ -80,8 -86,9 +86,9 @@@ static MAYBE_UNUSED void clear_ ##slabn
        s->slab = NULL;                                                 \
  }                                                                     \
                                                                        \
- static MAYBE_UNUSED elemtype *slabname## _at(struct slabname *s,      \
-                                      const struct commit *c)          \
+ static MAYBE_UNUSED elemtype *slabname## _at_peek(struct slabname *s, \
+                                                 const struct commit *c, \
+                                                 int add_if_missing)   \
  {                                                                     \
        int nth_slab, nth_slot;                                         \
                                                                        \
                                                                        \
        if (s->slab_count <= nth_slab) {                                \
                int i;                                                  \
 -              s->slab = xrealloc(s->slab,                             \
 -                                 (nth_slab + 1) * sizeof(*s->slab));  \
+               if (!add_if_missing)                                    \
+                       return NULL;                                    \
 +              REALLOC_ARRAY(s->slab, nth_slab + 1);                   \
                stat_ ##slabname## realloc++;                           \
                for (i = s->slab_count; i <= nth_slab; i++)             \
                        s->slab[i] = NULL;                              \
                s->slab_count = nth_slab + 1;                           \
        }                                                               \
-       if (!s->slab[nth_slab])                                         \
+       if (!s->slab[nth_slab]) {                                       \
+               if (!add_if_missing)                                    \
+                       return NULL;                                    \
                s->slab[nth_slab] = xcalloc(s->slab_size,               \
                                            sizeof(**s->slab) * s->stride);             \
-       return &s->slab[nth_slab][nth_slot * s->stride];                                \
+       }                                                               \
+       return &s->slab[nth_slab][nth_slot * s->stride];                \
+ }                                                                     \
+                                                                       \
+ static MAYBE_UNUSED elemtype *slabname## _at(struct slabname *s,      \
+                                            const struct commit *c)    \
+ {                                                                     \
+       return slabname##_at_peek(s, c, 1);                             \
+ }                                                                     \
+                                                                       \
+ static MAYBE_UNUSED elemtype *slabname## _peek(struct slabname *s,    \
+                                            const struct commit *c)    \
+ {                                                                     \
+       return slabname##_at_peek(s, c, 0);                             \
  }                                                                     \
                                                                        \
  static int stat_ ##slabname## realloc
diff --combined commit.c
index 62223a33e5d4d05a44efca48d5ea9fbfa2e957a3,5fb9496520df4813148aa56df44216bad015194c..494615d6ff15af5eb95e78053b2799e4c55577fc
+++ b/commit.c
@@@ -55,12 -55,12 +55,12 @@@ struct commit *lookup_commit(const unsi
  
  struct commit *lookup_commit_reference_by_name(const char *name)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
        struct commit *commit;
  
 -      if (get_sha1_committish(name, sha1))
 +      if (get_sha1_committish(name, oid.hash))
                return NULL;
 -      commit = lookup_commit_reference(sha1);
 +      commit = lookup_commit_reference(oid.hash);
        if (parse_commit(commit))
                return NULL;
        return commit;
@@@ -99,7 -99,7 +99,7 @@@ static int commit_graft_alloc, commit_g
  static const unsigned char *commit_graft_sha1_access(size_t index, void *table)
  {
        struct commit_graft **commit_graft_table = table;
 -      return commit_graft_table[index]->sha1;
 +      return commit_graft_table[index]->oid.hash;
  }
  
  static int commit_graft_pos(const unsigned char *sha1)
  
  int register_commit_graft(struct commit_graft *graft, int ignore_dups)
  {
 -      int pos = commit_graft_pos(graft->sha1);
 +      int pos = commit_graft_pos(graft->oid.hash);
  
        if (0 <= pos) {
                if (ignore_dups)
@@@ -138,23 -138,22 +138,23 @@@ struct commit_graft *read_graft_line(ch
        /* The format is just "Commit Parent1 Parent2 ...\n" */
        int i;
        struct commit_graft *graft = NULL;
 +      const int entry_size = GIT_SHA1_HEXSZ + 1;
  
        while (len && isspace(buf[len-1]))
                buf[--len] = '\0';
        if (buf[0] == '#' || buf[0] == '\0')
                return NULL;
 -      if ((len + 1) % 41)
 +      if ((len + 1) % entry_size)
                goto bad_graft_data;
 -      i = (len + 1) / 41 - 1;
 -      graft = xmalloc(sizeof(*graft) + 20 * i);
 +      i = (len + 1) / entry_size - 1;
 +      graft = xmalloc(sizeof(*graft) + GIT_SHA1_RAWSZ * i);
        graft->nr_parent = i;
 -      if (get_sha1_hex(buf, graft->sha1))
 +      if (get_oid_hex(buf, &graft->oid))
                goto bad_graft_data;
 -      for (i = 40; i < len; i += 41) {
 +      for (i = GIT_SHA1_HEXSZ; i < len; i += entry_size) {
                if (buf[i] != ' ')
                        goto bad_graft_data;
 -              if (get_sha1_hex(buf + i + 1, graft->parent[i/41]))
 +              if (get_sha1_hex(buf + i + 1, graft->parent[i/entry_size].hash))
                        goto bad_graft_data;
        }
        return graft;
@@@ -245,7 -244,12 +245,12 @@@ void set_commit_buffer(struct commit *c
  
  const void *get_cached_commit_buffer(const struct commit *commit, unsigned long *sizep)
  {
-       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit);
+       if (!v) {
+               if (sizep)
+                       *sizep = 0;
+               return NULL;
+       }
        if (sizep)
                *sizep = v->size;
        return v->buffer;
@@@ -272,24 -276,31 +277,31 @@@ const void *get_commit_buffer(const str
  
  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)
+       struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit);
+       if (!(v && 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;
+       struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit);
+       if (v) {
+               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);
+       struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit);
        void *ret;
  
+       if (!v) {
+               if (sizep)
+                       *sizep = 0;
+               return NULL;
+       }
        ret = v->buffer;
        if (sizep)
                *sizep = v->size;
@@@ -303,42 -314,39 +315,42 @@@ int parse_commit_buffer(struct commit *
  {
        const char *tail = buffer;
        const char *bufptr = buffer;
 -      unsigned char parent[20];
 +      struct object_id parent;
        struct commit_list **pptr;
        struct commit_graft *graft;
 +      const int tree_entry_len = GIT_SHA1_HEXSZ + 5;
 +      const int parent_entry_len = GIT_SHA1_HEXSZ + 7;
  
        if (item->object.parsed)
                return 0;
        item->object.parsed = 1;
        tail += size;
 -      if (tail <= bufptr + 46 || memcmp(bufptr, "tree ", 5) || bufptr[45] != '\n')
 +      if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
 +                      bufptr[tree_entry_len] != '\n')
                return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
 -      if (get_sha1_hex(bufptr + 5, parent) < 0)
 +      if (get_sha1_hex(bufptr + 5, parent.hash) < 0)
                return error("bad tree pointer in commit %s",
                             sha1_to_hex(item->object.sha1));
 -      item->tree = lookup_tree(parent);
 -      bufptr += 46; /* "tree " + "hex sha1" + "\n" */
 +      item->tree = lookup_tree(parent.hash);
 +      bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
  
        graft = lookup_commit_graft(item->object.sha1);
 -      while (bufptr + 48 < tail && !memcmp(bufptr, "parent ", 7)) {
 +      while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
                struct commit *new_parent;
  
 -              if (tail <= bufptr + 48 ||
 -                  get_sha1_hex(bufptr + 7, parent) ||
 -                  bufptr[47] != '\n')
 +              if (tail <= bufptr + parent_entry_len + 1 ||
 +                  get_sha1_hex(bufptr + 7, parent.hash) ||
 +                  bufptr[parent_entry_len] != '\n')
                        return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
 -              bufptr += 48;
 +              bufptr += parent_entry_len + 1;
                /*
                 * The clone is shallow if nr_parent < 0, and we must
                 * not traverse its real parents even when we unhide them.
                 */
                if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
                        continue;
 -              new_parent = lookup_commit(parent);
 +              new_parent = lookup_commit(parent.hash);
                if (new_parent)
                        pptr = &commit_list_insert(new_parent, pptr)->next;
        }
                int i;
                struct commit *new_parent;
                for (i = 0; i < graft->nr_parent; i++) {
 -                      new_parent = lookup_commit(graft->parent[i]);
 +                      new_parent = lookup_commit(graft->parent[i].hash);
                        if (!new_parent)
                                continue;
                        pptr = &commit_list_insert(new_parent, pptr)->next;
        return 0;
  }
  
 -int parse_commit(struct commit *item)
 +int parse_commit_gently(struct commit *item, int quiet_on_missing)
  {
        enum object_type type;
        void *buffer;
                return 0;
        buffer = read_sha1_file(item->object.sha1, &type, &size);
        if (!buffer)
 -              return error("Could not read %s",
 +              return quiet_on_missing ? -1 :
 +                      error("Could not read %s",
                             sha1_to_hex(item->object.sha1));
        if (type != OBJ_COMMIT) {
                free(buffer);
@@@ -435,7 -442,12 +447,7 @@@ struct commit_list *copy_commit_list(st
        struct commit_list *head = NULL;
        struct commit_list **pp = &head;
        while (list) {
 -              struct commit_list *new;
 -              new = xmalloc(sizeof(struct commit_list));
 -              new->item = list->item;
 -              new->next = NULL;
 -              *pp = new;
 -              pp = &new->next;
 +              pp = commit_list_append(list->item, pp);
                list = list->next;
        }
        return head;
@@@ -589,19 -601,26 +601,19 @@@ define_commit_slab(author_date_slab, un
  static void record_author_date(struct author_date_slab *author_date,
                               struct commit *commit)
  {
 -      const char *buf, *line_end, *ident_line;
        const char *buffer = get_commit_buffer(commit, NULL);
        struct ident_split ident;
 +      const char *ident_line;
 +      size_t ident_len;
        char *date_end;
        unsigned long date;
  
 -      for (buf = buffer; buf; buf = line_end + 1) {
 -              line_end = strchrnul(buf, '\n');
 -              ident_line = skip_prefix(buf, "author ");
 -              if (!ident_line) {
 -                      if (!line_end[0] || line_end[1] == '\n')
 -                              return; /* end of header */
 -                      continue;
 -              }
 -              if (split_ident_line(&ident,
 -                                   ident_line, line_end - ident_line) ||
 -                  !ident.date_begin || !ident.date_end)
 -                      goto fail_exit; /* malformed "author" line */
 -              break;
 -      }
 +      ident_line = find_commit_header(buffer, "author", &ident_len);
 +      if (!ident_line)
 +              goto fail_exit; /* no author line */
 +      if (split_ident_line(&ident, ident_line, ident_len) ||
 +          !ident.date_begin || !ident.date_end)
 +              goto fail_exit; /* malformed "author" line */
  
        date = strtoul(ident.date_begin, &date_end, 10);
        if (date_end != ident.date_end)
@@@ -763,41 -782,45 +775,41 @@@ void sort_in_topological_order(struct c
  
  static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
  
 -static struct commit *interesting(struct commit_list *list)
 +static int queue_has_nonstale(struct prio_queue *queue)
  {
 -      while (list) {
 -              struct commit *commit = list->item;
 -              list = list->next;
 -              if (commit->object.flags & STALE)
 -                      continue;
 -              return commit;
 +      int i;
 +      for (i = 0; i < queue->nr; i++) {
 +              struct commit *commit = queue->array[i].data;
 +              if (!(commit->object.flags & STALE))
 +                      return 1;
        }
 -      return NULL;
 +      return 0;
  }
  
  /* all input commits in one and twos[] must have been parsed! */
  static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos)
  {
 -      struct commit_list *list = NULL;
 +      struct prio_queue queue = { compare_commits_by_commit_date };
        struct commit_list *result = NULL;
        int i;
  
        one->object.flags |= PARENT1;
 -      commit_list_insert_by_date(one, &list);
 -      if (!n)
 -              return list;
 +      if (!n) {
 +              commit_list_append(one, &result);
 +              return result;
 +      }
 +      prio_queue_put(&queue, one);
 +
        for (i = 0; i < n; i++) {
                twos[i]->object.flags |= PARENT2;
 -              commit_list_insert_by_date(twos[i], &list);
 +              prio_queue_put(&queue, twos[i]);
        }
  
 -      while (interesting(list)) {
 -              struct commit *commit;
 +      while (queue_has_nonstale(&queue)) {
 +              struct commit *commit = prio_queue_get(&queue);
                struct commit_list *parents;
 -              struct commit_list *next;
                int flags;
  
 -              commit = list->item;
 -              next = list->next;
 -              free(list);
 -              list = next;
 -
                flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
                if (flags == (PARENT1 | PARENT2)) {
                        if (!(commit->object.flags & RESULT)) {
                        if (parse_commit(p))
                                return NULL;
                        p->object.flags |= flags;
 -                      commit_list_insert_by_date(p, &list);
 +                      prio_queue_put(&queue, p);
                }
        }
  
 -      free_commit_list(list);
 +      clear_prio_queue(&queue);
        return result;
  }
  
@@@ -872,7 -895,7 +884,7 @@@ struct commit_list *get_octopus_merge_b
  
                for (j = ret; j; j = j->next) {
                        struct commit_list *bases;
 -                      bases = get_merge_bases(i->item, j->item, 1);
 +                      bases = get_merge_bases(i->item, j->item);
                        if (!new)
                                new = bases;
                        else
@@@ -941,10 -964,10 +953,10 @@@ static int remove_redundant(struct comm
        return filled;
  }
  
 -struct commit_list *get_merge_bases_many(struct commit *one,
 -                                       int n,
 -                                       struct commit **twos,
 -                                       int cleanup)
 +static struct commit_list *get_merge_bases_many_0(struct commit *one,
 +                                                int n,
 +                                                struct commit **twos,
 +                                                int cleanup)
  {
        struct commit_list *list;
        struct commit **rslt;
        }
  
        /* There are more than one */
 -      cnt = 0;
 -      list = result;
 -      while (list) {
 -              list = list->next;
 -              cnt++;
 -      }
 +      cnt = commit_list_count(result);
        rslt = xcalloc(cnt, sizeof(*rslt));
        for (list = result, i = 0; list; list = list->next)
                rslt[i++] = list->item;
        return result;
  }
  
 -struct commit_list *get_merge_bases(struct commit *one, struct commit *two,
 -                                  int cleanup)
 +struct commit_list *get_merge_bases_many(struct commit *one,
 +                                       int n,
 +                                       struct commit **twos)
 +{
 +      return get_merge_bases_many_0(one, n, twos, 1);
 +}
 +
 +struct commit_list *get_merge_bases_many_dirty(struct commit *one,
 +                                             int n,
 +                                             struct commit **twos)
  {
 -      return get_merge_bases_many(one, 1, &two, cleanup);
 +      return get_merge_bases_many_0(one, n, twos, 0);
 +}
 +
 +struct commit_list *get_merge_bases(struct commit *one, struct commit *two)
 +{
 +      return get_merge_bases_many_0(one, 1, &two, 1);
  }
  
  /*
@@@ -1158,40 -1173,6 +1170,40 @@@ int parse_signed_commit(const struct co
        return saw_signature;
  }
  
 +int remove_signature(struct strbuf *buf)
 +{
 +      const char *line = buf->buf;
 +      const char *tail = buf->buf + buf->len;
 +      int in_signature = 0;
 +      const char *sig_start = NULL;
 +      const char *sig_end = NULL;
 +
 +      while (line < tail) {
 +              const char *next = memchr(line, '\n', tail - line);
 +              next = next ? next + 1 : tail;
 +
 +              if (in_signature && line[0] == ' ')
 +                      sig_end = next;
 +              else if (starts_with(line, gpg_sig_header) &&
 +                       line[gpg_sig_header_len] == ' ') {
 +                      sig_start = line;
 +                      sig_end = next;
 +                      in_signature = 1;
 +              } else {
 +                      if (*line == '\n')
 +                              /* dump the whole remainder of the buffer */
 +                              next = tail;
 +                      in_signature = 0;
 +              }
 +              line = next;
 +      }
 +
 +      if (sig_start)
 +              strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start);
 +
 +      return sig_start != NULL;
 +}
 +
  static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
  {
        struct merge_remote_desc *desc;
@@@ -1232,24 -1213,69 +1244,24 @@@ free_return
        free(buf);
  }
  
 -static struct {
 -      char result;
 -      const char *check;
 -} sigcheck_gpg_status[] = {
 -      { 'G', "\n[GNUPG:] GOODSIG " },
 -      { 'B', "\n[GNUPG:] BADSIG " },
 -      { 'U', "\n[GNUPG:] TRUST_NEVER" },
 -      { 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
 -};
 -
 -static void parse_gpg_output(struct signature_check *sigc)
 -{
 -      const char *buf = sigc->gpg_status;
 -      int i;
 -
 -      /* Iterate over all search strings */
 -      for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
 -              const char *found, *next;
 -
 -              found = skip_prefix(buf, sigcheck_gpg_status[i].check + 1);
 -              if (!found) {
 -                      found = strstr(buf, sigcheck_gpg_status[i].check);
 -                      if (!found)
 -                              continue;
 -                      found += strlen(sigcheck_gpg_status[i].check);
 -              }
 -              sigc->result = sigcheck_gpg_status[i].result;
 -              /* The trust messages are not followed by key/signer information */
 -              if (sigc->result != 'U') {
 -                      sigc->key = xmemdupz(found, 16);
 -                      found += 17;
 -                      next = strchrnul(found, '\n');
 -                      sigc->signer = xmemdupz(found, next - found);
 -              }
 -      }
 -}
 -
 -void check_commit_signature(const struct commit* commit, struct signature_check *sigc)
 +int check_commit_signature(const struct commit *commit, struct signature_check *sigc)
  {
        struct strbuf payload = STRBUF_INIT;
        struct strbuf signature = STRBUF_INIT;
 -      struct strbuf gpg_output = STRBUF_INIT;
 -      struct strbuf gpg_status = STRBUF_INIT;
 -      int status;
 +      int ret = 1;
  
        sigc->result = 'N';
  
        if (parse_signed_commit(commit, &payload, &signature) <= 0)
                goto out;
 -      status = verify_signed_buffer(payload.buf, payload.len,
 -                                    signature.buf, signature.len,
 -                                    &gpg_output, &gpg_status);
 -      if (status && !gpg_output.len)
 -              goto out;
 -      sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
 -      sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
 -      parse_gpg_output(sigc);
 +      ret = check_signature(payload.buf, payload.len, signature.buf,
 +              signature.len, sigc);
  
   out:
 -      strbuf_release(&gpg_status);
 -      strbuf_release(&gpg_output);
        strbuf_release(&payload);
        strbuf_release(&signature);
 +
 +      return ret;
  }
  
  
@@@ -1285,19 -1311,6 +1297,19 @@@ struct commit_extra_header *read_commit
        return extra;
  }
  
 +void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data)
 +{
 +      struct commit_extra_header *extra, *to_free;
 +
 +      to_free = read_commit_extra_headers(commit, NULL);
 +      for (extra = to_free; extra; extra = extra->next) {
 +              if (strcmp(extra->key, "mergetag"))
 +                      continue; /* not a merge tag */
 +              fn(commit, extra, data);
 +      }
 +      free_commit_extra_headers(to_free);
 +}
 +
  static inline int standard_header_field(const char *field, size_t len)
  {
        return ((len == 4 && !memcmp(field, "tree ", 5)) ||
@@@ -1576,10 -1589,10 +1588,10 @@@ struct commit *get_merge_parent(const c
  {
        struct object *obj;
        struct commit *commit;
 -      unsigned char sha1[20];
 -      if (get_sha1(name, sha1))
 +      struct object_id oid;
 +      if (get_sha1(name, oid.hash))
                return NULL;
 -      obj = parse_object(sha1);
 +      obj = parse_object(oid.hash);
        commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
        if (commit && !commit->util) {
                struct merge_remote_desc *desc;
@@@ -1627,71 -1640,3 +1639,71 @@@ void print_commit_list(struct commit_li
                printf(format, sha1_to_hex(list->item->object.sha1));
        }
  }
 +
 +const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
 +{
 +      int key_len = strlen(key);
 +      const char *line = msg;
 +
 +      while (line) {
 +              const char *eol = strchrnul(line, '\n');
 +
 +              if (line == eol)
 +                      return NULL;
 +
 +              if (eol - line > key_len &&
 +                  !strncmp(line, key, key_len) &&
 +                  line[key_len] == ' ') {
 +                      *out_len = eol - line - key_len - 1;
 +                      return line + key_len + 1;
 +              }
 +              line = *eol ? eol + 1 : NULL;
 +      }
 +      return NULL;
 +}
 +
 +/*
 + * Inspect sb and determine the true "end" of the log message, in
 + * order to find where to put a new Signed-off-by: line.  Ignored are
 + * trailing comment lines and blank lines, and also the traditional
 + * "Conflicts:" block that is not commented out, so that we can use
 + * "git commit -s --amend" on an existing commit that forgot to remove
 + * it.
 + *
 + * Returns the number of bytes from the tail to ignore, to be fed as
 + * the second parameter to append_signoff().
 + */
 +int ignore_non_trailer(struct strbuf *sb)
 +{
 +      int boc = 0;
 +      int bol = 0;
 +      int in_old_conflicts_block = 0;
 +
 +      while (bol < sb->len) {
 +              char *next_line;
 +
 +              if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol)))
 +                      next_line = sb->buf + sb->len;
 +              else
 +                      next_line++;
 +
 +              if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') {
 +                      /* is this the first of the run of comments? */
 +                      if (!boc)
 +                              boc = bol;
 +                      /* otherwise, it is just continuing */
 +              } else if (starts_with(sb->buf + bol, "Conflicts:\n")) {
 +                      in_old_conflicts_block = 1;
 +                      if (!boc)
 +                              boc = bol;
 +              } else if (in_old_conflicts_block && sb->buf[bol] == '\t') {
 +                      ; /* a pathname in the conflicts block */
 +              } else if (boc) {
 +                      /* the previous was not trailing comment */
 +                      boc = 0;
 +                      in_old_conflicts_block = 0;
 +              }
 +              bol = next_line - sb->buf;
 +      }
 +      return boc ? sb->len - boc : 0;
 +}