Skip to content

Commit 9d3d784

Browse files
committed
Merge branch 'jc/signed-commit' and 'jc/pull-signed-tag'
They both use the extended headers in commit objects, and the former has necessary infrastructure to show them that is useful to view the result of the latter. Signed-off-by: Junio C Hamano <[email protected]>
2 parents 0074d18 + 0c5e70f commit 9d3d784

File tree

15 files changed

+364
-19
lines changed

15 files changed

+364
-19
lines changed

Documentation/config.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,17 @@ grep.lineNumber::
10941094
grep.extendedRegexp::
10951095
If set to true, enable '--extended-regexp' option by default.
10961096

1097+
gpg.program::
1098+
Use this custom program instead of "gpg" found on $PATH when
1099+
making or verifying a PGP signature. The program must support the
1100+
same command line interface as GPG, namely, to verify a detached
1101+
signature, "gpg --verify $file - <$signature" is run, and the
1102+
program is expected to signal a good signature by exiting with
1103+
code 0, and to generate an ascii-armored detached signature, the
1104+
standard input of "gpg -bsau $key" is fed with the contents to be
1105+
signed, and the program is expected to send the result to its
1106+
standard output.
1107+
10971108
gui.commitmsgwidth::
10981109
Defines how wide the commit message window is in the
10991110
linkgit:git-gui[1]. "75" is the default.

Documentation/git-tag.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ created (i.e. a lightweight tag).
3838
A GnuPG signed tag object will be created when `-s` or `-u
3939
<key-id>` is used. When `-u <key-id>` is not used, the
4040
committer identity for the current user is used to find the
41-
GnuPG key for signing.
41+
GnuPG key for signing. The configuration variable `gpg.program`
42+
is used to specify custom GnuPG binary.
43+
4244

4345
OPTIONS
4446
-------
@@ -48,11 +50,11 @@ OPTIONS
4850

4951
-s::
5052
--sign::
51-
Make a GPG-signed tag, using the default e-mail address's key
53+
Make a GPG-signed tag, using the default e-mail address's key.
5254

5355
-u <key-id>::
5456
--local-user=<key-id>::
55-
Make a GPG-signed tag, using the given key
57+
Make a GPG-signed tag, using the given key.
5658

5759
-f::
5860
--force::

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

@@ -1492,7 +1501,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
14921501
}
14931502

14941503
if (commit_tree_extended(sb.buf, active_cache_tree->sha1, parents, sha1,
1495-
author_ident.buf, extra)) {
1504+
author_ident.buf, sign_commit, extra)) {
14961505
rollback_index_files();
14971506
die(_("failed to write commit object"));
14981507
}

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: 89 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,86 @@ 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+
880+
int parse_signed_commit(const unsigned char *sha1,
881+
struct strbuf *payload, struct strbuf *signature)
882+
{
883+
unsigned long size;
884+
enum object_type type;
885+
char *buffer = read_sha1_file(sha1, &type, &size);
886+
int in_signature, saw_signature = -1;
887+
char *line, *tail;
888+
889+
if (!buffer || type != OBJ_COMMIT)
890+
goto cleanup;
891+
892+
line = buffer;
893+
tail = buffer + size;
894+
in_signature = 0;
895+
saw_signature = 0;
896+
while (line < tail) {
897+
const char *sig = NULL;
898+
char *next = memchr(line, '\n', tail - line);
899+
900+
next = next ? next + 1 : tail;
901+
if (in_signature && line[0] == ' ')
902+
sig = line + 1;
903+
else if (!prefixcmp(line, gpg_sig_header) &&
904+
line[gpg_sig_header_len] == ' ')
905+
sig = line + gpg_sig_header_len + 1;
906+
if (sig) {
907+
strbuf_add(signature, sig, next - sig);
908+
saw_signature = 1;
909+
in_signature = 1;
910+
} else {
911+
if (*line == '\n')
912+
/* dump the whole remainder of the buffer */
913+
next = tail;
914+
strbuf_add(payload, line, next - line);
915+
in_signature = 0;
916+
}
917+
line = next;
918+
}
919+
cleanup:
920+
free(buffer);
921+
return saw_signature;
922+
}
923+
843924
static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
844925
{
845926
struct merge_remote_desc *desc;
@@ -975,13 +1056,14 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
9751056

9761057
int commit_tree(const char *msg, unsigned char *tree,
9771058
struct commit_list *parents, unsigned char *ret,
978-
const char *author)
1059+
const char *author, const char *sign_commit)
9791060
{
9801061
struct commit_extra_header *extra = NULL, **tail = &extra;
9811062
int result;
9821063

9831064
append_merge_tag_headers(parents, &tail);
984-
result = commit_tree_extended(msg, tree, parents, ret, author, extra);
1065+
result = commit_tree_extended(msg, tree, parents, ret,
1066+
author, sign_commit, extra);
9851067
free_commit_extra_headers(extra);
9861068
return result;
9871069
}
@@ -993,7 +1075,8 @@ static const char commit_utf8_warn[] =
9931075

9941076
int commit_tree_extended(const char *msg, unsigned char *tree,
9951077
struct commit_list *parents, unsigned char *ret,
996-
const char *author, struct commit_extra_header *extra)
1078+
const char *author, const char *sign_commit,
1079+
struct commit_extra_header *extra)
9971080
{
9981081
int result;
9991082
int encoding_is_utf8;
@@ -1043,6 +1126,9 @@ int commit_tree_extended(const char *msg, unsigned char *tree,
10431126
if (encoding_is_utf8 && !is_utf8(buffer.buf))
10441127
fprintf(stderr, commit_utf8_warn);
10451128

1129+
if (sign_commit && do_sign_commit(&buffer, sign_commit))
1130+
return -1;
1131+
10461132
result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
10471133
strbuf_release(&buffer);
10481134
return result;

commit.h

Lines changed: 4 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 *);
@@ -218,4 +218,6 @@ struct merge_remote_desc {
218218
*/
219219
struct commit *get_merge_parent(const char *name);
220220

221+
extern int parse_signed_commit(const unsigned char *sha1,
222+
struct strbuf *message, struct strbuf *signature);
221223
#endif /* COMMIT_H */

0 commit comments

Comments
 (0)