Skip to content

Commit f8f8f06

Browse files
committed
fast-import: add '--signed-commits=<mode>' option
A '--signed-commits=<mode>' option is already available when using `git fast-export` to decide what should be done at export time about commit signatures. At import time though, there is no option, or other way, in `git fast-import` to decide about commit signatures. To remediate that, let's add a '--signed-commits=<mode>' option to `git fast-import` too. For now the supported <mode>s are the same as those supported by `git fast-export`. Signed-off-by: Christian Couder <[email protected]>
1 parent f8a5988 commit f8f8f06

File tree

4 files changed

+145
-8
lines changed

4 files changed

+145
-8
lines changed

Documentation/git-fast-import.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ OPTIONS
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-import.c

Lines changed: 33 additions & 8 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),
@@ -2817,19 +2819,39 @@ static void parse_new_commit(const char *arg)
28172819
if (!committer)
28182820
die("Expected committer but didn't get one");
28192821

2820-
/* Process signatures (up to 2: one "sha1" and one "sha256") */
28212822
while (skip_prefix(command_buf.buf, "gpgsig ", &v)) {
28222823
struct signature_data sig = { NULL, NULL, STRBUF_INIT };
28232824

2824-
parse_one_signature(&sig, v);
2825+
if (signed_commit_mode == SIGN_ABORT)
2826+
die(_("encountered signed commit; use "
2827+
"--signed-commits=<mode> to handle it"));
28252828

2826-
if (!strcmp(sig.hash_algo, "sha1"))
2827-
store_signature(&sig_sha1, &sig, "SHA-1");
2828-
else if (!strcmp(sig.hash_algo, "sha256"))
2829-
store_signature(&sig_sha256, &sig, "SHA-256");
2830-
else
2831-
BUG("parse_one_signature() returned unknown hash algo");
2829+
parse_one_signature(&sig, v);
28322830

2831+
switch (signed_commit_mode) {
2832+
case SIGN_ABORT:
2833+
BUG("SIGN_ABORT should be handled before calling parse_one_signature()");
2834+
break;
2835+
case SIGN_WARN_VERBATIM:
2836+
warning(_("importing a commit signature verbatim"));
2837+
/* fallthru */
2838+
case SIGN_VERBATIM:
2839+
if (!strcmp(sig.hash_algo, "sha1"))
2840+
store_signature(&sig_sha1, &sig, "SHA-1");
2841+
else if (!strcmp(sig.hash_algo, "sha256"))
2842+
store_signature(&sig_sha256, &sig, "SHA-256");
2843+
else
2844+
die(_("parse_one_signature() returned unknown hash algo"));
2845+
break;
2846+
case SIGN_WARN_STRIP:
2847+
warning(_("stripping a commit signature"));
2848+
/* fallthru */
2849+
case SIGN_STRIP:
2850+
/* Just discard signature data */
2851+
strbuf_release(&sig.data);
2852+
free(sig.hash_algo);
2853+
break;
2854+
}
28332855
read_next_command();
28342856
}
28352857

@@ -3501,6 +3523,9 @@ static int parse_one_option(const char *option)
35013523
option_active_branches(option);
35023524
} else if (skip_prefix(option, "export-pack-edges=", &option)) {
35033525
option_export_pack_edges(option);
3526+
} else if (skip_prefix(option, "signed-commits=", &option)) {
3527+
if (parse_sign_mode(option, &signed_commit_mode))
3528+
usagef(_("unknown --signed-commits mode '%s'"), option);
35043529
} else if (!strcmp(option, "quiet")) {
35053530
show_stats = 0;
35063531
quiet = 1;

t/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,7 @@ integration_tests = [
10321032
't9302-fast-import-unpack-limit.sh',
10331033
't9303-fast-import-compression.sh',
10341034
't9304-fast-import-marks.sh',
1035+
't9305-fast-import-signatures.sh',
10351036
't9350-fast-export.sh',
10361037
't9351-fast-export-anonymize.sh',
10371038
't9400-git-cvsserver-server.sh',

t/t9305-fast-import-signatures.sh

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

0 commit comments

Comments
 (0)