Skip to content

Commit ba3c69a

Browse files
committed
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 <[email protected]>" 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 b877553 author Junio C Hamano <[email protected]> 1317862251 -0700 committer Junio C Hamano <[email protected]> 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 <[email protected]>
1 parent 96b8d93 commit ba3c69a

File tree

7 files changed

+95
-13
lines changed

7 files changed

+95
-13
lines changed

builtin/commit-tree.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
#include "tree.h"
99
#include "builtin.h"
1010
#include "utf8.h"
11+
#include "gpg-interface.h"
1112

12-
static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-m <message>] [-F <file>] <sha1> <changelog";
13+
static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S<signer>] [-m <message>] [-F <file>] <sha1> <changelog";
1314

1415
static void new_parent(struct commit *parent, struct commit_list **parents_p)
1516
{
@@ -25,19 +26,31 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p)
2526
commit_list_insert(parent, parents_p);
2627
}
2728

29+
static int commit_tree_config(const char *var, const char *value, void *cb)
30+
{
31+
int status = git_gpg_config(var, value, NULL);
32+
if (status)
33+
return status;
34+
return git_default_config(var, value, cb);
35+
}
36+
2837
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
2938
{
3039
int i, got_tree = 0;
3140
struct commit_list *parents = NULL;
3241
unsigned char tree_sha1[20];
3342
unsigned char commit_sha1[20];
3443
struct strbuf buffer = STRBUF_INIT;
44+
const char *sign_commit = NULL;
3545

36-
git_config(git_default_config, NULL);
46+
git_config(commit_tree_config, NULL);
3747

3848
if (argc < 2 || !strcmp(argv[1], "-h"))
3949
usage(commit_tree_usage);
4050

51+
if (get_sha1(argv[1], tree_sha1))
52+
die("Not a valid object name %s", argv[1]);
53+
4154
for (i = 1; i < argc; i++) {
4255
const char *arg = argv[i];
4356
if (!strcmp(arg, "-p")) {
@@ -51,6 +64,11 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
5164
continue;
5265
}
5366

67+
if (!memcmp(arg, "-S", 2)) {
68+
sign_commit = arg + 2;
69+
continue;
70+
}
71+
5472
if (!strcmp(arg, "-m")) {
5573
if (argc <= ++i)
5674
usage(commit_tree_usage);
@@ -98,7 +116,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
98116
die_errno("git commit-tree: failed to read");
99117
}
100118

101-
if (commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
119+
if (commit_tree(buffer.buf, tree_sha1, parents, commit_sha1,
120+
NULL, sign_commit)) {
102121
strbuf_release(&buffer);
103122
return 1;
104123
}

builtin/commit.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "unpack-trees.h"
2727
#include "quote.h"
2828
#include "submodule.h"
29+
#include "gpg-interface.h"
2930

3031
static const char * const builtin_commit_usage[] = {
3132
"git commit [options] [--] <filepattern>...",
@@ -85,6 +86,8 @@ static int all, edit_flag, also, interactive, patch_interactive, only, amend, si
8586
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
8687
static int no_post_rewrite, allow_empty_message;
8788
static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
89+
static char *sign_commit;
90+
8891
/*
8992
* The default commit message cleanup mode will remove the lines
9093
* beginning with # (shell comments) and leading and trailing
@@ -144,6 +147,8 @@ static struct option builtin_commit_options[] = {
144147
OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
145148
OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
146149
OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
150+
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
151+
"GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
147152
/* end commit message options */
148153

149154
OPT_GROUP("Commit contents options"),
@@ -1324,6 +1329,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
13241329
static int git_commit_config(const char *k, const char *v, void *cb)
13251330
{
13261331
struct wt_status *s = cb;
1332+
int status;
13271333

13281334
if (!strcmp(k, "commit.template"))
13291335
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)
13321338
return 0;
13331339
}
13341340

1341+
status = git_gpg_config(k, v, NULL);
1342+
if (status)
1343+
return status;
13351344
return git_status_config(k, v, s);
13361345
}
13371346

@@ -1488,7 +1497,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
14881497
extra = read_commit_extra_headers(current_head);
14891498

14901499
if (commit_tree_extended(sb.buf, active_cache_tree->sha1, parents, sha1,
1491-
author_ident.buf, extra)) {
1500+
author_ident.buf, sign_commit, extra)) {
14921501
rollback_index_files();
14931502
die(_("failed to write commit object"));
14941503
}

builtin/merge.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "merge-recursive.h"
2727
#include "resolve-undo.h"
2828
#include "remote.h"
29+
#include "gpg-interface.h"
2930

3031
#define DEFAULT_TWOHEAD (1<<0)
3132
#define DEFAULT_OCTOPUS (1<<1)
@@ -62,6 +63,7 @@ static int allow_rerere_auto;
6263
static int abort_current_merge;
6364
static int show_progress = -1;
6465
static int default_to_upstream;
66+
static const char *sign_commit;
6567

6668
static struct strategy all_strategy[] = {
6769
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -207,6 +209,8 @@ static struct option builtin_merge_options[] = {
207209
OPT_BOOLEAN(0, "abort", &abort_current_merge,
208210
"abort the current in-progress merge"),
209211
OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1),
212+
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
213+
"GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
210214
OPT_END()
211215
};
212216

@@ -533,6 +537,8 @@ static void parse_branch_merge_options(char *bmo)
533537

534538
static int git_merge_config(const char *k, const char *v, void *cb)
535539
{
540+
int status;
541+
536542
if (branch && !prefixcmp(k, "branch.") &&
537543
!prefixcmp(k + 7, branch) &&
538544
!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
@@ -570,6 +576,10 @@ static int git_merge_config(const char *k, const char *v, void *cb)
570576
default_to_upstream = git_config_bool(k, v);
571577
return 0;
572578
}
579+
580+
status = git_gpg_config(k, v, NULL);
581+
if (status)
582+
return status;
573583
return git_diff_ui_config(k, v, cb);
574584
}
575585

@@ -902,7 +912,8 @@ static int merge_trivial(struct commit *head)
902912
parent->next->item = remoteheads->item;
903913
parent->next->next = NULL;
904914
prepare_to_commit();
905-
commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
915+
commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL,
916+
sign_commit);
906917
finish(head, result_commit, "In-index merge");
907918
drop_save();
908919
return 0;
@@ -933,7 +944,8 @@ static int finish_automerge(struct commit *head,
933944
strbuf_addch(&merge_msg, '\n');
934945
prepare_to_commit();
935946
free_commit_list(remoteheads);
936-
commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
947+
commit_tree(merge_msg.buf, result_tree, parents, result_commit,
948+
NULL, sign_commit);
937949
strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
938950
finish(head, result_commit, buf.buf);
939951
strbuf_release(&buf);

commit.c

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "diff.h"
77
#include "revision.h"
88
#include "notes.h"
9+
#include "gpg-interface.h"
910

1011
int save_commit_buffer = 1;
1112

@@ -840,6 +841,42 @@ struct commit_list *reduce_heads(struct commit_list *heads)
840841
return result;
841842
}
842843

844+
static const char gpg_sig_header[] = "gpgsig";
845+
static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
846+
847+
static int do_sign_commit(struct strbuf *buf, const char *keyid)
848+
{
849+
struct strbuf sig = STRBUF_INIT;
850+
int inspos, copypos;
851+
852+
/* find the end of the header */
853+
inspos = strstr(buf->buf, "\n\n") - buf->buf + 1;
854+
855+
if (!keyid || !*keyid)
856+
keyid = get_signing_key();
857+
if (sign_buffer(buf, &sig, keyid)) {
858+
strbuf_release(&sig);
859+
return -1;
860+
}
861+
862+
for (copypos = 0; sig.buf[copypos]; ) {
863+
const char *bol = sig.buf + copypos;
864+
const char *eol = strchrnul(bol, '\n');
865+
int len = (eol - bol) + !!*eol;
866+
867+
if (!copypos) {
868+
strbuf_insert(buf, inspos, gpg_sig_header, gpg_sig_header_len);
869+
inspos += gpg_sig_header_len;
870+
}
871+
strbuf_insert(buf, inspos++, " ", 1);
872+
strbuf_insert(buf, inspos, bol, len);
873+
inspos += len;
874+
copypos += len;
875+
}
876+
strbuf_release(&sig);
877+
return 0;
878+
}
879+
843880
static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
844881
{
845882
struct merge_remote_desc *desc;
@@ -975,13 +1012,14 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
9751012

9761013
int commit_tree(const char *msg, unsigned char *tree,
9771014
struct commit_list *parents, unsigned char *ret,
978-
const char *author)
1015+
const char *author, const char *sign_commit)
9791016
{
9801017
struct commit_extra_header *extra = NULL, **tail = &extra;
9811018
int result;
9821019

9831020
append_merge_tag_headers(parents, &tail);
984-
result = commit_tree_extended(msg, tree, parents, ret, author, extra);
1021+
result = commit_tree_extended(msg, tree, parents, ret,
1022+
author, sign_commit, extra);
9851023
free_commit_extra_headers(extra);
9861024
return result;
9871025
}
@@ -993,7 +1031,8 @@ static const char commit_utf8_warn[] =
9931031

9941032
int commit_tree_extended(const char *msg, unsigned char *tree,
9951033
struct commit_list *parents, unsigned char *ret,
996-
const char *author, struct commit_extra_header *extra)
1034+
const char *author, const char *sign_commit,
1035+
struct commit_extra_header *extra)
9971036
{
9981037
int result;
9991038
int encoding_is_utf8;
@@ -1043,6 +1082,9 @@ int commit_tree_extended(const char *msg, unsigned char *tree,
10431082
if (encoding_is_utf8 && !is_utf8(buffer.buf))
10441083
fprintf(stderr, commit_utf8_warn);
10451084

1085+
if (sign_commit && do_sign_commit(&buffer, sign_commit))
1086+
return -1;
1087+
10461088
result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
10471089
strbuf_release(&buffer);
10481090
return result;

commit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,11 @@ extern void append_merge_tag_headers(struct commit_list *parents,
193193

194194
extern int commit_tree(const char *msg, unsigned char *tree,
195195
struct commit_list *parents, unsigned char *ret,
196-
const char *author);
196+
const char *author, const char *sign_commit);
197197

198198
extern int commit_tree_extended(const char *msg, unsigned char *tree,
199199
struct commit_list *parents, unsigned char *ret,
200-
const char *author,
200+
const char *author, const char *sign_commit,
201201
struct commit_extra_header *);
202202

203203
extern struct commit_extra_header *read_commit_extra_headers(struct commit *);

notes-cache.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ int notes_cache_write(struct notes_cache *c)
5656

5757
if (write_notes_tree(&c->tree, tree_sha1))
5858
return -1;
59-
if (commit_tree(c->validity, tree_sha1, NULL, commit_sha1, NULL) < 0)
59+
if (commit_tree(c->validity, tree_sha1, NULL, commit_sha1, NULL, NULL) < 0)
6060
return -1;
6161
if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
6262
0, QUIET_ON_ERR) < 0)

notes-merge.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
546546
/* else: t->ref points to nothing, assume root/orphan commit */
547547
}
548548

549-
if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL))
549+
if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
550550
die("Failed to commit notes tree to database");
551551
}
552552

0 commit comments

Comments
 (0)