Git fork

Merge branch 'cc/fast-import-export-signature-names'

Clean up the way how signature on commit objects are exported to
and imported from fast-import stream.

* cc/fast-import-export-signature-names:
fast-(import|export): improve on commit signature output format

+312 -44
+17
Documentation/git-fast-export.adoc
··· 50 is the same as how earlier versions of this command without 51 this option behaved. 52 + 53 NOTE: This is highly experimental and the format of the data stream may 54 change in the future without compatibility guarantees. 55
··· 50 is the same as how earlier versions of this command without 51 this option behaved. 52 + 53 + When exported, a signature starts with: 54 + + 55 + gpgsig <git-hash-algo> <signature-format> 56 + + 57 + where <git-hash-algo> is the Git object hash so either "sha1" or 58 + "sha256", and <signature-format> is the signature type, so "openpgp", 59 + "x509", "ssh" or "unknown". 60 + + 61 + For example, an OpenPGP signature on a SHA-1 commit starts with 62 + `gpgsig sha1 openpgp`, while an SSH signature on a SHA-256 commit 63 + starts with `gpgsig sha256 ssh`. 64 + + 65 + While all the signatures of a commit are exported, an importer may 66 + choose to accept only some of them. For example 67 + linkgit:git-fast-import[1] currently stores at most one signature per 68 + Git hash algorithm in each commit. 69 + + 70 NOTE: This is highly experimental and the format of the data stream may 71 change in the future without compatibility guarantees. 72
+32 -6
Documentation/git-fast-import.adoc
··· 445 original-oid? 446 ('author' (SP <name>)? SP LT <email> GT SP <when> LF)? 447 'committer' (SP <name>)? SP LT <email> GT SP <when> LF 448 - ('gpgsig' SP <alg> LF data)? 449 ('encoding' SP <encoding> LF)? 450 data 451 ('from' SP <commit-ish> LF)? ··· 518 ^^^^^^^^ 519 520 The optional `gpgsig` command is used to include a PGP/GPG signature 521 - that signs the commit data. 522 523 - Here <alg> specifies which hashing algorithm is used for this 524 - signature, either `sha1` or `sha256`. 525 526 - NOTE: This is highly experimental and the format of the data stream may 527 - change in the future without compatibility guarantees. 528 529 `encoding` 530 ^^^^^^^^^^
··· 445 original-oid? 446 ('author' (SP <name>)? SP LT <email> GT SP <when> LF)? 447 'committer' (SP <name>)? SP LT <email> GT SP <when> LF 448 + ('gpgsig' SP <algo> SP <format> LF data)? 449 ('encoding' SP <encoding> LF)? 450 data 451 ('from' SP <commit-ish> LF)? ··· 518 ^^^^^^^^ 519 520 The optional `gpgsig` command is used to include a PGP/GPG signature 521 + or other cryptographic signature that signs the commit data. 522 523 + .... 524 + 'gpgsig' SP <git-hash-algo> SP <signature-format> LF data 525 + .... 526 527 + The `gpgsig` command takes two arguments: 528 + 529 + * `<git-hash-algo>` specifies which Git object format this signature 530 + applies to, either `sha1` or `sha256`. This allows to know which 531 + representation of the commit was signed (the SHA-1 or the SHA-256 532 + version) which helps with both signature verification and 533 + interoperability between repos with different hash functions. 534 + 535 + * `<signature-format>` specifies the type of signature, such as 536 + `openpgp`, `x509`, `ssh`, or `unknown`. This is a convenience for 537 + tools that process the stream, so they don't have to parse the ASCII 538 + armor to identify the signature type. 539 + 540 + A commit may have at most one signature for the SHA-1 object format 541 + (stored in the "gpgsig" header) and one for the SHA-256 object format 542 + (stored in the "gpgsig-sha256" header). 543 + 544 + See below for a detailed description of the `data` command which 545 + contains the raw signature data. 546 + 547 + Signatures are not yet checked in the current implementation 548 + though. (Already setting the `extensions.compatObjectFormat` 549 + configuration option might help with verifying both SHA-1 and SHA-256 550 + object format signatures when it will be implemented.) 551 + 552 + NOTE: This is highly experimental and the format of the `gpgsig` 553 + command may change in the future without compatibility guarantees. 554 555 `encoding` 556 ^^^^^^^^^^
+48 -14
builtin/fast-export.c
··· 29 #include "quote.h" 30 #include "remote.h" 31 #include "blob.h" 32 33 static const char *const fast_export_usage[] = { 34 N_("git fast-export [<rev-list-opts>]"), ··· 652 return strbuf_detach(&val, NULL); 653 } 654 655 static void handle_commit(struct commit *commit, struct rev_info *rev, 656 struct string_list *paths_of_changed_objects) 657 { ··· 660 const char *author, *author_end, *committer, *committer_end; 661 const char *encoding = NULL; 662 size_t encoding_len; 663 - const char *signature_alg = NULL, *signature = NULL; 664 const char *message; 665 char *reencoded = NULL; 666 struct commit_list *p; ··· 700 } 701 702 if (*commit_buffer_cursor == '\n') { 703 - if ((signature = find_commit_multiline_header(commit_buffer_cursor + 1, "gpgsig", &commit_buffer_cursor))) 704 - signature_alg = "sha1"; 705 - else if ((signature = find_commit_multiline_header(commit_buffer_cursor + 1, "gpgsig-sha256", &commit_buffer_cursor))) 706 - signature_alg = "sha256"; 707 } 708 709 message = strstr(commit_buffer_cursor, "\n\n"); ··· 769 printf("%.*s\n%.*s\n", 770 (int)(author_end - author), author, 771 (int)(committer_end - committer), committer); 772 - if (signature) { 773 switch (signed_commit_mode) { 774 case SIGN_ABORT: 775 die("encountered signed commit %s; use " 776 "--signed-commits=<mode> to handle it", 777 oid_to_hex(&commit->object.oid)); 778 case SIGN_WARN_VERBATIM: 779 - warning("exporting signed commit %s", 780 - oid_to_hex(&commit->object.oid)); 781 /* fallthru */ 782 case SIGN_VERBATIM: 783 - printf("gpgsig %s\ndata %u\n%s", 784 - signature_alg, 785 - (unsigned)strlen(signature), 786 - signature); 787 break; 788 case SIGN_WARN_STRIP: 789 - warning("stripping signature from commit %s", 790 oid_to_hex(&commit->object.oid)); 791 /* fallthru */ 792 case SIGN_STRIP: 793 break; 794 } 795 - free((char *)signature); 796 } 797 if (!reencoded && encoding) 798 printf("encoding %.*s\n", (int)encoding_len, encoding);
··· 29 #include "quote.h" 30 #include "remote.h" 31 #include "blob.h" 32 + #include "gpg-interface.h" 33 34 static const char *const fast_export_usage[] = { 35 N_("git fast-export [<rev-list-opts>]"), ··· 653 return strbuf_detach(&val, NULL); 654 } 655 656 + static void print_signature(const char *signature, const char *object_hash) 657 + { 658 + if (!signature) 659 + return; 660 + 661 + printf("gpgsig %s %s\ndata %u\n%s\n", 662 + object_hash, 663 + get_signature_format(signature), 664 + (unsigned)strlen(signature), 665 + signature); 666 + } 667 + 668 + static const char *append_signatures_for_header(struct string_list *signatures, 669 + const char *pos, 670 + const char *header, 671 + const char *object_hash) 672 + { 673 + const char *signature; 674 + const char *start = pos; 675 + const char *end = pos; 676 + 677 + while ((signature = find_commit_multiline_header(start + 1, 678 + header, 679 + &end))) { 680 + string_list_append(signatures, signature)->util = (void *)object_hash; 681 + free((char *)signature); 682 + start = end; 683 + } 684 + 685 + return end; 686 + } 687 + 688 static void handle_commit(struct commit *commit, struct rev_info *rev, 689 struct string_list *paths_of_changed_objects) 690 { ··· 693 const char *author, *author_end, *committer, *committer_end; 694 const char *encoding = NULL; 695 size_t encoding_len; 696 + struct string_list signatures = STRING_LIST_INIT_DUP; 697 const char *message; 698 char *reencoded = NULL; 699 struct commit_list *p; ··· 733 } 734 735 if (*commit_buffer_cursor == '\n') { 736 + const char *after_sha1 = append_signatures_for_header(&signatures, commit_buffer_cursor, 737 + "gpgsig", "sha1"); 738 + const char *after_sha256 = append_signatures_for_header(&signatures, commit_buffer_cursor, 739 + "gpgsig-sha256", "sha256"); 740 + commit_buffer_cursor = (after_sha1 > after_sha256) ? after_sha1 : after_sha256; 741 } 742 743 message = strstr(commit_buffer_cursor, "\n\n"); ··· 803 printf("%.*s\n%.*s\n", 804 (int)(author_end - author), author, 805 (int)(committer_end - committer), committer); 806 + if (signatures.nr) { 807 switch (signed_commit_mode) { 808 case SIGN_ABORT: 809 die("encountered signed commit %s; use " 810 "--signed-commits=<mode> to handle it", 811 oid_to_hex(&commit->object.oid)); 812 case SIGN_WARN_VERBATIM: 813 + warning("exporting %"PRIuMAX" signature(s) for commit %s", 814 + (uintmax_t)signatures.nr, oid_to_hex(&commit->object.oid)); 815 /* fallthru */ 816 case SIGN_VERBATIM: 817 + for (size_t i = 0; i < signatures.nr; i++) { 818 + struct string_list_item *item = &signatures.items[i]; 819 + print_signature(item->string, item->util); 820 + } 821 break; 822 case SIGN_WARN_STRIP: 823 + warning("stripping signature(s) from commit %s", 824 oid_to_hex(&commit->object.oid)); 825 /* fallthru */ 826 case SIGN_STRIP: 827 break; 828 } 829 + string_list_clear(&signatures, 0); 830 } 831 if (!reencoded && encoding) 832 printf("encoding %.*s\n", (int)encoding_len, encoding);
+91 -22
builtin/fast-import.c
··· 29 #include "commit-reach.h" 30 #include "khash.h" 31 #include "date.h" 32 33 #define PACK_ID_BITS 16 34 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1) ··· 2716 return list; 2717 } 2718 2719 static void parse_new_commit(const char *arg) 2720 { 2721 - static struct strbuf sig = STRBUF_INIT; 2722 static struct strbuf msg = STRBUF_INIT; 2723 - struct string_list siglines = STRING_LIST_INIT_NODUP; 2724 struct branch *b; 2725 char *author = NULL; 2726 char *committer = NULL; 2727 - char *sig_alg = NULL; 2728 char *encoding = NULL; 2729 struct hash_list *merge_list = NULL; 2730 unsigned int merge_count; ··· 2748 } 2749 if (!committer) 2750 die("Expected committer but didn't get one"); 2751 - if (skip_prefix(command_buf.buf, "gpgsig ", &v)) { 2752 - sig_alg = xstrdup(v); 2753 read_next_command(); 2754 - parse_data(&sig, 0, NULL); 2755 - read_next_command(); 2756 - } else 2757 - strbuf_setlen(&sig, 0); 2758 if (skip_prefix(command_buf.buf, "encoding ", &v)) { 2759 encoding = xstrdup(v); 2760 read_next_command(); ··· 2828 strbuf_addf(&new_data, 2829 "encoding %s\n", 2830 encoding); 2831 - if (sig_alg) { 2832 - if (!strcmp(sig_alg, "sha1")) 2833 - strbuf_addstr(&new_data, "gpgsig "); 2834 - else if (!strcmp(sig_alg, "sha256")) 2835 - strbuf_addstr(&new_data, "gpgsig-sha256 "); 2836 - else 2837 - die("Expected gpgsig algorithm sha1 or sha256, got %s", sig_alg); 2838 - string_list_split_in_place(&siglines, sig.buf, "\n", -1); 2839 - strbuf_add_separated_string_list(&new_data, "\n ", &siglines); 2840 - strbuf_addch(&new_data, '\n'); 2841 - } 2842 strbuf_addch(&new_data, '\n'); 2843 strbuf_addbuf(&new_data, &msg); 2844 - string_list_clear(&siglines, 1); 2845 free(author); 2846 free(committer); 2847 - free(sig_alg); 2848 free(encoding); 2849 2850 if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark))
··· 29 #include "commit-reach.h" 30 #include "khash.h" 31 #include "date.h" 32 + #include "gpg-interface.h" 33 34 #define PACK_ID_BITS 16 35 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1) ··· 2717 return list; 2718 } 2719 2720 + struct signature_data { 2721 + char *hash_algo; /* "sha1" or "sha256" */ 2722 + char *sig_format; /* "openpgp", "x509", "ssh", or "unknown" */ 2723 + struct strbuf data; /* The actual signature data */ 2724 + }; 2725 + 2726 + static void parse_one_signature(struct signature_data *sig, const char *v) 2727 + { 2728 + char *args = xstrdup(v); /* Will be freed when sig->hash_algo is freed */ 2729 + char *space = strchr(args, ' '); 2730 + 2731 + if (!space) 2732 + die("Expected gpgsig format: 'gpgsig <hash-algo> <signature-format>', " 2733 + "got 'gpgsig %s'", args); 2734 + *space = '\0'; 2735 + 2736 + sig->hash_algo = args; 2737 + sig->sig_format = space + 1; 2738 + 2739 + /* Validate hash algorithm */ 2740 + if (strcmp(sig->hash_algo, "sha1") && 2741 + strcmp(sig->hash_algo, "sha256")) 2742 + die("Unknown git hash algorithm in gpgsig: '%s'", sig->hash_algo); 2743 + 2744 + /* Validate signature format */ 2745 + if (!valid_signature_format(sig->sig_format)) 2746 + die("Invalid signature format in gpgsig: '%s'", sig->sig_format); 2747 + if (!strcmp(sig->sig_format, "unknown")) 2748 + warning("'unknown' signature format in gpgsig"); 2749 + 2750 + /* Read signature data */ 2751 + read_next_command(); 2752 + parse_data(&sig->data, 0, NULL); 2753 + } 2754 + 2755 + static void add_gpgsig_to_commit(struct strbuf *commit_data, 2756 + const char *header, 2757 + struct signature_data *sig) 2758 + { 2759 + struct string_list siglines = STRING_LIST_INIT_NODUP; 2760 + 2761 + if (!sig->hash_algo) 2762 + return; 2763 + 2764 + strbuf_addstr(commit_data, header); 2765 + string_list_split_in_place(&siglines, sig->data.buf, "\n", -1); 2766 + strbuf_add_separated_string_list(commit_data, "\n ", &siglines); 2767 + strbuf_addch(commit_data, '\n'); 2768 + string_list_clear(&siglines, 1); 2769 + strbuf_release(&sig->data); 2770 + free(sig->hash_algo); 2771 + } 2772 + 2773 + static void store_signature(struct signature_data *stored_sig, 2774 + struct signature_data *new_sig, 2775 + const char *hash_type) 2776 + { 2777 + if (stored_sig->hash_algo) { 2778 + warning("multiple %s signatures found, " 2779 + "ignoring additional signature", 2780 + hash_type); 2781 + strbuf_release(&new_sig->data); 2782 + free(new_sig->hash_algo); 2783 + } else { 2784 + *stored_sig = *new_sig; 2785 + } 2786 + } 2787 + 2788 static void parse_new_commit(const char *arg) 2789 { 2790 static struct strbuf msg = STRBUF_INIT; 2791 + struct signature_data sig_sha1 = { NULL, NULL, STRBUF_INIT }; 2792 + struct signature_data sig_sha256 = { NULL, NULL, STRBUF_INIT }; 2793 struct branch *b; 2794 char *author = NULL; 2795 char *committer = NULL; 2796 char *encoding = NULL; 2797 struct hash_list *merge_list = NULL; 2798 unsigned int merge_count; ··· 2816 } 2817 if (!committer) 2818 die("Expected committer but didn't get one"); 2819 + 2820 + /* Process signatures (up to 2: one "sha1" and one "sha256") */ 2821 + while (skip_prefix(command_buf.buf, "gpgsig ", &v)) { 2822 + struct signature_data sig = { NULL, NULL, STRBUF_INIT }; 2823 + 2824 + parse_one_signature(&sig, v); 2825 + 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"); 2832 + 2833 read_next_command(); 2834 + } 2835 + 2836 if (skip_prefix(command_buf.buf, "encoding ", &v)) { 2837 encoding = xstrdup(v); 2838 read_next_command(); ··· 2906 strbuf_addf(&new_data, 2907 "encoding %s\n", 2908 encoding); 2909 + 2910 + add_gpgsig_to_commit(&new_data, "gpgsig ", &sig_sha1); 2911 + add_gpgsig_to_commit(&new_data, "gpgsig-sha256 ", &sig_sha256); 2912 + 2913 strbuf_addch(&new_data, '\n'); 2914 strbuf_addbuf(&new_data, &msg); 2915 free(author); 2916 free(committer); 2917 free(encoding); 2918 2919 if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark))
+12
gpg-interface.c
··· 144 return NULL; 145 } 146 147 void signature_check_clear(struct signature_check *sigc) 148 { 149 FREE_AND_NULL(sigc->payload);
··· 144 return NULL; 145 } 146 147 + const char *get_signature_format(const char *buf) 148 + { 149 + struct gpg_format *format = get_format_by_sig(buf); 150 + return format ? format->name : "unknown"; 151 + } 152 + 153 + int valid_signature_format(const char *format) 154 + { 155 + return (!!get_format_by_name(format) || 156 + !strcmp(format, "unknown")); 157 + } 158 + 159 void signature_check_clear(struct signature_check *sigc) 160 { 161 FREE_AND_NULL(sigc->payload);
+12
gpg-interface.h
··· 48 void signature_check_clear(struct signature_check *sigc); 49 50 /* 51 * Look at a GPG signed tag object. If such a signature exists, store it in 52 * signature and the signed content in payload. Return 1 if a signature was 53 * found, and 0 otherwise.
··· 48 void signature_check_clear(struct signature_check *sigc); 49 50 /* 51 + * Return the format of the signature (like "openpgp", "x509", "ssh" 52 + * or "unknown"). 53 + */ 54 + const char *get_signature_format(const char *buf); 55 + 56 + /* 57 + * Is the signature format valid (like "openpgp", "x509", "ssh" or 58 + * "unknown") 59 + */ 60 + int valid_signature_format(const char *format); 61 + 62 + /* 63 * Look at a GPG signed tag object. If such a signature exists, store it in 64 * signature and the signed content in payload. Return 1 if a signature was 65 * found, and 0 otherwise.
+100 -2
t/t9350-fast-export.sh
··· 314 test_expect_success GPG 'signed-commits=verbatim' ' 315 316 git fast-export --signed-commits=verbatim --reencode=no commit-signing >output && 317 - grep "^gpgsig sha" output && 318 grep "encoding ISO-8859-1" output && 319 ( 320 cd new && ··· 328 test_expect_success GPG 'signed-commits=warn-verbatim' ' 329 330 git fast-export --signed-commits=warn-verbatim --reencode=no commit-signing >output 2>err && 331 - grep "^gpgsig sha" output && 332 grep "encoding ISO-8859-1" output && 333 test -s err && 334 ( ··· 366 STRIPPED=$(git rev-parse --verify refs/heads/commit-strip-signing) && 367 test $COMMIT_SIGNING != $STRIPPED 368 ) 369 370 ' 371 ··· 903 # fix up lines which mention the ref for comparison 904 sed s/--dashes/nodash/ <actual.raw >actual && 905 test_cmp expect actual 906 ' 907 908 test_done
··· 314 test_expect_success GPG 'signed-commits=verbatim' ' 315 316 git fast-export --signed-commits=verbatim --reencode=no commit-signing >output && 317 + test_grep -E "^gpgsig $GIT_DEFAULT_HASH openpgp" output && 318 grep "encoding ISO-8859-1" output && 319 ( 320 cd new && ··· 328 test_expect_success GPG 'signed-commits=warn-verbatim' ' 329 330 git fast-export --signed-commits=warn-verbatim --reencode=no commit-signing >output 2>err && 331 + test_grep -E "^gpgsig $GIT_DEFAULT_HASH openpgp" output && 332 grep "encoding ISO-8859-1" output && 333 test -s err && 334 ( ··· 366 STRIPPED=$(git rev-parse --verify refs/heads/commit-strip-signing) && 367 test $COMMIT_SIGNING != $STRIPPED 368 ) 369 + 370 + ' 371 + 372 + test_expect_success GPGSM 'setup X.509 signed commit' ' 373 + 374 + git checkout -b x509-signing main && 375 + test_config gpg.format x509 && 376 + test_config user.signingkey $GIT_COMMITTER_EMAIL && 377 + echo "X.509 content" >file && 378 + git add file && 379 + git commit -S -m "X.509 signed commit" && 380 + X509_COMMIT=$(git rev-parse HEAD) && 381 + git checkout main 382 + 383 + ' 384 + 385 + test_expect_success GPGSM 'round-trip X.509 signed commit' ' 386 + 387 + git fast-export --signed-commits=verbatim x509-signing >output && 388 + test_grep -E "^gpgsig $GIT_DEFAULT_HASH x509" output && 389 + ( 390 + cd new && 391 + git fast-import && 392 + git cat-file commit refs/heads/x509-signing >actual && 393 + grep "^gpgsig" actual && 394 + IMPORTED=$(git rev-parse refs/heads/x509-signing) && 395 + test $X509_COMMIT = $IMPORTED 396 + ) <output 397 + 398 + ' 399 + 400 + test_expect_success GPGSSH 'setup SSH signed commit' ' 401 + 402 + git checkout -b ssh-signing main && 403 + test_config gpg.format ssh && 404 + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && 405 + echo "SSH content" >file && 406 + git add file && 407 + git commit -S -m "SSH signed commit" && 408 + SSH_COMMIT=$(git rev-parse HEAD) && 409 + git checkout main 410 + 411 + ' 412 + 413 + test_expect_success GPGSSH 'round-trip SSH signed commit' ' 414 + 415 + git fast-export --signed-commits=verbatim ssh-signing >output && 416 + test_grep -E "^gpgsig $GIT_DEFAULT_HASH ssh" output && 417 + ( 418 + cd new && 419 + git fast-import && 420 + git cat-file commit refs/heads/ssh-signing >actual && 421 + grep "^gpgsig" actual && 422 + IMPORTED=$(git rev-parse refs/heads/ssh-signing) && 423 + test $SSH_COMMIT = $IMPORTED 424 + ) <output 425 426 ' 427 ··· 959 # fix up lines which mention the ref for comparison 960 sed s/--dashes/nodash/ <actual.raw >actual && 961 test_cmp expect actual 962 + ' 963 + 964 + test_expect_success GPG 'setup a commit with dual signatures on its SHA-1 and SHA-256 formats' ' 965 + # Create a signed SHA-256 commit 966 + git init --object-format=sha256 explicit-sha256 && 967 + git -C explicit-sha256 config extensions.compatObjectFormat sha1 && 968 + git -C explicit-sha256 checkout -b dual-signed && 969 + test_commit -C explicit-sha256 A && 970 + echo B >explicit-sha256/B && 971 + git -C explicit-sha256 add B && 972 + test_tick && 973 + git -C explicit-sha256 commit -S -m "signed" B && 974 + SHA256_B=$(git -C explicit-sha256 rev-parse dual-signed) && 975 + 976 + # Create the corresponding SHA-1 commit 977 + SHA1_B=$(git -C explicit-sha256 rev-parse --output-object-format=sha1 dual-signed) && 978 + 979 + # Check that the resulting SHA-1 commit has both signatures 980 + echo $SHA1_B | git -C explicit-sha256 cat-file --batch >out && 981 + test_grep -E "^gpgsig " out && 982 + test_grep -E "^gpgsig-sha256 " out 983 + ' 984 + 985 + test_expect_success GPG 'export and import of doubly signed commit' ' 986 + git -C explicit-sha256 fast-export --signed-commits=verbatim dual-signed >output && 987 + test_grep -E "^gpgsig sha1 openpgp" output && 988 + test_grep -E "^gpgsig sha256 openpgp" output && 989 + 990 + ( 991 + cd new && 992 + git fast-import && 993 + git cat-file commit refs/heads/dual-signed >actual && 994 + test_grep -E "^gpgsig " actual && 995 + test_grep -E "^gpgsig-sha256 " actual && 996 + IMPORTED=$(git rev-parse refs/heads/dual-signed) && 997 + if test "$GIT_DEFAULT_HASH" = "sha1" 998 + then 999 + test $SHA1_B = $IMPORTED 1000 + else 1001 + test $SHA256_B = $IMPORTED 1002 + fi 1003 + ) <output 1004 ' 1005 1006 test_done