Git fork

fast-import: add '--signed-tags=<mode>' option

Recently, eaaddf5791 (fast-import: add '--signed-commits=<mode>'
option, 2025-09-17) added support for controlling how signed commits
are handled by `git fast-import`, but there is no option yet to
decide about signed tags.

To remediate that, let's add a '--signed-tags=<mode>' option to
`git fast-import` too.

With this, both `git fast-export` and `git fast-import` have both
a '--signed-tags=<mode>' and a '--signed-commits=<mode>' supporting
the same <mode>s.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Christian Couder and committed by
Junio C Hamano
d8ce08aa 31f375c3

+129
+5
Documentation/git-fast-import.adoc
··· 66 66 remote-helpers that use the `import` capability, as they are 67 67 already trusted to run their own code. 68 68 69 + --signed-tags=(verbatim|warn-verbatim|warn-strip|strip|abort):: 70 + Specify how to handle signed tags. 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 + 69 74 --signed-commits=(verbatim|warn-verbatim|warn-strip|strip|abort):: 70 75 Specify how to handle signed commits. Behaves in the same way 71 76 as the same option in linkgit:git-fast-export[1], except that
+43
builtin/fast-import.c
··· 188 188 static const char **global_argv; 189 189 static const char *global_prefix; 190 190 191 + static enum sign_mode signed_tag_mode = SIGN_VERBATIM; 191 192 static enum sign_mode signed_commit_mode = SIGN_VERBATIM; 192 193 193 194 /* Memory pools */ ··· 2961 2962 b->last_commit = object_count_by_type[OBJ_COMMIT]; 2962 2963 } 2963 2964 2965 + static void handle_tag_signature(struct strbuf *msg, const char *name) 2966 + { 2967 + size_t sig_offset = parse_signed_buffer(msg->buf, msg->len); 2968 + 2969 + /* If there is no signature, there is nothing to do. */ 2970 + if (sig_offset >= msg->len) 2971 + return; 2972 + 2973 + switch (signed_tag_mode) { 2974 + 2975 + /* First, modes that don't change anything */ 2976 + case SIGN_ABORT: 2977 + die(_("encountered signed tag; use " 2978 + "--signed-tags=<mode> to handle it")); 2979 + case SIGN_WARN_VERBATIM: 2980 + warning(_("importing a tag signature verbatim for tag '%s'"), name); 2981 + /* fallthru */ 2982 + case SIGN_VERBATIM: 2983 + /* Nothing to do, the signature will be put into the imported tag. */ 2984 + break; 2985 + 2986 + /* Second, modes that remove the signature */ 2987 + case SIGN_WARN_STRIP: 2988 + warning(_("stripping a tag signature for tag '%s'"), name); 2989 + /* fallthru */ 2990 + case SIGN_STRIP: 2991 + /* Truncate the buffer to remove the signature */ 2992 + strbuf_setlen(msg, sig_offset); 2993 + break; 2994 + 2995 + /* Third, BUG */ 2996 + default: 2997 + BUG("invalid signed_tag_mode value %d from tag '%s'", 2998 + signed_tag_mode, name); 2999 + } 3000 + } 3001 + 2964 3002 static void parse_new_tag(const char *arg) 2965 3003 { 2966 3004 static struct strbuf msg = STRBUF_INIT; ··· 3023 3061 3024 3062 /* tag payload/message */ 3025 3063 parse_data(&msg, 0, NULL); 3064 + 3065 + handle_tag_signature(&msg, t->name); 3026 3066 3027 3067 /* build the tag object */ 3028 3068 strbuf_reset(&new_data); ··· 3544 3584 } else if (skip_prefix(option, "signed-commits=", &option)) { 3545 3585 if (parse_sign_mode(option, &signed_commit_mode)) 3546 3586 usagef(_("unknown --signed-commits mode '%s'"), option); 3587 + } else if (skip_prefix(option, "signed-tags=", &option)) { 3588 + if (parse_sign_mode(option, &signed_tag_mode)) 3589 + usagef(_("unknown --signed-tags mode '%s'"), option); 3547 3590 } else if (!strcmp(option, "quiet")) { 3548 3591 show_stats = 0; 3549 3592 quiet = 1;
+1
t/meson.build
··· 1036 1036 't9303-fast-import-compression.sh', 1037 1037 't9304-fast-import-marks.sh', 1038 1038 't9305-fast-import-signatures.sh', 1039 + 't9306-fast-import-signed-tags.sh', 1039 1040 't9350-fast-export.sh', 1040 1041 't9351-fast-export-anonymize.sh', 1041 1042 't9400-git-cvsserver-server.sh',
+80
t/t9306-fast-import-signed-tags.sh
··· 1 + #!/bin/sh 2 + 3 + test_description='git fast-import --signed-tags=<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 'import no signed tag with --signed-tags=abort' ' 16 + git fast-export --signed-tags=verbatim >output && 17 + git -C new fast-import --quiet --signed-tags=abort <output 18 + ' 19 + 20 + test_expect_success GPG 'set up OpenPGP signed tag' ' 21 + git tag -s -m "OpenPGP signed tag" openpgp-signed first && 22 + OPENPGP_SIGNED=$(git rev-parse --verify refs/tags/openpgp-signed) && 23 + git fast-export --signed-tags=verbatim openpgp-signed >output 24 + ' 25 + 26 + test_expect_success GPG 'import OpenPGP signed tag with --signed-tags=abort' ' 27 + test_must_fail git -C new fast-import --quiet --signed-tags=abort <output 28 + ' 29 + 30 + test_expect_success GPG 'import OpenPGP signed tag with --signed-tags=verbatim' ' 31 + git -C new fast-import --quiet --signed-tags=verbatim <output >log 2>&1 && 32 + IMPORTED=$(git -C new rev-parse --verify refs/tags/openpgp-signed) && 33 + test $OPENPGP_SIGNED = $IMPORTED && 34 + test_must_be_empty log 35 + ' 36 + 37 + test_expect_success GPGSM 'setup X.509 signed tag' ' 38 + test_config gpg.format x509 && 39 + test_config user.signingkey $GIT_COMMITTER_EMAIL && 40 + 41 + git tag -s -m "X.509 signed tag" x509-signed first && 42 + X509_SIGNED=$(git rev-parse --verify refs/tags/x509-signed) && 43 + git fast-export --signed-tags=verbatim x509-signed >output 44 + ' 45 + 46 + test_expect_success GPGSM 'import X.509 signed tag with --signed-tags=warn-strip' ' 47 + git -C new fast-import --quiet --signed-tags=warn-strip <output >log 2>&1 && 48 + test_grep "stripping a tag signature for tag '\''x509-signed'\''" log && 49 + IMPORTED=$(git -C new rev-parse --verify refs/tags/x509-signed) && 50 + test $X509_SIGNED != $IMPORTED && 51 + git -C new cat-file -p x509-signed >out && 52 + test_grep ! "SIGNED MESSAGE" out 53 + ' 54 + 55 + test_expect_success GPGSSH 'setup SSH signed tag' ' 56 + test_config gpg.format ssh && 57 + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && 58 + 59 + git tag -s -m "SSH signed tag" ssh-signed first && 60 + SSH_SIGNED=$(git rev-parse --verify refs/tags/ssh-signed) && 61 + git fast-export --signed-tags=verbatim ssh-signed >output 62 + ' 63 + 64 + test_expect_success GPGSSH 'import SSH signed tag with --signed-tags=warn-verbatim' ' 65 + git -C new fast-import --quiet --signed-tags=warn-verbatim <output >log 2>&1 && 66 + test_grep "importing a tag signature verbatim for tag '\''ssh-signed'\''" log && 67 + IMPORTED=$(git -C new rev-parse --verify refs/tags/ssh-signed) && 68 + test $SSH_SIGNED = $IMPORTED 69 + ' 70 + 71 + test_expect_success GPGSSH 'import SSH signed tag with --signed-tags=strip' ' 72 + git -C new fast-import --quiet --signed-tags=strip <output >log 2>&1 && 73 + test_must_be_empty log && 74 + IMPORTED=$(git -C new rev-parse --verify refs/tags/ssh-signed) && 75 + test $SSH_SIGNED != $IMPORTED && 76 + git -C new cat-file -p ssh-signed >out && 77 + test_grep ! "SSH SIGNATURE" out 78 + ' 79 + 80 + test_done