@@ -26,6 +26,192 @@ struct patch_util {
26
26
struct object_id oid ;
27
27
};
28
28
29
+ static int read_mbox (const char * path , struct string_list * list )
30
+ {
31
+ struct strbuf buf = STRBUF_INIT , contents = STRBUF_INIT ;
32
+ struct patch_util * util = NULL ;
33
+ enum {
34
+ MBOX_BEFORE_HEADER ,
35
+ MBOX_IN_HEADER ,
36
+ MBOX_IN_COMMIT_MESSAGE ,
37
+ MBOX_AFTER_TRIPLE_DASH ,
38
+ MBOX_IN_DIFF
39
+ } state = MBOX_BEFORE_HEADER ;
40
+ char * line , * current_filename = NULL ;
41
+ int offset , len ;
42
+ size_t size ;
43
+ const char * author = NULL , * subject = NULL ;
44
+
45
+ if (!strcmp (path , "-" )) {
46
+ if (strbuf_read (& contents , STDIN_FILENO , 0 ) < 0 )
47
+ return error_errno (_ ("could not read stdin" ));
48
+ } else if (strbuf_read_file (& contents , path , 0 ) < 0 )
49
+ return error_errno (_ ("could not read '%s'" ), path );
50
+
51
+ line = contents .buf ;
52
+ size = contents .len ;
53
+ for (offset = 0 ; size > 0 ; offset += len , size -= len , line += len ) {
54
+ char * eol ;
55
+ const char * p ;
56
+
57
+ eol = memchr (line , '\n' , size );
58
+ if (!eol )
59
+ len = eol - line ;
60
+ else {
61
+ len = eol + 1 - line ;
62
+ if (eol != line && eol [-1 ] == '\r' )
63
+ eol [-1 ] = '\0' ;
64
+ else
65
+ * eol = '\0' ;
66
+ }
67
+
68
+ if (state == MBOX_BEFORE_HEADER ) {
69
+ if (starts_with (line , "From " ))
70
+ state = MBOX_IN_HEADER ;
71
+ else
72
+ continue ;
73
+ }
74
+
75
+ if (starts_with (line , "diff --git " )) {
76
+ struct patch patch = { 0 };
77
+ struct strbuf root = STRBUF_INIT ;
78
+ int linenr = 0 ;
79
+ int orig_len ;
80
+
81
+ state = MBOX_IN_DIFF ;
82
+ strbuf_addch (& buf , '\n' );
83
+ if (!util ) {
84
+ util = xcalloc (sizeof (* util ), 1 );
85
+ oidcpy (& util -> oid , null_oid ());
86
+ util -> matching = -1 ;
87
+ author = subject = NULL ;
88
+ }
89
+ if (!util -> diff_offset )
90
+ util -> diff_offset = buf .len ;
91
+ line [len - 1 ] = '\n' ;
92
+ orig_len = len ;
93
+ len = parse_git_diff_header (& root , & linenr , 0 , line ,
94
+ len , size , & patch );
95
+ if (len < 0 ) {
96
+ error (_ ("could not parse git header '%.*s'" ),
97
+ orig_len , line );
98
+ free (util );
99
+ free (current_filename );
100
+ string_list_clear (list , 1 );
101
+ strbuf_release (& buf );
102
+ strbuf_release (& contents );
103
+ return -1 ;
104
+ }
105
+
106
+ if (patch .old_name )
107
+ skip_prefix (patch .old_name , "a/" ,
108
+ (const char * * )& patch .old_name );
109
+ if (patch .new_name )
110
+ skip_prefix (patch .new_name , "b/" ,
111
+ (const char * * )& patch .new_name );
112
+
113
+ strbuf_addstr (& buf , " ## " );
114
+ if (patch .is_new > 0 )
115
+ strbuf_addf (& buf , "%s (new)" , patch .new_name );
116
+ else if (patch .is_delete > 0 )
117
+ strbuf_addf (& buf , "%s (deleted)" , patch .old_name );
118
+ else if (patch .is_rename )
119
+ strbuf_addf (& buf , "%s => %s" , patch .old_name , patch .new_name );
120
+ else
121
+ strbuf_addstr (& buf , patch .new_name );
122
+
123
+ free (current_filename );
124
+ if (patch .is_delete > 0 )
125
+ current_filename = xstrdup (patch .old_name );
126
+ else
127
+ current_filename = xstrdup (patch .new_name );
128
+
129
+ if (patch .new_mode && patch .old_mode &&
130
+ patch .old_mode != patch .new_mode )
131
+ strbuf_addf (& buf , " (mode change %06o => %06o)" ,
132
+ patch .old_mode , patch .new_mode );
133
+
134
+ strbuf_addstr (& buf , " ##\n" );
135
+ util -> diffsize ++ ;
136
+ } else if (state == MBOX_IN_HEADER ) {
137
+ if (!line [0 ]) {
138
+ state = MBOX_IN_COMMIT_MESSAGE ;
139
+ strbuf_addstr (& buf , " ## Metadata ##\n" );
140
+ if (author )
141
+ strbuf_addf (& buf , "Author: %s\n" , author );
142
+ strbuf_addstr (& buf , "\n ## Commit message ##\n" );
143
+ if (subject )
144
+ strbuf_addf (& buf , " %s\n\n" , subject );
145
+ } else if (skip_prefix (line , "From: " , & p )) {
146
+ author = p ;
147
+ } else if (skip_prefix (line , "Subject: " , & p )) {
148
+ const char * q ;
149
+
150
+ subject = p ;
151
+
152
+ if (starts_with (p , "[PATCH" ) &&
153
+ (q = strchr (p , ']' ))) {
154
+ q ++ ;
155
+ while (isspace (* q ))
156
+ q ++ ;
157
+ subject = q ;
158
+ }
159
+ }
160
+ } else if (state == MBOX_IN_COMMIT_MESSAGE ) {
161
+ if (!strcmp (line , "---" ))
162
+ state = MBOX_AFTER_TRIPLE_DASH ;
163
+ else
164
+ strbuf_addf (& buf , " %s\n" , line );
165
+ } else if (state == MBOX_IN_DIFF ) {
166
+ switch (line [0 ]) {
167
+ case '\0' :
168
+ continue ; /* ignore empty lines after diff */
169
+ case '+' :
170
+ case '-' :
171
+ case ' ' :
172
+ case '\\' :
173
+ strbuf_addstr (& buf , line );
174
+ strbuf_addch (& buf , '\n' );
175
+ util -> diffsize ++ ;
176
+ continue ;
177
+ case '@' :
178
+ if (skip_prefix (line , "@@ " , & p )) {
179
+ p = strstr (p , "@@" );
180
+ strbuf_addstr (& buf , "@@" );
181
+ if (current_filename && p [2 ])
182
+ strbuf_addf (& buf , " %s:" ,
183
+ current_filename );
184
+ if (p )
185
+ strbuf_addstr (& buf , p + 2 );
186
+
187
+ strbuf_addch (& buf , '\n' );
188
+ util -> diffsize ++ ;
189
+ continue ;
190
+ }
191
+ break ;
192
+ }
193
+
194
+ if (util ) {
195
+ string_list_append (list , buf .buf )-> util = util ;
196
+ strbuf_reset (& buf );
197
+ }
198
+ util = xcalloc (sizeof (* util ), 1 );
199
+ oidcpy (& util -> oid , null_oid ());
200
+ util -> matching = -1 ;
201
+ author = subject = NULL ;
202
+ state = MBOX_BEFORE_HEADER ;
203
+ }
204
+ }
205
+ strbuf_release (& contents );
206
+
207
+ if (util )
208
+ string_list_append (list , buf .buf )-> util = util ;
209
+ strbuf_release (& buf );
210
+ free (current_filename );
211
+
212
+ return 0 ;
213
+ }
214
+
29
215
/*
30
216
* Reads the patches into a string list, with the `util` field being populated
31
217
* as struct object_id (will need to be free()d).
@@ -40,6 +226,10 @@ static int read_patches(const char *range, struct string_list *list,
40
226
char * line , * current_filename = NULL ;
41
227
ssize_t len ;
42
228
size_t size ;
229
+ const char * path ;
230
+
231
+ if (skip_prefix (range , "mbox:" , & path ))
232
+ return read_mbox (path , list );
43
233
44
234
strvec_pushl (& cp .args , "log" , "--no-color" , "-p" , "--no-merges" ,
45
235
"--reverse" , "--date-order" , "--decorate=no" ,
@@ -428,6 +618,19 @@ static void output_pair_header(struct diff_options *diffopt,
428
618
429
619
strbuf_addch (buf , ' ' );
430
620
pp_commit_easy (CMIT_FMT_ONELINE , commit , buf );
621
+ } else {
622
+ struct patch_util * util = b_util ? b_util : a_util ;
623
+ const char * needle = "\n ## Commit message ##\n" ;
624
+ const char * p = !util || !util -> patch ?
625
+ NULL : strstr (util -> patch , needle );
626
+ if (p ) {
627
+ if (status == '!' )
628
+ strbuf_addf (buf , "%s%s" , color_reset , color );
629
+
630
+ strbuf_addch (buf , ' ' );
631
+ p += strlen (needle );
632
+ strbuf_add (buf , p , strchrnul (p , '\n' ) - p );
633
+ }
431
634
}
432
635
strbuf_addf (buf , "%s\n" , color_reset );
433
636
@@ -558,6 +761,9 @@ int show_range_diff(const char *range1, const char *range2,
558
761
if (range_diff_opts -> left_only && range_diff_opts -> right_only )
559
762
res = error (_ ("--left-only and --right-only are mutually exclusive" ));
560
763
764
+ if (!strcmp (range1 , "mbox:-" ) && !strcmp (range2 , "mbox:-" ))
765
+ res = error (_ ("only one mbox can be read from stdin" ));
766
+
561
767
if (!res && read_patches (range1 , & branch1 , range_diff_opts -> other_arg ))
562
768
res = error (_ ("could not parse log for '%s'" ), range1 );
563
769
if (!res && read_patches (range2 , & branch2 , range_diff_opts -> other_arg ))
0 commit comments