summaryrefslogtreecommitdiff
path: root/commit.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2011-10-05 17:23:20 -0700
committerJunio C Hamano <gitster@pobox.com>2011-11-12 22:27:37 -0800
commitba3c69a9ee1894de397b60d3b548383e13ef49e3 (patch)
treea0d13cdc7e85835f529afce77e53a67b3ef42a84 /commit.c
parent96b8d93a539f3a9f43f0fc38214db8753cac3c2e (diff)
downloadgit-ba3c69a9ee1894de397b60d3b548383e13ef49e3.tar.gz
commit: teach --gpg-sign option
This uses the gpg-interface.[ch] to allow signing the commit, i.e. $ git commit --gpg-sign -m foo You need a passphrase to unlock the secret key for user: "Junio C Hamano <gitster@pobox.com>" 4096-bit RSA key, ID 96AFE6CB, created 2011-10-03 (main key ID 713660A7) [master 8457d13] foo 1 files changed, 1 insertions(+), 0 deletions(-) The lines of GPG detached signature are placed in a new multi-line header field, instead of tucking the signature block at the end of the commit log message text (similar to how signed tag is done), for multiple reasons: - The signature won't clutter output from "git log" and friends if it is in the extra header. If we place it at the end of the log message, we would need to teach "git log" and friends to strip the signature block with an option. - Teaching new versions of "git log" and "gitk" to optionally verify and show signatures is cleaner if we structurally know where the signature block is (instead of scanning in the commit log message). - The signature needs to be stripped upon various commit rewriting operations, e.g. rebase, filter-branch, etc. They all already ignore unknown headers, but if we place signature in the log message, all of these tools (and third-party tools) also need to learn how a signature block would look like. - When we added the optional encoding header, all the tools (both in tree and third-party) that acts on the raw commit object should have been fixed to ignore headers they do not understand, so it is not like that new header would be more likely to break than extra text in the commit. A commit made with the above sample sequence would look like this: $ git cat-file commit HEAD tree 3cd71d90e3db4136e5260ab54599791c4f883b9d parent b87755351a47b09cb27d6913e6e0e17e6254a4d4 author Junio C Hamano <gitster@pobox.com> 1317862251 -0700 committer Junio C Hamano <gitster@pobox.com> 1317862251 -0700 gpgsig -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) iQIcBAABAgAGBQJOjPtrAAoJELC16IaWr+bL4TMP/RSe2Y/jYnCkds9unO5JEnfG ... =dt98 -----END PGP SIGNATURE----- foo but "git log" (unless you ask for it with --pretty=raw) output is not cluttered with the signature information. Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'commit.c')
-rw-r--r--commit.c48
1 files changed, 45 insertions, 3 deletions
diff --git a/commit.c b/commit.c
index b78127403b..f00076e91f 100644
--- a/commit.c
+++ b/commit.c
@@ -6,6 +6,7 @@
#include "diff.h"
#include "revision.h"
#include "notes.h"
+#include "gpg-interface.h"
int save_commit_buffer = 1;
@@ -840,6 +841,42 @@ struct commit_list *reduce_heads(struct commit_list *heads)
return result;
}
+static const char gpg_sig_header[] = "gpgsig";
+static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
+
+static int do_sign_commit(struct strbuf *buf, const char *keyid)
+{
+ struct strbuf sig = STRBUF_INIT;
+ int inspos, copypos;
+
+ /* find the end of the header */
+ inspos = strstr(buf->buf, "\n\n") - buf->buf + 1;
+
+ if (!keyid || !*keyid)
+ keyid = get_signing_key();
+ if (sign_buffer(buf, &sig, keyid)) {
+ strbuf_release(&sig);
+ return -1;
+ }
+
+ for (copypos = 0; sig.buf[copypos]; ) {
+ const char *bol = sig.buf + copypos;
+ const char *eol = strchrnul(bol, '\n');
+ int len = (eol - bol) + !!*eol;
+
+ if (!copypos) {
+ strbuf_insert(buf, inspos, gpg_sig_header, gpg_sig_header_len);
+ inspos += gpg_sig_header_len;
+ }
+ strbuf_insert(buf, inspos++, " ", 1);
+ strbuf_insert(buf, inspos, bol, len);
+ inspos += len;
+ copypos += len;
+ }
+ strbuf_release(&sig);
+ return 0;
+}
+
static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
{
struct merge_remote_desc *desc;
@@ -975,13 +1012,14 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
int commit_tree(const char *msg, unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
- const char *author)
+ const char *author, const char *sign_commit)
{
struct commit_extra_header *extra = NULL, **tail = &extra;
int result;
append_merge_tag_headers(parents, &tail);
- result = commit_tree_extended(msg, tree, parents, ret, author, extra);
+ result = commit_tree_extended(msg, tree, parents, ret,
+ author, sign_commit, extra);
free_commit_extra_headers(extra);
return result;
}
@@ -993,7 +1031,8 @@ static const char commit_utf8_warn[] =
int commit_tree_extended(const char *msg, unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
- const char *author, struct commit_extra_header *extra)
+ const char *author, const char *sign_commit,
+ struct commit_extra_header *extra)
{
int result;
int encoding_is_utf8;
@@ -1043,6 +1082,9 @@ int commit_tree_extended(const char *msg, unsigned char *tree,
if (encoding_is_utf8 && !is_utf8(buffer.buf))
fprintf(stderr, commit_utf8_warn);
+ if (sign_commit && do_sign_commit(&buffer, sign_commit))
+ return -1;
+
result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
strbuf_release(&buffer);
return result;