@@ -35,8 +35,11 @@ static const char *fast_export_usage[] = {
35
35
NULL
36
36
};
37
37
38
+ enum sign_mode { SIGN_ABORT , SIGN_VERBATIM , SIGN_STRIP , SIGN_WARN_VERBATIM , SIGN_WARN_STRIP };
39
+
38
40
static int progress ;
39
- static enum signed_tag_mode { SIGNED_TAG_ABORT , VERBATIM , WARN , 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 ;
40
43
static enum tag_of_filtered_mode { TAG_FILTERING_ABORT , DROP , REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT ;
41
44
static enum reencode_mode { REENCODE_ABORT , REENCODE_YES , REENCODE_NO } reencode_mode = REENCODE_ABORT ;
42
45
static int fake_missing_tagger ;
@@ -53,23 +56,24 @@ static int anonymize;
53
56
static struct hashmap anonymized_seeds ;
54
57
static struct revision_sources revision_sources ;
55
58
56
- static int parse_opt_signed_tag_mode (const struct option * opt ,
59
+ static int parse_opt_sign_mode (const struct option * opt ,
57
60
const char * arg , int unset )
58
61
{
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 ;
63
67
else if (!strcmp (arg , "verbatim" ) || !strcmp (arg , "ignore" ))
64
- * val = VERBATIM ;
65
- else if (!strcmp (arg , "warn" ))
66
- * val = WARN ;
68
+ * val = SIGN_VERBATIM ;
69
+ else if (!strcmp (arg , "warn-verbatim" ) || ! strcmp ( arg , "warn " ))
70
+ * val = SIGN_WARN_VERBATIM ;
67
71
else if (!strcmp (arg , "warn-strip" ))
68
- * val = WARN_STRIP ;
72
+ * val = SIGN_WARN_STRIP ;
69
73
else if (!strcmp (arg , "strip" ))
70
- * val = STRIP ;
74
+ * val = SIGN_STRIP ;
71
75
else
72
- return error ("Unknown signed-tags mode: %s" , arg );
76
+ return error ("Unknown %s mode: %s" , opt -> long_name , arg );
73
77
return 0 ;
74
78
}
75
79
@@ -510,21 +514,6 @@ static void show_filemodify(struct diff_queue_struct *q,
510
514
}
511
515
}
512
516
513
- static const char * find_encoding (const char * begin , const char * end )
514
- {
515
- const char * needle = "\nencoding " ;
516
- char * bol , * eol ;
517
-
518
- bol = memmem (begin , end ? end - begin : strlen (begin ),
519
- needle , strlen (needle ));
520
- if (!bol )
521
- return NULL ;
522
- bol += strlen (needle );
523
- eol = strchrnul (bol , '\n' );
524
- * eol = '\0' ;
525
- return bol ;
526
- }
527
-
528
517
static char * anonymize_ref_component (void )
529
518
{
530
519
static int counter ;
@@ -626,13 +615,53 @@ static void anonymize_ident_line(const char **beg, const char **end)
626
615
* end = out -> buf + out -> len ;
627
616
}
628
617
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
+
629
655
static void handle_commit (struct commit * commit , struct rev_info * rev ,
630
656
struct string_list * paths_of_changed_objects )
631
657
{
632
658
int saved_output_format = rev -> diffopt .output_format ;
633
- const char * commit_buffer ;
659
+ const char * commit_buffer , * commit_buffer_cursor ;
634
660
const char * author , * author_end , * committer , * committer_end ;
635
- const char * encoding , * message ;
661
+ const char * encoding = NULL ;
662
+ size_t encoding_len ;
663
+ const char * signature_alg = NULL , * signature = NULL ;
664
+ const char * message ;
636
665
char * reencoded = NULL ;
637
666
struct commit_list * p ;
638
667
const char * refname ;
@@ -641,21 +670,43 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
641
670
rev -> diffopt .output_format = DIFF_FORMAT_CALLBACK ;
642
671
643
672
parse_commit_or_die (commit );
644
- commit_buffer = repo_get_commit_buffer (the_repository , commit , NULL );
645
- author = strstr (commit_buffer , "\nauthor " );
673
+ commit_buffer_cursor = commit_buffer = repo_get_commit_buffer (the_repository , commit , NULL );
674
+
675
+ author = strstr (commit_buffer_cursor , "\nauthor " );
646
676
if (!author )
647
677
die ("could not find author in commit %s" ,
648
678
oid_to_hex (& commit -> object .oid ));
649
679
author ++ ;
650
- author_end = strchrnul (author , '\n' );
651
- committer = strstr (author_end , "\ncommitter " );
680
+ commit_buffer_cursor = author_end = strchrnul (author , '\n' );
681
+
682
+ committer = strstr (commit_buffer_cursor , "\ncommitter " );
652
683
if (!committer )
653
684
die ("could not find committer in commit %s" ,
654
685
oid_to_hex (& commit -> object .oid ));
655
686
committer ++ ;
656
- committer_end = strchrnul (committer , '\n' );
657
- message = strstr (committer_end , "\n\n" );
658
- encoding = find_encoding (committer_end , message );
687
+ commit_buffer_cursor = committer_end = strchrnul (committer , '\n' );
688
+
689
+ /*
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
693
+ * pointer to the beginning of the next line.
694
+ */
695
+
696
+ if (* commit_buffer_cursor == '\n' ) {
697
+ encoding = find_commit_header (commit_buffer_cursor + 1 , "encoding" , & encoding_len );
698
+ if (encoding )
699
+ commit_buffer_cursor = encoding + encoding_len ;
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" );
659
710
if (message )
660
711
message += 2 ;
661
712
@@ -694,16 +745,20 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
694
745
if (anonymize ) {
695
746
reencoded = anonymize_commit_message ();
696
747
} else if (encoding ) {
697
- switch (reencode_mode ) {
748
+ char * buf ;
749
+ switch (reencode_mode ) {
698
750
case REENCODE_YES :
699
- reencoded = reencode_string (message , "UTF-8" , encoding );
751
+ buf = xstrfmt ("%.*s" , (int )encoding_len , encoding );
752
+ reencoded = reencode_string (message , "UTF-8" , buf );
753
+ free (buf );
700
754
break ;
701
755
case REENCODE_NO :
702
756
break ;
703
757
case REENCODE_ABORT :
704
- die ("Encountered commit-specific encoding %s in commit "
758
+ die ("Encountered commit-specific encoding %.* s in commit "
705
759
"%s; use --reencode=[yes|no] to handle it" ,
706
- encoding , oid_to_hex (& commit -> object .oid ));
760
+ (int )encoding_len , encoding ,
761
+ oid_to_hex (& commit -> object .oid ));
707
762
}
708
763
}
709
764
if (!commit -> parents )
@@ -714,8 +769,33 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
714
769
printf ("%.*s\n%.*s\n" ,
715
770
(int )(author_end - author ), author ,
716
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
+ }
717
797
if (!reencoded && encoding )
718
- printf ("encoding %s\n" , encoding );
798
+ printf ("encoding %.* s\n" , ( int ) encoding_len , encoding );
719
799
printf ("data %u\n%s" ,
720
800
(unsigned )(reencoded
721
801
? strlen (reencoded ) : message
@@ -828,22 +908,22 @@ static void handle_tag(const char *name, struct tag *tag)
828
908
const char * signature = strstr (message ,
829
909
"\n-----BEGIN PGP SIGNATURE-----\n" );
830
910
if (signature )
831
- switch (signed_tag_mode ) {
832
- case SIGNED_TAG_ABORT :
911
+ switch (signed_tag_mode ) {
912
+ case SIGN_ABORT :
833
913
die ("encountered signed tag %s; use "
834
914
"--signed-tags=<mode> to handle it" ,
835
915
oid_to_hex (& tag -> object .oid ));
836
- case WARN :
916
+ case SIGN_WARN_VERBATIM :
837
917
warning ("exporting signed tag %s" ,
838
918
oid_to_hex (& tag -> object .oid ));
839
919
/* fallthru */
840
- case VERBATIM :
920
+ case SIGN_VERBATIM :
841
921
break ;
842
- case WARN_STRIP :
922
+ case SIGN_WARN_STRIP :
843
923
warning ("stripping signature from tag %s" ,
844
924
oid_to_hex (& tag -> object .oid ));
845
925
/* fallthru */
846
- case STRIP :
926
+ case SIGN_STRIP :
847
927
message_size = signature + 1 - message ;
848
928
break ;
849
929
}
@@ -853,7 +933,7 @@ static void handle_tag(const char *name, struct tag *tag)
853
933
tagged = tag -> tagged ;
854
934
tagged_mark = get_object_mark (tagged );
855
935
if (!tagged_mark ) {
856
- switch (tag_of_filtered_mode ) {
936
+ switch (tag_of_filtered_mode ) {
857
937
case TAG_FILTERING_ABORT :
858
938
die ("tag %s tags unexported object; use "
859
939
"--tag-of-filtered-object=<mode> to handle it" ,
@@ -965,7 +1045,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
965
1045
continue ;
966
1046
}
967
1047
968
- switch (commit -> object .type ) {
1048
+ switch (commit -> object .type ) {
969
1049
case OBJ_COMMIT :
970
1050
break ;
971
1051
case OBJ_BLOB :
@@ -1189,6 +1269,7 @@ int cmd_fast_export(int argc,
1189
1269
const char * prefix ,
1190
1270
struct repository * repo UNUSED )
1191
1271
{
1272
+ const char * env_signed_commits_noabort ;
1192
1273
struct rev_info revs ;
1193
1274
struct commit * commit ;
1194
1275
char * export_filename = NULL ,
@@ -1202,7 +1283,10 @@ int cmd_fast_export(int argc,
1202
1283
N_ ("show progress after <n> objects" )),
1203
1284
OPT_CALLBACK (0 , "signed-tags" , & signed_tag_mode , N_ ("mode" ),
1204
1285
N_ ("select handling of signed tags" ),
1205
- 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 ),
1206
1290
OPT_CALLBACK (0 , "tag-of-filtered-object" , & tag_of_filtered_mode , N_ ("mode" ),
1207
1291
N_ ("select handling of tags that tag filtered objects" ),
1208
1292
parse_opt_tag_of_filtered_mode ),
@@ -1243,6 +1327,10 @@ int cmd_fast_export(int argc,
1243
1327
if (argc == 1 )
1244
1328
usage_with_options (fast_export_usage , options );
1245
1329
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
+
1246
1334
/* we handle encodings */
1247
1335
git_config (git_default_config , NULL );
1248
1336
0 commit comments