Merge branch 'bw/mailmap'
[gitweb.git] / commit.c
index fb4c99cb8908b53c2bbba43ef08de0e2e217533e..d13a7bc3746406bdaf0bda0975c25b40111afe47 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -17,6 +17,8 @@
 #include "sha1-lookup.h"
 #include "wt-status.h"
 #include "advice.h"
+#include "refs.h"
+#include "commit-reach.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
@@ -46,7 +48,7 @@ struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref
        struct commit *c = lookup_commit_reference(the_repository, oid);
        if (!c)
                die(_("could not parse %s"), ref_name);
-       if (oidcmp(oid, &c->object.oid)) {
+       if (!oideq(oid, &c->object.oid)) {
                warning(_("%s %s is not a commit!"),
                        ref_name, oid_to_hex(oid));
        }
@@ -655,11 +657,10 @@ struct commit *pop_commit(struct commit_list **stack)
 /* count number of children that have not been emitted */
 define_commit_slab(indegree_slab, int);
 
-/* record author-date for each commit object */
 define_commit_slab(author_date_slab, timestamp_t);
 
-static void record_author_date(struct author_date_slab *author_date,
-                              struct commit *commit)
+void record_author_date(struct author_date_slab *author_date,
+                       struct commit *commit)
 {
        const char *buffer = get_commit_buffer(commit, NULL);
        struct ident_split ident;
@@ -684,8 +685,8 @@ static void record_author_date(struct author_date_slab *author_date,
        unuse_commit_buffer(commit, buffer);
 }
 
-static int compare_commits_by_author_date(const void *a_, const void *b_,
-                                         void *cb_data)
+int compare_commits_by_author_date(const void *a_, const void *b_,
+                                  void *cb_data)
 {
        const struct commit *a = a_, *b = b_;
        struct author_date_slab *author_date = cb_data;
@@ -843,367 +844,86 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so
                clear_author_date_slab(&author_date);
 }
 
-/* merge-base stuff */
-
-/* Remember to update object flag allocation in object.h */
-#define PARENT1                (1u<<16)
-#define PARENT2                (1u<<17)
-#define STALE          (1u<<18)
-#define RESULT         (1u<<19)
-
-static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
-
-static int queue_has_nonstale(struct prio_queue *queue)
-{
-       int i;
-       for (i = 0; i < queue->nr; i++) {
-               struct commit *commit = queue->array[i].data;
-               if (!(commit->object.flags & STALE))
-                       return 1;
-       }
-       return 0;
-}
+struct rev_collect {
+       struct commit **commit;
+       int nr;
+       int alloc;
+       unsigned int initial : 1;
+};
 
-/* 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,
-                                               int min_generation)
+static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
 {
-       struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
-       struct commit_list *result = NULL;
-       int i;
-       uint32_t last_gen = GENERATION_NUMBER_INFINITY;
-
-       if (!min_generation)
-               queue.compare = compare_commits_by_commit_date;
-
-       one->object.flags |= PARENT1;
-       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;
-               prio_queue_put(&queue, twos[i]);
-       }
-
-       while (queue_has_nonstale(&queue)) {
-               struct commit *commit = prio_queue_get(&queue);
-               struct commit_list *parents;
-               int flags;
-
-               if (min_generation && commit->generation > last_gen)
-                       BUG("bad generation skip %8x > %8x at %s",
-                           commit->generation, last_gen,
-                           oid_to_hex(&commit->object.oid));
-               last_gen = commit->generation;
+       struct commit *commit;
 
-               if (commit->generation < min_generation)
-                       break;
+       if (is_null_oid(oid))
+               return;
 
-               flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
-               if (flags == (PARENT1 | PARENT2)) {
-                       if (!(commit->object.flags & RESULT)) {
-                               commit->object.flags |= RESULT;
-                               commit_list_insert_by_date(commit, &result);
-                       }
-                       /* Mark parents of a found merge stale */
-                       flags |= STALE;
-               }
-               parents = commit->parents;
-               while (parents) {
-                       struct commit *p = parents->item;
-                       parents = parents->next;
-                       if ((p->object.flags & flags) == flags)
-                               continue;
-                       if (parse_commit(p))
-                               return NULL;
-                       p->object.flags |= flags;
-                       prio_queue_put(&queue, p);
-               }
-       }
+       commit = lookup_commit(the_repository, oid);
+       if (!commit ||
+           (commit->object.flags & TMP_MARK) ||
+           parse_commit(commit))
+               return;
 
-       clear_prio_queue(&queue);
-       return result;
+       ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
+       revs->commit[revs->nr++] = commit;
+       commit->object.flags |= TMP_MARK;
 }
 
-static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
+static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
+                                 const char *ident, timestamp_t timestamp,
+                                 int tz, const char *message, void *cbdata)
 {
-       struct commit_list *list = NULL;
-       struct commit_list *result = NULL;
-       int i;
-
-       for (i = 0; i < n; i++) {
-               if (one == twos[i])
-                       /*
-                        * We do not mark this even with RESULT so we do not
-                        * have to clean it up.
-                        */
-                       return commit_list_insert(one, &result);
-       }
-
-       if (parse_commit(one))
-               return NULL;
-       for (i = 0; i < n; i++) {
-               if (parse_commit(twos[i]))
-                       return NULL;
-       }
-
-       list = paint_down_to_common(one, n, twos, 0);
+       struct rev_collect *revs = cbdata;
 
-       while (list) {
-               struct commit *commit = pop_commit(&list);
-               if (!(commit->object.flags & STALE))
-                       commit_list_insert_by_date(commit, &result);
+       if (revs->initial) {
+               revs->initial = 0;
+               add_one_commit(ooid, revs);
        }
-       return result;
+       add_one_commit(noid, revs);
+       return 0;
 }
 
-struct commit_list *get_octopus_merge_bases(struct commit_list *in)
+struct commit *get_fork_point(const char *refname, struct commit *commit)
 {
-       struct commit_list *i, *j, *k, *ret = NULL;
+       struct object_id oid;
+       struct rev_collect revs;
+       struct commit_list *bases;
+       int i;
+       struct commit *ret = NULL;
 
-       if (!in)
-               return ret;
+       memset(&revs, 0, sizeof(revs));
+       revs.initial = 1;
+       for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
 
-       commit_list_insert(in->item, &ret);
+       if (!revs.nr && !get_oid(refname, &oid))
+               add_one_commit(&oid, &revs);
 
-       for (i = in->next; i; i = i->next) {
-               struct commit_list *new_commits = NULL, *end = NULL;
+       for (i = 0; i < revs.nr; i++)
+               revs.commit[i]->object.flags &= ~TMP_MARK;
 
-               for (j = ret; j; j = j->next) {
-                       struct commit_list *bases;
-                       bases = get_merge_bases(i->item, j->item);
-                       if (!new_commits)
-                               new_commits = bases;
-                       else
-                               end->next = bases;
-                       for (k = bases; k; k = k->next)
-                               end = k;
-               }
-               ret = new_commits;
-       }
-       return ret;
-}
+       bases = get_merge_bases_many(commit, revs.nr, revs.commit);
 
-static int remove_redundant(struct commit **array, int cnt)
-{
        /*
-        * Some commit in the array may be an ancestor of
-        * another commit.  Move such commit to the end of
-        * the array, and return the number of commits that
-        * are independent from each other.
+        * There should be one and only one merge base, when we found
+        * a common ancestor among reflog entries.
         */
-       struct commit **work;
-       unsigned char *redundant;
-       int *filled_index;
-       int i, j, filled;
-
-       work = xcalloc(cnt, sizeof(*work));
-       redundant = xcalloc(cnt, 1);
-       ALLOC_ARRAY(filled_index, cnt - 1);
-
-       for (i = 0; i < cnt; i++)
-               parse_commit(array[i]);
-       for (i = 0; i < cnt; i++) {
-               struct commit_list *common;
-               uint32_t min_generation = array[i]->generation;
-
-               if (redundant[i])
-                       continue;
-               for (j = filled = 0; j < cnt; j++) {
-                       if (i == j || redundant[j])
-                               continue;
-                       filled_index[filled] = j;
-                       work[filled++] = array[j];
-
-                       if (array[j]->generation < min_generation)
-                               min_generation = array[j]->generation;
-               }
-               common = paint_down_to_common(array[i], filled, work,
-                                             min_generation);
-               if (array[i]->object.flags & PARENT2)
-                       redundant[i] = 1;
-               for (j = 0; j < filled; j++)
-                       if (work[j]->object.flags & PARENT1)
-                               redundant[filled_index[j]] = 1;
-               clear_commit_marks(array[i], all_flags);
-               clear_commit_marks_many(filled, work, all_flags);
-               free_commit_list(common);
-       }
-
-       /* Now collect the result */
-       COPY_ARRAY(work, array, cnt);
-       for (i = filled = 0; i < cnt; i++)
-               if (!redundant[i])
-                       array[filled++] = work[i];
-       for (j = filled, i = 0; i < cnt; i++)
-               if (redundant[i])
-                       array[j++] = work[i];
-       free(work);
-       free(redundant);
-       free(filled_index);
-       return filled;
-}
-
-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;
-       struct commit_list *result;
-       int cnt, i;
-
-       result = merge_bases_many(one, n, twos);
-       for (i = 0; i < n; i++) {
-               if (one == twos[i])
-                       return result;
-       }
-       if (!result || !result->next) {
-               if (cleanup) {
-                       clear_commit_marks(one, all_flags);
-                       clear_commit_marks_many(n, twos, all_flags);
-               }
-               return result;
-       }
-
-       /* There are more than one */
-       cnt = commit_list_count(result);
-       rslt = xcalloc(cnt, sizeof(*rslt));
-       for (list = result, i = 0; list; list = list->next)
-               rslt[i++] = list->item;
-       free_commit_list(result);
-
-       clear_commit_marks(one, all_flags);
-       clear_commit_marks_many(n, twos, all_flags);
-
-       cnt = remove_redundant(rslt, cnt);
-       result = NULL;
-       for (i = 0; i < cnt; i++)
-               commit_list_insert_by_date(rslt[i], &result);
-       free(rslt);
-       return result;
-}
-
-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_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);
-}
-
-/*
- * Is "commit" a descendant of one of the elements on the "with_commit" list?
- */
-int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
-{
-       if (!with_commit)
-               return 1;
-       while (with_commit) {
-               struct commit *other;
-
-               other = with_commit->item;
-               with_commit = with_commit->next;
-               if (in_merge_bases(other, commit))
-                       return 1;
-       }
-       return 0;
-}
-
-/*
- * Is "commit" an ancestor of one of the "references"?
- */
-int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference)
-{
-       struct commit_list *bases;
-       int ret = 0, i;
-       uint32_t min_generation = GENERATION_NUMBER_INFINITY;
+       if (!bases || bases->next)
+               goto cleanup_return;
 
-       if (parse_commit(commit))
-               return ret;
-       for (i = 0; i < nr_reference; i++) {
-               if (parse_commit(reference[i]))
-                       return ret;
-               if (reference[i]->generation < min_generation)
-                       min_generation = reference[i]->generation;
-       }
+       /* And the found one must be one of the reflog entries */
+       for (i = 0; i < revs.nr; i++)
+               if (&bases->item->object == &revs.commit[i]->object)
+                       break; /* found */
+       if (revs.nr <= i)
+               goto cleanup_return;
 
-       if (commit->generation > min_generation)
-               return ret;
+       ret = bases->item;
 
-       bases = paint_down_to_common(commit, nr_reference, reference, commit->generation);
-       if (commit->object.flags & PARENT2)
-               ret = 1;
-       clear_commit_marks(commit, all_flags);
-       clear_commit_marks_many(nr_reference, reference, all_flags);
+cleanup_return:
        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;
-       struct commit_list *result = NULL, **tail = &result;
-       struct commit **array;
-       int num_head, i;
-
-       if (!heads)
-               return NULL;
-
-       /* Uniquify */
-       for (p = heads; p; p = p->next)
-               p->item->object.flags &= ~STALE;
-       for (p = heads, num_head = 0; p; p = p->next) {
-               if (p->item->object.flags & STALE)
-                       continue;
-               p->item->object.flags |= STALE;
-               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;
-                       p->item->object.flags &= ~STALE;
-               }
-       }
-       num_head = remove_redundant(array, num_head);
-       for (i = 0; i < num_head; i++)
-               tail = &commit_list_insert(array[i], tail)->next;
-       free(array);
-       return result;
-}
-
-void reduce_heads_replace(struct commit_list **heads)
-{
-       struct commit_list *result = reduce_heads(*heads);
-       free_commit_list(*heads);
-       *heads = result;
-}
-
 static const char gpg_sig_header[] = "gpgsig";
 static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
 
@@ -1379,7 +1099,33 @@ int check_commit_signature(const struct commit *commit, struct signature_check *
        return ret;
 }
 
+void verify_merge_signature(struct commit *commit, int verbosity)
+{
+       char hex[GIT_MAX_HEXSZ + 1];
+       struct signature_check signature_check;
+       memset(&signature_check, 0, sizeof(signature_check));
+
+       check_commit_signature(commit, &signature_check);
 
+       find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV);
+       switch (signature_check.result) {
+       case 'G':
+               break;
+       case 'U':
+               die(_("Commit %s has an untrusted GPG signature, "
+                     "allegedly by %s."), hex, signature_check.signer);
+       case 'B':
+               die(_("Commit %s has a bad GPG signature "
+                     "allegedly by %s."), hex, signature_check.signer);
+       default: /* 'N' */
+               die(_("Commit %s does not have a GPG signature."), hex);
+       }
+       if (verbosity >= 0 && signature_check.result == 'G')
+               printf(_("Commit %s has a good GPG signature by %s\n"),
+                      hex, signature_check.signer);
+
+       signature_check_clear(&signature_check);
+}
 
 void append_merge_tag_headers(struct commit_list *parents,
                              struct commit_extra_header ***tail)