Merge branch 'jk/verify-sig-merge-into-void'
authorJunio C Hamano <gitster@pobox.com>
Sun, 18 Nov 2018 09:23:54 +0000 (18:23 +0900)
committerJunio C Hamano <gitster@pobox.com>
Sun, 18 Nov 2018 09:23:54 +0000 (18:23 +0900)
"git merge" and "git pull" that merges into an unborn branch used
to completely ignore "--verify-signatures", which has been
corrected.

* jk/verify-sig-merge-into-void:
pull: handle --verify-signatures for unborn branch
merge: handle --verify-signatures for unborn branch
merge: extract verify_merge_signature() helper

1  2 
builtin/merge.c
builtin/pull.c
commit.c
commit.h
diff --combined builtin/merge.c
index adb0402e847a50bc9e9be69e2257b1b555a780ec,05ea0bbe01641b39eca08955b0f35131d5434c16..c3c976d471ce78268e673a5135a4c37ebc6d0f6d
@@@ -36,7 -36,6 +36,7 @@@
  #include "packfile.h"
  #include "tag.h"
  #include "alias.h"
 +#include "commit-reach.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -224,7 -223,6 +224,7 @@@ static int option_parse_x(const struct 
  static int option_parse_n(const struct option *opt,
                          const char *arg, int unset)
  {
 +      BUG_ON_OPT_ARG(arg);
        show_diffstat = unset;
        return 0;
  }
@@@ -391,7 -389,7 +391,7 @@@ static void squash_message(struct commi
  
        printf(_("Squash commit -- not updating HEAD\n"));
  
 -      init_revisions(&rev, NULL);
 +      repo_init_revisions(the_repository, &rev, NULL);
        rev.ignore_merges = 1;
        rev.commit_format = CMIT_FMT_MEDIUM;
  
@@@ -454,7 -452,7 +454,7 @@@ static void finish(struct commit *head_
        }
        if (new_head && show_diffstat) {
                struct diff_options opts;
 -              diff_setup(&opts);
 +              repo_diff_setup(the_repository, &opts);
                opts.stat_width = -1; /* use full terminal width */
                opts.stat_graph_width = -1; /* respect statGraphWidth config */
                opts.output_format |=
@@@ -730,9 -728,8 +730,9 @@@ static int try_merge_strategy(const cha
                        die(_("unable to write %s"), get_index_file());
                return clean ? 0 : 1;
        } else {
 -              return try_merge_command(strategy, xopts_nr, xopts,
 -                                              common, head_arg, remoteheads);
 +              return try_merge_command(the_repository,
 +                                       strategy, xopts_nr, xopts,
 +                                       common, head_arg, remoteheads);
        }
  }
  
@@@ -901,7 -898,7 +901,7 @@@ static int suggest_conflicts(void
        fputs(msgbuf.buf, fp);
        strbuf_release(&msgbuf);
        fclose(fp);
 -      rerere(allow_rerere_auto);
 +      repo_rerere(the_repository, allow_rerere_auto);
        printf(_("Automatic merge failed; "
                        "fix conflicts and then commit the result.\n"));
        return 1;
@@@ -913,7 -910,7 +913,7 @@@ static int evaluate_result(void
        struct rev_info rev;
  
        /* Check how many files differ. */
 -      init_revisions(&rev, "");
 +      repo_init_revisions(the_repository, &rev, "");
        setup_revisions(0, NULL, &rev, NULL);
        rev.diffopt.output_format |=
                DIFF_FORMAT_CALLBACK;
@@@ -1192,7 -1189,7 +1192,7 @@@ static int merging_a_throwaway_tag(stru
        tag_ref = xstrfmt("refs/tags/%s",
                          ((struct tag *)merge_remote_util(commit)->obj)->tag);
        if (!read_ref(tag_ref, &oid) &&
 -          !oidcmp(&oid, &merge_remote_util(commit)->obj->oid))
 +          oideq(&oid, &merge_remote_util(commit)->obj->oid))
                is_throwaway_tag = 0;
        else
                is_throwaway_tag = 1;
@@@ -1337,6 -1334,10 +1337,10 @@@ int cmd_merge(int argc, const char **ar
                        die(_("%s - not something we can merge"), argv[0]);
                if (remoteheads->next)
                        die(_("Can merge only exactly one commit into empty head"));
+               if (verify_signatures)
+                       verify_merge_signature(remoteheads->item, verbosity);
                remote_head_oid = &remoteheads->item->object.oid;
                read_empty(remote_head_oid, 0);
                update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
  
        if (verify_signatures) {
                for (p = remoteheads; p; p = p->next) {
-                       struct commit *commit = p->item;
-                       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);
+                       verify_merge_signature(p->item, verbosity);
                }
        }
  
                goto done;
        } else if (fast_forward != FF_NO && !remoteheads->next &&
                        !common->next &&
 -                      !oidcmp(&common->item->object.oid, &head_commit->object.oid)) {
 +                      oideq(&common->item->object.oid, &head_commit->object.oid)) {
                /* Again the most common case of merging one remote. */
                struct strbuf msg = STRBUF_INIT;
                struct commit *commit;
                        goto done;
                }
  
 -              if (checkout_fast_forward(&head_commit->object.oid,
 +              if (checkout_fast_forward(the_repository,
 +                                        &head_commit->object.oid,
                                          &commit->object.oid,
                                          overwrite_ignore)) {
                        ret = 1;
                         * HEAD^^" would be missed.
                         */
                        common_one = get_merge_bases(head_commit, j->item);
 -                      if (oidcmp(&common_one->item->object.oid, &j->item->object.oid)) {
 +                      if (!oideq(&common_one->item->object.oid, &j->item->object.oid)) {
                                up_to_date = 0;
                                break;
                        }
diff --combined builtin/pull.c
index c21aa276f1e93ef2593397d5304e72d59e6914b0,96c795f581aab8724d8c95ab847ecdcdd528684b..1b90622b1311187612ef8b33995c92a6802eb343
@@@ -22,7 -22,6 +22,7 @@@
  #include "tempfile.h"
  #include "lockfile.h"
  #include "wt-status.h"
 +#include "commit-reach.h"
  
  enum rebase_type {
        REBASE_INVALID = -1,
@@@ -557,15 -556,24 +557,26 @@@ static int run_fetch(const char *repo, 
  static int pull_into_void(const struct object_id *merge_head,
                const struct object_id *curr_head)
  {
+       if (opt_verify_signatures) {
+               struct commit *commit;
+               commit = lookup_commit(the_repository, merge_head);
+               if (!commit)
+                       die(_("unable to access commit %s"),
+                           oid_to_hex(merge_head));
+               verify_merge_signature(commit, opt_verbosity);
+       }
        /*
         * Two-way merge: we treat the index as based on an empty tree,
         * and try to fast-forward to HEAD. This ensures we will not lose
         * index/worktree changes that the user already made on the unborn
         * branch.
         */
 -      if (checkout_fast_forward(the_hash_algo->empty_tree, merge_head, 0))
 +      if (checkout_fast_forward(the_repository,
 +                                the_hash_algo->empty_tree,
 +                                merge_head, 0))
                return 1;
  
        if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
@@@ -802,7 -810,7 +813,7 @@@ static int run_rebase(const struct obje
        struct argv_array args = ARGV_ARRAY_INIT;
  
        if (!get_octopus_merge_base(&oct_merge_base, curr_head, merge_head, fork_point))
 -              if (!is_null_oid(fork_point) && !oidcmp(&oct_merge_base, fork_point))
 +              if (!is_null_oid(fork_point) && oideq(&oct_merge_base, fork_point))
                        fork_point = NULL;
  
        argv_array_push(&args, "rebase");
@@@ -905,7 -913,7 +916,7 @@@ int cmd_pull(int argc, const char **arg
                oidclr(&curr_head);
  
        if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) &&
 -                      oidcmp(&orig_head, &curr_head)) {
 +                      !oideq(&orig_head, &curr_head)) {
                /*
                 * The fetch involved updating the current branch.
                 *
                        "fast-forwarding your working tree from\n"
                        "commit %s."), oid_to_hex(&orig_head));
  
 -              if (checkout_fast_forward(&orig_head, &curr_head, 0))
 +              if (checkout_fast_forward(the_repository, &orig_head,
 +                                        &curr_head, 0))
                        die(_("Cannot fast-forward your working tree.\n"
                                "After making sure that you saved anything precious from\n"
                                "$ git diff %s\n"
                int ret = 0;
                if ((recurse_submodules == RECURSE_SUBMODULES_ON ||
                     recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) &&
 -                  submodule_touches_in_range(&rebase_fork_point, &curr_head))
 +                  submodule_touches_in_range(the_repository, &rebase_fork_point, &curr_head))
                        die(_("cannot rebase with locally recorded submodule modifications"));
                if (!autostash) {
                        struct commit_list *list = NULL;
diff --combined commit.c
index bee7b7b62ef9ce61393c8657203fef71e5822225,9abb9971a8a0ec7ae68c1aee8aa066638b66b19f..d13a7bc3746406bdaf0bda0975c25b40111afe47
+++ b/commit.c
@@@ -17,8 -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 **);
  
@@@ -48,7 -46,7 +48,7 @@@ struct commit *lookup_commit_or_die(con
        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));
        }
@@@ -211,7 -209,7 +211,7 @@@ static int read_graft_file(struct repos
        return 0;
  }
  
 -static void prepare_commit_graft(struct repository *r)
 +void prepare_commit_graft(struct repository *r)
  {
        char *graft_file;
  
@@@ -657,10 -655,11 +657,10 @@@ struct commit *pop_commit(struct commit
  /* 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;
@@@ -685,8 -684,8 +685,8 @@@ fail_exit
        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;
@@@ -844,86 -843,367 +844,86 @@@ void sort_in_topological_order(struct c
                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);
 -      }
 +      if (!bases || bases->next)
 +              goto cleanup_return;
  
 -      /* 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;
 -      }
 +      /* 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;
  
 -      /* 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);
 -}
 +      ret = bases->item;
  
 -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 (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;
 -      }
 -
 -      if (commit->generation > min_generation)
 -              return ret;
 -
 -      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;
  
@@@ -1099,7 -1379,33 +1099,33 @@@ int check_commit_signature(const struc
        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)
@@@ -1507,10 -1813,10 +1533,10 @@@ const char *find_commit_header(const ch
   * Returns the number of bytes from the tail to ignore, to be fed as
   * the second parameter to append_signoff().
   */
 -int ignore_non_trailer(const char *buf, size_t len)
 +size_t ignore_non_trailer(const char *buf, size_t len)
  {
 -      int boc = 0;
 -      int bol = 0;
 +      size_t boc = 0;
 +      size_t bol = 0;
        int in_old_conflicts_block = 0;
        size_t cutoff = wt_status_locate_end(buf, len);
  
diff --combined commit.h
index 03ab19508f222a4a9cdfc12e9850eb9e07787230,8e3aeb11142837d9a6a94e639353fd0773a89aaa..98664536cb82c65f04a1742c3e5c1e269afc9493
+++ b/commit.h
@@@ -8,7 -8,6 +8,7 @@@
  #include "gpg-interface.h"
  #include "string-list.h"
  #include "pretty.h"
 +#include "commit-slab.h"
  
  #define COMMIT_NOT_FROM_GRAPH 0xFFFFFFFF
  #define GENERATION_NUMBER_INFINITY 0xFFFFFFFF
@@@ -203,10 -202,14 +203,10 @@@ typedef int (*each_commit_graft_fn)(con
  
  struct commit_graft *read_graft_line(struct strbuf *line);
  int register_commit_graft(struct repository *r, struct commit_graft *, int);
 +void prepare_commit_graft(struct repository *r);
  struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid);
  
 -extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2);
 -extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos);
 -extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
 -
 -/* To be used only when object flags after this call no longer matter */
 -extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct commit **twos);
 +struct commit *get_fork_point(const char *refname, struct commit *commit);
  
  /* largest positive number a signed 32-bit integer can contain */
  #define INFINITE_DEPTH 0x7fffffff
@@@ -252,15 -255,35 +252,15 @@@ extern void assign_shallow_commits_to_r
                                           uint32_t **used,
                                           int *ref_status);
  extern int delayed_reachability_test(struct shallow_info *si, int c);
 -extern void prune_shallow(int show_only);
 +#define PRUNE_SHOW_ONLY 1
 +#define PRUNE_QUICK 2
 +extern void prune_shallow(unsigned options);
  extern struct trace_key trace_shallow;
  
 -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,
                               const struct pathspec *pathspec);
  
 -/*
 - * Takes a list of commits and returns a new list where those
 - * have been removed that can be reached from other commits in
 - * the list. It is useful for, e.g., reducing the commits
 - * randomly thrown at the git-merge command and removing
 - * redundant commits that the user shouldn't have given to it.
 - *
 - * This function destroys the STALE bit of the commit objects'
 - * flags.
 - */
 -extern struct commit_list *reduce_heads(struct commit_list *heads);
 -
 -/*
 - * Like `reduce_heads()`, except it replaces the list. Use this
 - * instead of `foo = reduce_heads(foo);` to avoid memory leaks.
 - */
 -extern void reduce_heads_replace(struct commit_list **heads);
 -
  struct commit_extra_header {
        struct commit_extra_header *next;
        char *key;
@@@ -299,7 -322,7 +299,7 @@@ extern const char *find_commit_header(c
                                      size_t *out_len);
  
  /* Find the end of the log message, the right place for a new trailer. */
 -extern int ignore_non_trailer(const char *buf, size_t len);
 +extern size_t ignore_non_trailer(const char *buf, size_t len);
  
  typedef int (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
                                 void *cb_data);
@@@ -334,12 -357,13 +334,20 @@@ extern int remove_signature(struct strb
   */
  extern int check_commit_signature(const struct commit *commit, struct signature_check *sigc);
  
 +/* record author-date for each commit object */
 +struct author_date_slab;
 +void record_author_date(struct author_date_slab *author_date,
 +                      struct commit *commit);
 +
 +int compare_commits_by_author_date(const void *a_, const void *b_, void *unused);
++
+ /*
+  * Verify a single commit with check_commit_signature() and die() if it is not
+  * a good signature. This isn't really suitable for general use, but is a
+  * helper to implement consistent logic for pull/merge --verify-signatures.
+  */
+ void verify_merge_signature(struct commit *commit, int verbose);
  int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
  int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused);