diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index b9c331225f..d5e19af547 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -8,8 +8,9 @@ #include "tree.h" #include "builtin.h" #include "utf8.h" +#include "gpg-interface.h" -static const char commit_tree_usage[] = "git commit-tree [(-p )...] [-m ] [-F ] ...", @@ -85,6 +86,8 @@ static int all, edit_flag, also, interactive, patch_interactive, only, amend, si static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int no_post_rewrite, allow_empty_message; static char *untracked_files_arg, *force_date, *ignore_submodule_arg; +static char *sign_commit; + /* * The default commit message cleanup mode will remove the lines * beginning with # (shell comments) and leading and trailing @@ -144,6 +147,8 @@ static struct option builtin_commit_options[] = { OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"), OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"), OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"), + { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id", + "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, /* end commit message options */ OPT_GROUP("Commit contents options"), @@ -1324,6 +1329,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1, static int git_commit_config(const char *k, const char *v, void *cb) { struct wt_status *s = cb; + int status; if (!strcmp(k, "commit.template")) return git_config_pathname(&template_file, k, v); @@ -1332,6 +1338,9 @@ static int git_commit_config(const char *k, const char *v, void *cb) return 0; } + status = git_gpg_config(k, v, NULL); + if (status) + return status; return git_status_config(k, v, s); } @@ -1488,7 +1497,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) extra = read_commit_extra_headers(current_head); if (commit_tree_extended(sb.buf, active_cache_tree->sha1, parents, sha1, - author_ident.buf, extra)) { + author_ident.buf, sign_commit, extra)) { rollback_index_files(); die(_("failed to write commit object")); } diff --git a/builtin/merge.c b/builtin/merge.c index 99f1429b35..e5afe64cef 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -26,6 +26,7 @@ #include "merge-recursive.h" #include "resolve-undo.h" #include "remote.h" +#include "gpg-interface.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -62,6 +63,7 @@ static int allow_rerere_auto; static int abort_current_merge; static int show_progress = -1; static int default_to_upstream; +static const char *sign_commit; static struct strategy all_strategy[] = { { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL }, @@ -207,6 +209,8 @@ static struct option builtin_merge_options[] = { OPT_BOOLEAN(0, "abort", &abort_current_merge, "abort the current in-progress merge"), OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1), + { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id", + "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, OPT_END() }; @@ -533,6 +537,8 @@ static void parse_branch_merge_options(char *bmo) static int git_merge_config(const char *k, const char *v, void *cb) { + int status; + if (branch && !prefixcmp(k, "branch.") && !prefixcmp(k + 7, branch) && !strcmp(k + 7 + strlen(branch), ".mergeoptions")) { @@ -570,6 +576,10 @@ static int git_merge_config(const char *k, const char *v, void *cb) default_to_upstream = git_config_bool(k, v); return 0; } + + status = git_gpg_config(k, v, NULL); + if (status) + return status; return git_diff_ui_config(k, v, cb); } @@ -902,7 +912,8 @@ static int merge_trivial(struct commit *head) parent->next->item = remoteheads->item; parent->next->next = NULL; prepare_to_commit(); - commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL); + commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL, + sign_commit); finish(head, result_commit, "In-index merge"); drop_save(); return 0; @@ -933,7 +944,8 @@ static int finish_automerge(struct commit *head, strbuf_addch(&merge_msg, '\n'); prepare_to_commit(); free_commit_list(remoteheads); - commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL); + commit_tree(merge_msg.buf, result_tree, parents, result_commit, + NULL, sign_commit); strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy); finish(head, result_commit, buf.buf); strbuf_release(&buf); 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; diff --git a/commit.h b/commit.h index 3745f12099..d2c3e650b1 100644 --- a/commit.h +++ b/commit.h @@ -193,11 +193,11 @@ extern void append_merge_tag_headers(struct commit_list *parents, extern 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); extern int commit_tree_extended(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 *); extern struct commit_extra_header *read_commit_extra_headers(struct commit *); diff --git a/notes-cache.c b/notes-cache.c index 4c8984ede1..c36a960bc3 100644 --- a/notes-cache.c +++ b/notes-cache.c @@ -56,7 +56,7 @@ int notes_cache_write(struct notes_cache *c) if (write_notes_tree(&c->tree, tree_sha1)) return -1; - if (commit_tree(c->validity, tree_sha1, NULL, commit_sha1, NULL) < 0) + if (commit_tree(c->validity, tree_sha1, NULL, commit_sha1, NULL, NULL) < 0) return -1; if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL, 0, QUIET_ON_ERR) < 0) diff --git a/notes-merge.c b/notes-merge.c index e9e4199311..61cf18eeab 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -546,7 +546,7 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents, /* else: t->ref points to nothing, assume root/orphan commit */ } - if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL)) + if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL)) die("Failed to commit notes tree to database"); }