@@ -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,44 @@ 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, rathar 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 statically allocated memory (not to memory 
625+  * within 'msg'), so it is only valid until the next call to 
626+  * find_commit_multiline_header. 
627+  * 
628+  * If the header is found, then *end is set to point at the '\n' in 
629+  * msg that immediately follows the header value. 
630+  */ 
631+ static  const  char  * find_commit_multiline_header (const  char  * msg ,
632+ 						const  char  * key ,
633+ 						const  char  * * end )
634+ {
635+ 	struct  strbuf  val  =  STRBUF_INIT ;
636+ 	const  char  * bol , * eol ;
637+ 	size_t  len ;
638+ 
639+ 	bol  =  find_commit_header (msg , key , & len );
640+ 	if  (!bol )
641+ 		return  NULL ;
642+ 	eol  =  bol  +  len ;
643+ 	strbuf_add (& val , bol , len );
644+ 
645+ 	while  (eol [0 ] ==  '\n'  &&  eol [1 ] ==  ' ' ) {
646+ 		bol  =  eol  +  2 ;
647+ 		eol  =  strchrnul (bol , '\n' );
648+ 		strbuf_addch (& val , '\n' );
649+ 		strbuf_add (& val , bol , eol  -  bol );
650+ 	}
651+ 
652+ 	* end  =  eol ;
653+ 	return  strbuf_detach (& val , NULL );
654+ }
655+ 
614656static  void  handle_commit (struct  commit  * commit , struct  rev_info  * rev ,
615657			  struct  string_list  * paths_of_changed_objects )
616658{
@@ -619,6 +661,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
619661	const  char  * author , * author_end , * committer , * committer_end ;
620662	const  char  * encoding  =  NULL ;
621663	size_t  encoding_len ;
664+ 	const  char  * signature_alg  =  NULL , * signature  =  NULL ;
622665	const  char  * message ;
623666	char  * reencoded  =  NULL ;
624667	struct  commit_list  * p ;
@@ -645,17 +688,25 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
645688	commit_buffer_cursor  =  committer_end  =  strchrnul (committer , '\n' );
646689
647690	/* 
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 
691+ 	 * find_commit_header() and find_commit_multiline_header() get  
692+ 	 * a `+ 1` because commit_buffer_cursor points  at the trailing  
693+ 	 * "\n" at the end  of the previous line, but they want  a 
651694	 * pointer to the beginning of the next line. 
652695	 */ 
696+ 
653697	if  (* commit_buffer_cursor  ==  '\n' ) {
654698		encoding  =  find_commit_header (commit_buffer_cursor  +  1 , "encoding" , & encoding_len );
655699		if  (encoding )
656700			commit_buffer_cursor  =  encoding  +  encoding_len ;
657701	}
658702
703+ 	if  (* commit_buffer_cursor  ==  '\n' ) {
704+ 		if  ((signature  =  find_commit_multiline_header (commit_buffer_cursor  +  1 , "gpgsig" , & commit_buffer_cursor )))
705+ 			signature_alg  =  "sha1" ;
706+ 		else  if  ((signature  =  find_commit_multiline_header (commit_buffer_cursor  +  1 , "gpgsig-sha256" , & commit_buffer_cursor )))
707+ 			signature_alg  =  "sha256" ;
708+ 	}
709+ 
659710	message  =  strstr (commit_buffer_cursor , "\n\n" );
660711	if  (message )
661712		message  +=  2 ;
@@ -719,6 +770,29 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
719770	printf ("%.*s\n%.*s\n" ,
720771	       (int )(author_end  -  author ), author ,
721772	       (int )(committer_end  -  committer ), committer );
773+ 	if  (signature )
774+ 		switch  (signed_commit_mode ) {
775+ 		case  SIGN_ABORT :
776+ 			die ("encountered signed commit %s; use " 
777+ 			    "--signed-commits=<mode> to handle it" ,
778+ 			    oid_to_hex (& commit -> object .oid ));
779+ 		case  SIGN_WARN_VERBATIM :
780+ 			warning ("exporting signed commit %s" ,
781+ 				oid_to_hex (& commit -> object .oid ));
782+ 			/* fallthru */ 
783+ 		case  SIGN_VERBATIM :
784+ 			printf ("gpgsig %s\ndata %u\n%s" ,
785+ 			       signature_alg ,
786+ 			       (unsigned )strlen (signature ),
787+ 			       signature );
788+ 			break ;
789+ 		case  SIGN_WARN_STRIP :
790+ 			warning ("stripping signature from commit %s" ,
791+ 				oid_to_hex (& commit -> object .oid ));
792+ 			/* fallthru */ 
793+ 		case  SIGN_STRIP :
794+ 			break ;
795+ 		}
722796	if  (!reencoded  &&  encoding )
723797		printf ("encoding %.*s\n" , (int )encoding_len , encoding );
724798	printf ("data %u\n%s" ,
@@ -834,21 +908,21 @@ static void handle_tag(const char *name, struct tag *tag)
834908					       "\n-----BEGIN PGP SIGNATURE-----\n" );
835909		if  (signature )
836910			switch  (signed_tag_mode ) {
837- 			case  SIGNED_TAG_ABORT :
911+ 			case  SIGN_ABORT :
838912				die ("encountered signed tag %s; use " 
839913				    "--signed-tags=<mode> to handle it" ,
840914				    oid_to_hex (& tag -> object .oid ));
841- 			case  WARN_VERBATIM :
915+ 			case  SIGN_WARN_VERBATIM :
842916				warning ("exporting signed tag %s" ,
843917					oid_to_hex (& tag -> object .oid ));
844918				/* fallthru */ 
845- 			case  VERBATIM :
919+ 			case  SIGN_VERBATIM :
846920				break ;
847- 			case  WARN_STRIP :
921+ 			case  SIGN_WARN_STRIP :
848922				warning ("stripping signature from tag %s" ,
849923					oid_to_hex (& tag -> object .oid ));
850924				/* fallthru */ 
851- 			case  STRIP :
925+ 			case  SIGN_STRIP :
852926				message_size  =  signature  +  1  -  message ;
853927				break ;
854928			}
@@ -1194,6 +1268,7 @@ int cmd_fast_export(int argc,
11941268		    const  char  * prefix ,
11951269		    struct  repository  * repo  UNUSED )
11961270{
1271+ 	const  char  * env_signed_commits_noabort ;
11971272	struct  rev_info  revs ;
11981273	struct  commit  * commit ;
11991274	char  * export_filename  =  NULL ,
@@ -1207,7 +1282,10 @@ int cmd_fast_export(int argc,
12071282			    N_ ("show progress after <n> objects" )),
12081283		OPT_CALLBACK (0 , "signed-tags" , & signed_tag_mode , N_ ("mode" ),
12091284			     N_ ("select handling of signed tags" ),
1210- 			     parse_opt_signed_tag_mode ),
1285+ 			     parse_opt_sign_mode ),
1286+ 		OPT_CALLBACK (0 , "signed-commits" , & signed_commit_mode , N_ ("mode" ),
1287+ 			     N_ ("select handling of signed commits" ),
1288+ 			     parse_opt_sign_mode ),
12111289		OPT_CALLBACK (0 , "tag-of-filtered-object" , & tag_of_filtered_mode , N_ ("mode" ),
12121290			     N_ ("select handling of tags that tag filtered objects" ),
12131291			     parse_opt_tag_of_filtered_mode ),
@@ -1248,6 +1326,10 @@ int cmd_fast_export(int argc,
12481326	if  (argc  ==  1 )
12491327		usage_with_options  (fast_export_usage , options );
12501328
1329+ 	env_signed_commits_noabort  =  getenv ("FAST_EXPORT_SIGNED_COMMITS_NOABORT" );
1330+ 	if  (env_signed_commits_noabort  &&  * env_signed_commits_noabort )
1331+ 		signed_commit_mode  =  SIGN_WARN_STRIP ;
1332+ 
12511333	/* we handle encodings */ 
12521334	git_config (git_default_config , NULL );
12531335
0 commit comments