Merge branch 'sg/gpg-sig'
authorJunio C Hamano <gitster@pobox.com>
Fri, 5 Apr 2013 21:15:16 +0000 (14:15 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 5 Apr 2013 21:15:16 +0000 (14:15 -0700)
Teach "merge/pull" to optionally verify and reject commits that are
not signed properly.

* sg/gpg-sig:
pretty printing: extend %G? to include 'N' and 'U'
merge/pull Check for untrusted good GPG signatures
merge/pull: verify GPG signatures of commits being merged
commit.c/GPG signature verification: Also look at the first GPG status line
Move commit GPG signature verification to commit.c

1  2 
Documentation/merge-options.txt
builtin/merge.c
index 34a844582846ae409e17347a65ac6cbeb28202a5,a0f022b41df07b3f2d431a9f284d63ce040a55ad..2adccf8fec71c10f8223490f1be4b2a215ace5ea
@@@ -30,8 -30,7 +30,8 @@@ set to `no` at the beginning of them
  
  --no-ff::
        Create a merge commit even when the merge resolves as a
 -      fast-forward.
 +      fast-forward.  This is the default behaviour when merging an
 +      annotated (and possibly signed) tag.
  
  --ff-only::
        Refuse to merge and exit with a non-zero status unless the
@@@ -84,6 -83,11 +84,11 @@@ option can be used to override --squash
        Pass merge strategy specific option through to the merge
        strategy.
  
+ --verify-signatures::
+ --no-verify-signatures::
+       Verify that the commits being merged have good and trusted GPG signatures
+       and abort the merge in case they do not.
  --summary::
  --no-summary::
        Synonyms to --stat and --no-stat; these are deprecated and will be
diff --combined builtin/merge.c
index 0d94d89e7409c625fd2a15f65205a45d4a6d2b89,bac11d1605c51d558dc18d7d2e67470b79a39d2d..3e2daa37c367560450217cfae5cbb717bfb508af
@@@ -49,7 -49,7 +49,7 @@@ static const char * const builtin_merge
  static int show_diffstat = 1, shortlog_len = -1, squash;
  static int option_commit = 1, allow_fast_forward = 1;
  static int fast_forward_only, option_edit = -1;
- static int allow_trivial = 1, have_message;
+ static int allow_trivial = 1, have_message, verify_signatures;
  static int overwrite_ignore = 1;
  static struct strbuf merge_msg = STRBUF_INIT;
  static struct strategy **use_strategies;
@@@ -199,6 -199,8 +199,8 @@@ static struct option builtin_merge_opti
        OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
                N_("abort if fast-forward is not possible")),
        OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
+       OPT_BOOL(0, "verify-signatures", &verify_signatures,
+               N_("Verify that the named commit has a valid GPG signature")),
        OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
                N_("merge strategy to use"), option_parse_strategy),
        OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
@@@ -516,19 -518,6 +518,19 @@@ static void merge_name(const char *remo
                strbuf_release(&line);
                goto cleanup;
        }
 +
 +      if (remote_head->util) {
 +              struct merge_remote_desc *desc;
 +              desc = merge_remote_util(remote_head);
 +              if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
 +                      strbuf_addf(msg, "%s\t\t%s '%s'\n",
 +                                  sha1_to_hex(desc->obj->sha1),
 +                                  typename(desc->obj->type),
 +                                  remote);
 +                      goto cleanup;
 +              }
 +      }
 +
        strbuf_addf(msg, "%s\t\tcommit '%s'\n",
                sha1_to_hex(remote_head->object.sha1), remote);
  cleanup:
@@@ -1246,6 -1235,39 +1248,39 @@@ int cmd_merge(int argc, const char **ar
                usage_with_options(builtin_merge_usage,
                        builtin_merge_options);
  
+       if (verify_signatures) {
+               for (p = remoteheads; p; p = p->next) {
+                       struct commit *commit = p->item;
+                       char hex[41];
+                       struct signature_check signature_check;
+                       memset(&signature_check, 0, sizeof(signature_check));
+                       check_commit_signature(commit, &signature_check);
+                       strcpy(hex, find_unique_abbrev(commit->object.sha1, 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);
+                       free(signature_check.gpg_output);
+                       free(signature_check.gpg_status);
+                       free(signature_check.signer);
+                       free(signature_check.key);
+               }
+       }
        strbuf_addstr(&buf, "merge");
        for (p = remoteheads; p; p = p->next)
                strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name);