Merge branch 'jk/commit-info-slab'
authorJunio C Hamano <gitster@pobox.com>
Mon, 1 Jul 2013 19:41:19 +0000 (12:41 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 1 Jul 2013 19:41:19 +0000 (12:41 -0700)
Allow adding custom information to commit objects in order to
represent unbound number of flag bits etc.

* jk/commit-info-slab:
commit-slab: introduce a macro to define a slab for new type
commit-slab: avoid large realloc
commit: allow associating auxiliary info on-demand

1  2 
commit.c
commit.h
diff --combined commit.c
index 888e02ae2f65ab566555465e5d88d02bbe52420f,f97456ddfa4ee338f0ff7b0a848c9bc69038b51e..a1096a2b5a2c6b04155f1efc093da6fb5059551f
+++ b/commit.c
@@@ -8,12 -8,14 +8,14 @@@
  #include "notes.h"
  #include "gpg-interface.h"
  #include "mergesort.h"
+ #include "commit-slab.h"
  
  static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
  
  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,
@@@ -58,8 -60,11 +60,11 @@@ struct commit *lookup_commit_or_die(con
  struct commit *lookup_commit(const unsigned char *sha1)
  {
        struct object *obj = lookup_object(sha1);
-       if (!obj)
-               return create_object(sha1, OBJ_COMMIT, alloc_commit_node());
+       if (!obj) {
+               struct commit *c = alloc_commit_node();
+               c->index = commit_count++;
+               return create_object(sha1, OBJ_COMMIT, c);
+       }
        if (!obj->type)
                obj->type = OBJ_COMMIT;
        return check_commit(obj, sha1, 0);
@@@ -463,23 -468,14 +468,23 @@@ static void clear_commit_marks_1(struc
        }
  }
  
 -void clear_commit_marks(struct commit *commit, unsigned int mark)
 +void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark)
  {
        struct commit_list *list = NULL;
 -      commit_list_insert(commit, &list);
 +
 +      while (nr--) {
 +              commit_list_insert(*commit, &list);
 +              commit++;
 +      }
        while (list)
                clear_commit_marks_1(&list, pop_commit(&list), mark);
  }
  
 +void clear_commit_marks(struct commit *commit, unsigned int mark)
 +{
 +      clear_commit_marks_many(1, &commit, mark);
 +}
 +
  void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark)
  {
        struct object *object;
@@@ -506,6 -502,13 +511,13 @@@ struct commit *pop_commit(struct commit
        return item;
  }
  
+ /*
+  * Topological sort support
+  */
+ /* count number of children that have not been emitted */
+ define_commit_slab(indegree_slab, int);
  /*
   * Performs an in-place topological sort on the list supplied.
   */
@@@ -514,15 -517,18 +526,18 @@@ void sort_in_topological_order(struct c
        struct commit_list *next, *orig = *list;
        struct commit_list *work, **insert;
        struct commit_list **pptr;
+       struct indegree_slab indegree;
  
        if (!orig)
                return;
        *list = NULL;
  
+       init_indegree_slab(&indegree);
        /* Mark them and clear the indegree */
        for (next = orig; next; next = next->next) {
                struct commit *commit = next->item;
-               commit->indegree = 1;
+               *(indegree_slab_at(&indegree, commit)) = 1;
        }
  
        /* update the indegree */
                struct commit_list * parents = next->item->parents;
                while (parents) {
                        struct commit *parent = parents->item;
+                       int *pi = indegree_slab_at(&indegree, parent);
  
-                       if (parent->indegree)
-                               parent->indegree++;
+                       if (*pi)
+                               (*pi)++;
                        parents = parents->next;
                }
        }
        for (next = orig; next; next = next->next) {
                struct commit *commit = next->item;
  
-               if (commit->indegree == 1)
+               if (*(indegree_slab_at(&indegree, commit)) == 1)
                        insert = &commit_list_insert(commit, insert)->next;
        }
  
                commit = work_item->item;
                for (parents = commit->parents; parents ; parents = parents->next) {
                        struct commit *parent = parents->item;
+                       int *pi = indegree_slab_at(&indegree, parent);
  
-                       if (!parent->indegree)
+                       if (!*pi)
                                continue;
  
                        /*
                         * when all their children have been emitted thereby
                         * guaranteeing topological order.
                         */
-                       if (--parent->indegree == 1) {
+                       if (--(*pi) == 1) {
                                if (!lifo)
                                        commit_list_insert_by_date(parent, &work);
                                else
                 * work_item is a commit all of whose children
                 * have already been emitted. we can emit it now.
                 */
-               commit->indegree = 0;
+               *(indegree_slab_at(&indegree, commit)) = 0;
                *pptr = work_item;
                pptr = &work_item->next;
        }
+       clear_indegree_slab(&indegree);
  }
  
  /* merge-base stuff */
@@@ -806,7 -816,8 +825,7 @@@ struct commit_list *get_merge_bases_man
        if (!result || !result->next) {
                if (cleanup) {
                        clear_commit_marks(one, all_flags);
 -                      for (i = 0; i < n; i++)
 -                              clear_commit_marks(twos[i], all_flags);
 +                      clear_commit_marks_many(n, twos, all_flags);
                }
                return result;
        }
        free_commit_list(result);
  
        clear_commit_marks(one, all_flags);
 -      for (i = 0; i < n; i++)
 -              clear_commit_marks(twos[i], all_flags);
 +      clear_commit_marks_many(n, twos, all_flags);
  
        cnt = remove_redundant(rslt, cnt);
        result = NULL;
@@@ -859,36 -871,25 +878,36 @@@ int is_descendant_of(struct commit *com
  }
  
  /*
 - * Is "commit" an ancestor of (i.e. reachable from) the "reference"?
 + * Is "commit" an ancestor of one of the "references"?
   */
 -int in_merge_bases(struct commit *commit, struct commit *reference)
 +int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference)
  {
        struct commit_list *bases;
 -      int ret = 0;
 +      int ret = 0, i;
  
 -      if (parse_commit(commit) || parse_commit(reference))
 +      if (parse_commit(commit))
                return ret;
 +      for (i = 0; i < nr_reference; i++)
 +              if (parse_commit(reference[i]))
 +                      return ret;
  
 -      bases = paint_down_to_common(commit, 1, &reference);
 +      bases = paint_down_to_common(commit, nr_reference, reference);
        if (commit->object.flags & PARENT2)
                ret = 1;
        clear_commit_marks(commit, all_flags);
 -      clear_commit_marks(reference, all_flags);
 +      clear_commit_marks_many(nr_reference, reference, all_flags);
        free_commit_list(bases);
        return ret;
  }
  
 +/*
 + * Is "commit" an ancestor of (i.e. reachable from) the "reference"?
 + */
 +int in_merge_bases(struct commit *commit, struct commit *reference)
 +{
 +      return in_merge_bases_many(commit, 1, &reference);
 +}
 +
  struct commit_list *reduce_heads(struct commit_list *heads)
  {
        struct commit_list *p;
@@@ -1041,76 -1042,6 +1060,76 @@@ 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;
 +
 +              if (!prefixcmp(buf, sigcheck_gpg_status[i].check + 1)) {
 +                      /* At the very beginning of the buffer */
 +                      found = buf + strlen(sigcheck_gpg_status[i].check + 1);
 +              } else {
 +                      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)
 +{
 +      struct strbuf payload = STRBUF_INIT;
 +      struct strbuf signature = STRBUF_INIT;
 +      struct strbuf gpg_output = STRBUF_INIT;
 +      struct strbuf gpg_status = STRBUF_INIT;
 +      int status;
 +
 +      sigc->result = 'N';
 +
 +      if (parse_signed_commit(commit->object.sha1,
 +                              &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);
 +
 + out:
 +      strbuf_release(&gpg_status);
 +      strbuf_release(&gpg_output);
 +      strbuf_release(&payload);
 +      strbuf_release(&signature);
 +}
 +
 +
 +
  void append_merge_tag_headers(struct commit_list *parents,
                              struct commit_extra_header ***tail)
  {
diff --combined commit.h
index 6e9c7cd9d5da7d24d4810b36039681f184325932,70e749d69ffec1818575b327e08dde6808ad44f1..350472114b6ddd3bad40a91c7acfb0629ece14a7
+++ b/commit.h
@@@ -5,7 -5,6 +5,7 @@@
  #include "tree.h"
  #include "strbuf.h"
  #include "decorate.h"
 +#include "gpg-interface.h"
  
  struct commit_list {
        struct commit *item;
@@@ -15,7 -14,7 +15,7 @@@
  struct commit {
        struct object object;
        void *util;
-       unsigned int indegree;
+       unsigned int index;
        unsigned long date;
        struct commit_list *parents;
        struct tree *tree;
@@@ -101,7 -100,6 +101,7 @@@ struct userformat_want 
  extern int has_non_ascii(const char *text);
  struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
  extern char *logmsg_reencode(const struct commit *commit,
 +                           char **commit_encoding,
                             const char *output_encoding);
  extern void logmsg_free(char *msg, const struct commit *commit);
  extern void get_commit_format(const char *arg, struct rev_info *);
@@@ -139,7 -137,6 +139,7 @@@ struct commit *pop_most_recent_commit(s
  struct commit *pop_commit(struct commit_list **stack);
  
  void clear_commit_marks(struct commit *commit, unsigned int mark);
 +void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark);
  void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
  
  /*
@@@ -176,12 -173,9 +176,12 @@@ extern int for_each_commit_graft(each_c
  extern int is_repository_shallow(void);
  extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
 +extern void check_shallow_file_for_update(void);
 +extern void set_alternate_shallow_file(const char *path);
  
  int is_descendant_of(struct commit *, struct commit_list *);
  int in_merge_bases(struct commit *, struct commit *);
 +int in_merge_bases_many(struct commit *, int, struct commit **);
  
  extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
  extern int run_add_interactive(const char *revision, const char *patch_mode,
@@@ -236,13 -230,4 +236,13 @@@ extern void print_commit_list(struct co
                              const char *format_cur,
                              const char *format_last);
  
 +/*
 + * Check the signature of the given commit. The result of the check is stored
 + * in sig->check_result, 'G' for a good signature, 'U' for a good signature
 + * from an untrusted signer, 'B' for a bad signature and 'N' for no signature
 + * at all.  This may allocate memory for sig->gpg_output, sig->gpg_status,
 + * sig->signer and sig->key.
 + */
 +extern void check_commit_signature(const struct commit* commit, struct signature_check *sigc);
 +
  #endif /* COMMIT_H */