Skip to content

Commit 4d5f7c0

Browse files
committed
Merge branch 'cc/fast-import-strip-signed-commits' into seen
* cc/fast-import-strip-signed-commits: fast-import: add '--signed-commits=<mode>' option gpg-interface: refactor 'enum sign_mode' parsing
2 parents 3568fae + 61f60ef commit 4d5f7c0

7 files changed

Lines changed: 196 additions & 28 deletions

File tree

Documentation/git-fast-import.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ fast-import stream! This option is enabled automatically for
6666
remote-helpers that use the `import` capability, as they are
6767
already trusted to run their own code.
6868

69+
--signed-commits=(verbatim|warn-verbatim|warn-strip|strip|abort)::
70+
Specify how to handle signed commits. Behaves in the same way
71+
as the same option in linkgit:git-fast-export[1], except that
72+
default is 'verbatim' (instead of 'abort').
73+
6974
Options for Frontends
7075
~~~~~~~~~~~~~~~~~~~~~
7176

builtin/fast-export.c

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ static const char *const fast_export_usage[] = {
3737
NULL
3838
};
3939

40-
enum sign_mode { SIGN_ABORT, SIGN_VERBATIM, SIGN_STRIP, SIGN_WARN_VERBATIM, SIGN_WARN_STRIP };
41-
4240
static int progress;
4341
static enum sign_mode signed_tag_mode = SIGN_ABORT;
4442
static enum sign_mode signed_commit_mode = SIGN_STRIP;
@@ -59,24 +57,17 @@ static struct hashmap anonymized_seeds;
5957
static struct revision_sources revision_sources;
6058

6159
static int parse_opt_sign_mode(const struct option *opt,
62-
const char *arg, int unset)
60+
const char *arg, int unset)
6361
{
6462
enum sign_mode *val = opt->value;
63+
6564
if (unset)
6665
return 0;
67-
else if (!strcmp(arg, "abort"))
68-
*val = SIGN_ABORT;
69-
else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
70-
*val = SIGN_VERBATIM;
71-
else if (!strcmp(arg, "warn-verbatim") || !strcmp(arg, "warn"))
72-
*val = SIGN_WARN_VERBATIM;
73-
else if (!strcmp(arg, "warn-strip"))
74-
*val = SIGN_WARN_STRIP;
75-
else if (!strcmp(arg, "strip"))
76-
*val = SIGN_STRIP;
77-
else
78-
return error("Unknown %s mode: %s", opt->long_name, arg);
79-
return 0;
66+
67+
if (!parse_sign_mode(arg, val))
68+
return 0;
69+
70+
return error("Unknown %s mode: %s", opt->long_name, arg);
8071
}
8172

8273
static int parse_opt_tag_of_filtered_mode(const struct option *opt,

builtin/fast-import.c

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ static int global_argc;
188188
static const char **global_argv;
189189
static const char *global_prefix;
190190

191+
static enum sign_mode signed_commit_mode = SIGN_VERBATIM;
192+
191193
/* Memory pools */
192194
static struct mem_pool fi_mem_pool = {
193195
.block_alloc = 2*1024*1024 - sizeof(struct mp_block),
@@ -2787,6 +2789,23 @@ static void store_signature(struct signature_data *stored_sig,
27872789
}
27882790
}
27892791

2792+
/* Process signatures (up to 2: one "sha1" and one "sha256") */
2793+
static void import_signature(struct signature_data *sig_sha1,
2794+
struct signature_data *sig_sha256,
2795+
const char *v)
2796+
{
2797+
struct signature_data sig = { NULL, NULL, STRBUF_INIT };
2798+
2799+
parse_one_signature(&sig, v);
2800+
2801+
if (!strcmp(sig.hash_algo, "sha1"))
2802+
store_signature(sig_sha1, &sig, "SHA-1");
2803+
else if (!strcmp(sig.hash_algo, "sha256"))
2804+
store_signature(sig_sha256, &sig, "SHA-256");
2805+
else
2806+
BUG("parse_one_signature() returned unknown hash algo");
2807+
}
2808+
27902809
static void parse_new_commit(const char *arg)
27912810
{
27922811
static struct strbuf msg = STRBUF_INIT;
@@ -2819,19 +2838,28 @@ static void parse_new_commit(const char *arg)
28192838
if (!committer)
28202839
die("Expected committer but didn't get one");
28212840

2822-
/* Process signatures (up to 2: one "sha1" and one "sha256") */
28232841
while (skip_prefix(command_buf.buf, "gpgsig ", &v)) {
2824-
struct signature_data sig = { NULL, NULL, STRBUF_INIT };
2825-
2826-
parse_one_signature(&sig, v);
2827-
2828-
if (!strcmp(sig.hash_algo, "sha1"))
2829-
store_signature(&sig_sha1, &sig, "SHA-1");
2830-
else if (!strcmp(sig.hash_algo, "sha256"))
2831-
store_signature(&sig_sha256, &sig, "SHA-256");
2832-
else
2833-
BUG("parse_one_signature() returned unknown hash algo");
2834-
2842+
struct strbuf data = STRBUF_INIT;
2843+
switch (signed_commit_mode) {
2844+
case SIGN_ABORT:
2845+
die("encountered signed commit; use "
2846+
"--signed-commits=<mode> to handle it");
2847+
case SIGN_WARN_VERBATIM:
2848+
warning("importing a commit signature");
2849+
/* fallthru */
2850+
case SIGN_VERBATIM:
2851+
import_signature(&sig_sha1, &sig_sha256, v);
2852+
break;
2853+
case SIGN_WARN_STRIP:
2854+
warning("stripping a commit signature");
2855+
/* fallthru */
2856+
case SIGN_STRIP:
2857+
/* Read signature data and discard it */
2858+
read_next_command();
2859+
parse_data(&data, 0, NULL);
2860+
strbuf_release(&data);
2861+
break;
2862+
}
28352863
read_next_command();
28362864
}
28372865

@@ -3503,6 +3531,9 @@ static int parse_one_option(const char *option)
35033531
option_active_branches(option);
35043532
} else if (skip_prefix(option, "export-pack-edges=", &option)) {
35053533
option_export_pack_edges(option);
3534+
} else if (skip_prefix(option, "signed-commits=", &option)) {
3535+
if (parse_sign_mode(option, &signed_commit_mode))
3536+
die("unknown --signed-commits mode '%s'", option);
35063537
} else if (!strcmp(option, "quiet")) {
35073538
show_stats = 0;
35083539
quiet = 1;

gpg-interface.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,3 +1125,20 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
11251125
FREE_AND_NULL(ssh_signing_key_file);
11261126
return ret;
11271127
}
1128+
1129+
int parse_sign_mode(const char *arg, enum sign_mode *mode)
1130+
{
1131+
if (!strcmp(arg, "abort"))
1132+
*mode = SIGN_ABORT;
1133+
else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
1134+
*mode = SIGN_VERBATIM;
1135+
else if (!strcmp(arg, "warn-verbatim") || !strcmp(arg, "warn"))
1136+
*mode = SIGN_WARN_VERBATIM;
1137+
else if (!strcmp(arg, "warn-strip"))
1138+
*mode = SIGN_WARN_STRIP;
1139+
else if (!strcmp(arg, "strip"))
1140+
*mode = SIGN_STRIP;
1141+
else
1142+
return -1;
1143+
return 0;
1144+
}

gpg-interface.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,19 @@ int check_signature(struct signature_check *sigc,
104104
void print_signature_buffer(const struct signature_check *sigc,
105105
unsigned flags);
106106

107+
/* Modes for --signed-tags=<mode> and --signed-commits=<mode> options */
108+
enum sign_mode {
109+
SIGN_ABORT,
110+
SIGN_WARN_VERBATIM,
111+
SIGN_VERBATIM,
112+
SIGN_WARN_STRIP,
113+
SIGN_STRIP,
114+
};
115+
116+
/*
117+
* Return 0 if `arg` can be parsed into an `enum sign_mode`. Return -1
118+
* otherwise.
119+
*/
120+
int parse_sign_mode(const char *arg, enum sign_mode *mode);
121+
107122
#endif

t/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,7 @@ integration_tests = [
10371037
't9302-fast-import-unpack-limit.sh',
10381038
't9303-fast-import-compression.sh',
10391039
't9304-fast-import-marks.sh',
1040+
't9305-fast-import-signatures.sh',
10401041
't9350-fast-export.sh',
10411042
't9351-fast-export-anonymize.sh',
10421043
't9400-git-cvsserver-server.sh',

t/t9305-fast-import-signatures.sh

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#!/bin/sh
2+
3+
test_description='git fast-import --signed-commits=<mode>'
4+
5+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6+
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
7+
8+
. ./test-lib.sh
9+
. "$TEST_DIRECTORY/lib-gpg.sh"
10+
11+
test_expect_success 'set up unsigned initial commit and import repo' '
12+
test_commit first &&
13+
mkdir new &&
14+
git --git-dir=new/.git init
15+
'
16+
17+
test_expect_success GPG 'set up OpenPGP signed commit' '
18+
git checkout -b openpgp-signing main &&
19+
echo "Content for OpenPGP signing." >file-sign &&
20+
git add file-sign &&
21+
git commit -S -m "OpenPGP signed commit" &&
22+
OPENPGP_SIGNING=$(git rev-parse --verify openpgp-signing)
23+
'
24+
25+
test_expect_success GPG 'import OpenPGP signature with --signed-commits=verbatim' '
26+
git fast-export --signed-commits=verbatim openpgp-signing >output &&
27+
git -C new fast-import --quiet --signed-commits=verbatim <output >log 2>&1 &&
28+
IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
29+
test $OPENPGP_SIGNING = $IMPORTED &&
30+
test_must_be_empty log
31+
'
32+
33+
test_expect_success GPGSM 'set up X.509 signed commit' '
34+
git checkout -b x509-signing main &&
35+
test_config gpg.format x509 &&
36+
test_config user.signingkey $GIT_COMMITTER_EMAIL &&
37+
echo "Content for X.509 signing." >file-sign &&
38+
git add file-sign &&
39+
git commit -S -m "X.509 signed commit" &&
40+
X509_SIGNING=$(git rev-parse HEAD)
41+
'
42+
43+
test_expect_success GPGSM 'import X.509 signature fails with --signed-commits=abort' '
44+
git fast-export --signed-commits=verbatim x509-signing >output &&
45+
test_must_fail git -C new fast-import --quiet --signed-commits=abort <output
46+
'
47+
48+
test_expect_success GPGSM 'import X.509 signature with --signed-commits=warn-verbatim' '
49+
git -C new fast-import --quiet --signed-commits=warn-verbatim <output >log 2>&1 &&
50+
IMPORTED=$(git -C new rev-parse --verify refs/heads/x509-signing) &&
51+
test $X509_SIGNING = $IMPORTED &&
52+
test_grep "importing a commit signature" log
53+
'
54+
55+
test_expect_success GPGSSH 'set up SSH signed commit' '
56+
git checkout -b ssh-signing main &&
57+
test_config gpg.format ssh &&
58+
test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
59+
echo "Content for SSH signing." >file-sign &&
60+
git add file-sign &&
61+
git commit -S -m "SSH signed commit" &&
62+
SSH_SIGNING=$(git rev-parse HEAD)
63+
'
64+
65+
test_expect_success GPGSSH 'strip SSH signature with --signed-commits=strip' '
66+
git fast-export --signed-commits=verbatim ssh-signing >output &&
67+
git -C new fast-import --quiet --signed-commits=strip <output >log 2>&1 &&
68+
IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) &&
69+
test $SSH_SIGNING != $IMPORTED &&
70+
git -C new cat-file commit "$IMPORTED" >actual &&
71+
test_grep ! -E "^gpgsig" actual &&
72+
test_must_be_empty log
73+
'
74+
75+
test_expect_success GPG 'setup a commit with dual OpenPGP signatures on its SHA-1 and SHA-256 formats' '
76+
# Create a signed SHA-256 commit
77+
git init --object-format=sha256 explicit-sha256 &&
78+
git -C explicit-sha256 config extensions.compatObjectFormat sha1 &&
79+
git -C explicit-sha256 checkout -b dual-signed &&
80+
test_commit -C explicit-sha256 A &&
81+
echo B >explicit-sha256/B &&
82+
git -C explicit-sha256 add B &&
83+
test_tick &&
84+
git -C explicit-sha256 commit -S -m "signed" B &&
85+
SHA256_B=$(git -C explicit-sha256 rev-parse dual-signed) &&
86+
87+
# Create the corresponding SHA-1 commit
88+
SHA1_B=$(git -C explicit-sha256 rev-parse --output-object-format=sha1 dual-signed) &&
89+
90+
# Check that the resulting SHA-1 commit has both signatures
91+
echo $SHA1_B | git -C explicit-sha256 cat-file --batch >out &&
92+
test_grep -E "^gpgsig " out &&
93+
test_grep -E "^gpgsig-sha256 " out
94+
'
95+
96+
test_expect_success GPG 'strip both OpenPGP signatures with --signed-commits=warn-strip' '
97+
git -C explicit-sha256 fast-export --signed-commits=verbatim dual-signed >output &&
98+
test_grep -E "^gpgsig sha1 openpgp" output &&
99+
test_grep -E "^gpgsig sha256 openpgp" output &&
100+
git -C new fast-import --quiet --signed-commits=warn-strip <output >log 2>&1 &&
101+
git -C new cat-file commit refs/heads/dual-signed >actual &&
102+
test_grep ! -E "^gpgsig " actual &&
103+
test_grep ! -E "^gpgsig-sha256 " actual &&
104+
test_grep "stripping a commit signature" log >out &&
105+
test_line_count = 2 out
106+
'
107+
108+
test_done

0 commit comments

Comments
 (0)