Merge branch 'jc/merge-bases-paint-fix'
authorJunio C Hamano <gitster@pobox.com>
Mon, 8 Oct 2012 18:42:15 +0000 (11:42 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 8 Oct 2012 18:42:15 +0000 (11:42 -0700)
"git fmt-merge-msg" (an internal helper reduce_heads() it uses) had
a severe performance regression; an empty "git pull" took forever to
finish as the result.

* jc/merge-bases-paint-fix:
paint_down_to_common(): parse commit before relying on its timestamp

1  2 
commit.c
diff --combined commit.c
index 02467676a3e70684065ed668b53e64c1cc561e1e,47027f68ec8dfa9652cdb6d7bbac465f901b85be..213bc98c183b820c43c66d67a15133b4a401f6a4
+++ b/commit.c
@@@ -7,9 -7,6 +7,9 @@@
  #include "revision.h"
  #include "notes.h"
  #include "gpg-interface.h"
 +#include "mergesort.h"
 +
 +static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
  
  int save_commit_buffer = 1;
  
@@@ -70,7 -67,7 +70,7 @@@ struct commit *lookup_commit_reference_
        unsigned char sha1[20];
        struct commit *commit;
  
 -      if (get_sha1(name, sha1))
 +      if (get_sha1_committish(name, sha1))
                return NULL;
        commit = lookup_commit_reference(sha1);
        if (!commit || parse_commit(commit))
@@@ -393,31 -390,15 +393,31 @@@ struct commit_list * commit_list_insert
        return commit_list_insert(item, pp);
  }
  
 +static int commit_list_compare_by_date(const void *a, const void *b)
 +{
 +      unsigned long a_date = ((const struct commit_list *)a)->item->date;
 +      unsigned long b_date = ((const struct commit_list *)b)->item->date;
 +      if (a_date < b_date)
 +              return 1;
 +      if (a_date > b_date)
 +              return -1;
 +      return 0;
 +}
 +
 +static void *commit_list_get_next(const void *a)
 +{
 +      return ((const struct commit_list *)a)->next;
 +}
 +
 +static void commit_list_set_next(void *a, void *next)
 +{
 +      ((struct commit_list *)a)->next = next;
 +}
  
  void commit_list_sort_by_date(struct commit_list **list)
  {
 -      struct commit_list *ret = NULL;
 -      while (*list) {
 -              commit_list_insert_by_date((*list)->item, &ret);
 -              *list = (*list)->next;
 -      }
 -      *list = ret;
 +      *list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next,
 +                              commit_list_compare_by_date);
  }
  
  struct commit *pop_most_recent_commit(struct commit_list **list,
        return ret;
  }
  
 -void clear_commit_marks(struct commit *commit, unsigned int mark)
 +static void clear_commit_marks_1(struct commit_list **plist,
 +                               struct commit *commit, unsigned int mark)
  {
        while (commit) {
                struct commit_list *parents;
                        return;
  
                while ((parents = parents->next))
 -                      clear_commit_marks(parents->item, mark);
 +                      commit_list_insert(parents->item, plist);
  
                commit = commit->parents->item;
        }
  }
  
 +void clear_commit_marks(struct commit *commit, unsigned int mark)
 +{
 +      struct commit_list *list = NULL;
 +      commit_list_insert(commit, &list);
 +      while (list)
 +              clear_commit_marks_1(&list, pop_commit(&list), mark);
 +}
 +
  void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark)
  {
        struct object *object;
@@@ -609,6 -581,7 +609,7 @@@ static struct commit *interesting(struc
        return NULL;
  }
  
+ /* 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;
  
        one->object.flags |= PARENT1;
        commit_list_insert_by_date(one, &list);
+       if (!n)
+               return list;
        for (i = 0; i < n; i++) {
                twos[i]->object.flags |= PARENT2;
                commit_list_insert_by_date(twos[i], &list);
@@@ -737,6 -712,8 +740,8 @@@ static int remove_redundant(struct comm
        redundant = xcalloc(cnt, 1);
        filled_index = xmalloc(sizeof(*filled_index) * (cnt - 1));
  
+       for (i = 0; i < cnt; i++)
+               parse_commit(array[i]);
        for (i = 0; i < cnt; i++) {
                struct commit_list *common;
  
@@@ -1075,9 -1052,8 +1080,9 @@@ static int excluded_header_field(const 
        return 0;
  }
  
 -struct commit_extra_header *read_commit_extra_header_lines(const char *buffer, size_t size,
 -                                                         const char **exclude)
 +static struct commit_extra_header *read_commit_extra_header_lines(
 +      const char *buffer, size_t size,
 +      const char **exclude)
  {
        struct commit_extra_header *extra = NULL, **tail = &extra, *it = NULL;
        const char *line, *next, *eof, *eob;
@@@ -1144,92 -1120,8 +1149,92 @@@ int commit_tree(const struct strbuf *ms
        return result;
  }
  
 +static int find_invalid_utf8(const char *buf, int len)
 +{
 +      int offset = 0;
 +
 +      while (len) {
 +              unsigned char c = *buf++;
 +              int bytes, bad_offset;
 +
 +              len--;
 +              offset++;
 +
 +              /* Simple US-ASCII? No worries. */
 +              if (c < 0x80)
 +                      continue;
 +
 +              bad_offset = offset-1;
 +
 +              /*
 +               * Count how many more high bits set: that's how
 +               * many more bytes this sequence should have.
 +               */
 +              bytes = 0;
 +              while (c & 0x40) {
 +                      c <<= 1;
 +                      bytes++;
 +              }
 +
 +              /* Must be between 1 and 5 more bytes */
 +              if (bytes < 1 || bytes > 5)
 +                      return bad_offset;
 +
 +              /* Do we *have* that many bytes? */
 +              if (len < bytes)
 +                      return bad_offset;
 +
 +              offset += bytes;
 +              len -= bytes;
 +
 +              /* And verify that they are good continuation bytes */
 +              do {
 +                      if ((*buf++ & 0xc0) != 0x80)
 +                              return bad_offset;
 +              } while (--bytes);
 +
 +              /* We could/should check the value and length here too */
 +      }
 +      return -1;
 +}
 +
 +/*
 + * This verifies that the buffer is in proper utf8 format.
 + *
 + * If it isn't, it assumes any non-utf8 characters are Latin1,
 + * and does the conversion.
 + *
 + * Fixme: we should probably also disallow overlong forms and
 + * invalid characters. But we don't do that currently.
 + */
 +static int verify_utf8(struct strbuf *buf)
 +{
 +      int ok = 1;
 +      long pos = 0;
 +
 +      for (;;) {
 +              int bad;
 +              unsigned char c;
 +              unsigned char replace[2];
 +
 +              bad = find_invalid_utf8(buf->buf + pos, buf->len - pos);
 +              if (bad < 0)
 +                      return ok;
 +              pos += bad;
 +              ok = 0;
 +              c = buf->buf[pos];
 +              strbuf_remove(buf, pos, 1);
 +
 +              /* We know 'c' must be in the range 128-255 */
 +              replace[0] = 0xc0 + (c >> 6);
 +              replace[1] = 0x80 + (c & 0x3f);
 +              strbuf_insert(buf, pos, replace, 2);
 +              pos += 2;
 +      }
 +}
 +
  static const char commit_utf8_warn[] =
 -"Warning: commit message does not conform to UTF-8.\n"
 +"Warning: commit message did not conform to UTF-8.\n"
  "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";
  
@@@ -1270,9 -1162,9 +1275,9 @@@ int commit_tree_extended(const struct s
  
        /* Person/date information */
        if (!author)
 -              author = git_author_info(IDENT_ERROR_ON_NO_NAME);
 +              author = git_author_info(IDENT_STRICT);
        strbuf_addf(&buffer, "author %s\n", author);
 -      strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
 +      strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT));
        if (!encoding_is_utf8)
                strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
  
        strbuf_addbuf(&buffer, msg);
  
        /* And check the encoding */
 -      if (encoding_is_utf8 && !is_utf8(buffer.buf))
 +      if (encoding_is_utf8 && !verify_utf8(&buffer))
                fprintf(stderr, commit_utf8_warn);
  
        if (sign_commit && do_sign_commit(&buffer, sign_commit))
@@@ -1315,30 -1207,3 +1320,30 @@@ struct commit *get_merge_parent(const c
        }
        return commit;
  }
 +
 +/*
 + * Append a commit to the end of the commit_list.
 + *
 + * next starts by pointing to the variable that holds the head of an
 + * empty commit_list, and is updated to point to the "next" field of
 + * the last item on the list as new commits are appended.
 + *
 + * Usage example:
 + *
 + *     struct commit_list *list;
 + *     struct commit_list **next = &list;
 + *
 + *     next = commit_list_append(c1, next);
 + *     next = commit_list_append(c2, next);
 + *     assert(commit_list_count(list) == 2);
 + *     return list;
 + */
 +struct commit_list **commit_list_append(struct commit *commit,
 +                                      struct commit_list **next)
 +{
 +      struct commit_list *new = xmalloc(sizeof(struct commit_list));
 +      new->item = commit;
 +      *next = new;
 +      new->next = NULL;
 +      return &new->next;
 +}