@@ -35,8 +35,11 @@ static const char *fast_export_usage[] = {
3535 NULL
3636};
3737
38+ enum sign_mode { SIGN_ABORT , SIGN_VERBATIM , SIGN_STRIP , SIGN_WARN_VERBATIM , SIGN_WARN_STRIP };
39+
3840static int progress ;
39- static enum signed_tag_mode { SIGNED_TAG_ABORT , VERBATIM , WARN_VERBATIM , WARN_STRIP , STRIP } signed_tag_mode = SIGNED_TAG_ABORT ;
41+ static enum sign_mode signed_tag_mode = SIGN_ABORT ;
42+ static enum sign_mode signed_commit_mode = SIGN_ABORT ;
4043static enum tag_of_filtered_mode { TAG_FILTERING_ABORT , DROP , REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT ;
4144static enum reencode_mode { REENCODE_ABORT , REENCODE_YES , REENCODE_NO } reencode_mode = REENCODE_ABORT ;
4245static int fake_missing_tagger ;
@@ -53,23 +56,24 @@ static int anonymize;
5356static struct hashmap anonymized_seeds ;
5457static struct revision_sources revision_sources ;
5558
56- static int parse_opt_signed_tag_mode (const struct option * opt ,
59+ static int parse_opt_sign_mode (const struct option * opt ,
5760 const char * arg , int unset )
5861{
59- enum signed_tag_mode * val = opt -> value ;
60-
61- if (unset || !strcmp (arg , "abort" ))
62- * val = SIGNED_TAG_ABORT ;
62+ enum sign_mode * val = opt -> value ;
63+ if (unset )
64+ return 0 ;
65+ else if (!strcmp (arg , "abort" ))
66+ * val = SIGN_ABORT ;
6367 else if (!strcmp (arg , "verbatim" ) || !strcmp (arg , "ignore" ))
64- * val = VERBATIM ;
68+ * val = SIGN_VERBATIM ;
6569 else if (!strcmp (arg , "warn-verbatim" ) || !strcmp (arg , "warn" ))
66- * val = WARN_VERBATIM ;
70+ * val = SIGN_WARN_VERBATIM ;
6771 else if (!strcmp (arg , "warn-strip" ))
68- * val = WARN_STRIP ;
72+ * val = SIGN_WARN_STRIP ;
6973 else if (!strcmp (arg , "strip" ))
70- * val = STRIP ;
74+ * val = SIGN_STRIP ;
7175 else
72- return error ("Unknown signed-tags mode: %s" , arg );
76+ return error ("Unknown %s mode: %s" , opt -> long_name , arg );
7377 return 0 ;
7478}
7579
@@ -611,6 +615,43 @@ static void anonymize_ident_line(const char **beg, const char **end)
611615 * end = out -> buf + out -> len ;
612616}
613617
618+ /*
619+ * find_commit_multiline_header is similar to find_commit_header,
620+ * except that it handles multi-line headers, rather than simply
621+ * returning the first line of the header.
622+ *
623+ * The returned string has had the ' ' line continuation markers
624+ * removed, and points to allocated memory that must be free()d (not
625+ * to memory within 'msg').
626+ *
627+ * If the header is found, then *end is set to point at the '\n' in
628+ * msg that immediately follows the header value.
629+ */
630+ static const char * find_commit_multiline_header (const char * msg ,
631+ const char * key ,
632+ const char * * end )
633+ {
634+ struct strbuf val = STRBUF_INIT ;
635+ const char * bol , * eol ;
636+ size_t len ;
637+
638+ bol = find_commit_header (msg , key , & len );
639+ if (!bol )
640+ return NULL ;
641+ eol = bol + len ;
642+ strbuf_add (& val , bol , len );
643+
644+ while (eol [0 ] == '\n' && eol [1 ] == ' ' ) {
645+ bol = eol + 2 ;
646+ eol = strchrnul (bol , '\n' );
647+ strbuf_addch (& val , '\n' );
648+ strbuf_add (& val , bol , eol - bol );
649+ }
650+
651+ * end = eol ;
652+ return strbuf_detach (& val , NULL );
653+ }
654+
614655static void handle_commit (struct commit * commit , struct rev_info * rev ,
615656 struct string_list * paths_of_changed_objects )
616657{
@@ -619,6 +660,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
619660 const char * author , * author_end , * committer , * committer_end ;
620661 const char * encoding = NULL ;
621662 size_t encoding_len ;
663+ const char * signature_alg = NULL , * signature = NULL ;
622664 const char * message ;
623665 char * reencoded = NULL ;
624666 struct commit_list * p ;
@@ -645,17 +687,25 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
645687 commit_buffer_cursor = committer_end = strchrnul (committer , '\n' );
646688
647689 /*
648- * find_commit_header() gets a `+ 1` because
649- * commit_buffer_cursor points at the trailing "\n" at the end
650- * of the previous line, but find_commit_header() wants a
690+ * find_commit_header() and find_commit_multiline_header() get
691+ * a `+ 1` because commit_buffer_cursor points at the trailing
692+ * "\n" at the end of the previous line, but they want a
651693 * pointer to the beginning of the next line.
652694 */
695+
653696 if (* commit_buffer_cursor == '\n' ) {
654697 encoding = find_commit_header (commit_buffer_cursor + 1 , "encoding" , & encoding_len );
655698 if (encoding )
656699 commit_buffer_cursor = encoding + encoding_len ;
657700 }
658701
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+
659709 message = strstr (commit_buffer_cursor , "\n\n" );
660710 if (message )
661711 message += 2 ;
@@ -719,6 +769,31 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
719769 printf ("%.*s\n%.*s\n" ,
720770 (int )(author_end - author ), author ,
721771 (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+ }
722797 if (!reencoded && encoding )
723798 printf ("encoding %.*s\n" , (int )encoding_len , encoding );
724799 printf ("data %u\n%s" ,
@@ -834,21 +909,21 @@ static void handle_tag(const char *name, struct tag *tag)
834909 "\n-----BEGIN PGP SIGNATURE-----\n" );
835910 if (signature )
836911 switch (signed_tag_mode ) {
837- case SIGNED_TAG_ABORT :
912+ case SIGN_ABORT :
838913 die ("encountered signed tag %s; use "
839914 "--signed-tags=<mode> to handle it" ,
840915 oid_to_hex (& tag -> object .oid ));
841- case WARN_VERBATIM :
916+ case SIGN_WARN_VERBATIM :
842917 warning ("exporting signed tag %s" ,
843918 oid_to_hex (& tag -> object .oid ));
844919 /* fallthru */
845- case VERBATIM :
920+ case SIGN_VERBATIM :
846921 break ;
847- case WARN_STRIP :
922+ case SIGN_WARN_STRIP :
848923 warning ("stripping signature from tag %s" ,
849924 oid_to_hex (& tag -> object .oid ));
850925 /* fallthru */
851- case STRIP :
926+ case SIGN_STRIP :
852927 message_size = signature + 1 - message ;
853928 break ;
854929 }
@@ -1194,6 +1269,7 @@ int cmd_fast_export(int argc,
11941269 const char * prefix ,
11951270 struct repository * repo UNUSED )
11961271{
1272+ const char * env_signed_commits_noabort ;
11971273 struct rev_info revs ;
11981274 struct commit * commit ;
11991275 char * export_filename = NULL ,
@@ -1207,7 +1283,10 @@ int cmd_fast_export(int argc,
12071283 N_ ("show progress after <n> objects" )),
12081284 OPT_CALLBACK (0 , "signed-tags" , & signed_tag_mode , N_ ("mode" ),
12091285 N_ ("select handling of signed tags" ),
1210- parse_opt_signed_tag_mode ),
1286+ parse_opt_sign_mode ),
1287+ OPT_CALLBACK (0 , "signed-commits" , & signed_commit_mode , N_ ("mode" ),
1288+ N_ ("select handling of signed commits" ),
1289+ parse_opt_sign_mode ),
12111290 OPT_CALLBACK (0 , "tag-of-filtered-object" , & tag_of_filtered_mode , N_ ("mode" ),
12121291 N_ ("select handling of tags that tag filtered objects" ),
12131292 parse_opt_tag_of_filtered_mode ),
@@ -1248,6 +1327,10 @@ int cmd_fast_export(int argc,
12481327 if (argc == 1 )
12491328 usage_with_options (fast_export_usage , options );
12501329
1330+ env_signed_commits_noabort = getenv ("FAST_EXPORT_SIGNED_COMMITS_NOABORT" );
1331+ if (env_signed_commits_noabort && * env_signed_commits_noabort )
1332+ signed_commit_mode = SIGN_WARN_STRIP ;
1333+
12511334 /* we handle encodings */
12521335 git_config (git_default_config , NULL );
12531336
0 commit comments