@@ -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