@@ -36,6 +36,171 @@ static size_t find_end_of_line(char *buffer, unsigned long size)
36
36
return eol + 1 - buffer ;
37
37
}
38
38
39
+ static int read_mbox (const char * path , struct string_list * list )
40
+ {
41
+ struct strbuf buf = STRBUF_INIT , contents = STRBUF_INIT ;
42
+ struct patch_util * util = NULL ;
43
+ int in_header = 1 , in_diff = 0 ;
44
+ char * line , * current_filename = NULL ;
45
+ int offset , len ;
46
+ size_t size ;
47
+ const char * author = NULL , * subject = NULL ;
48
+
49
+ if (!strcmp (path , "-" )) {
50
+ if (strbuf_read (& contents , STDIN_FILENO , 0 ) < 0 )
51
+ return error_errno (_ ("could not read stdin" ));
52
+ } else if (strbuf_read_file (& contents , path , 0 ) < 0 )
53
+ return error_errno (_ ("could not read '%s'" ), path );
54
+
55
+ line = contents .buf ;
56
+ size = contents .len ;
57
+ for (offset = 0 ; size > 0 ; offset += len , size -= len , line += len ) {
58
+ const char * p ;
59
+
60
+ len = find_end_of_line (line , size );
61
+ line [len - 1 ] = '\0' ;
62
+
63
+ if (starts_with (line , "diff --git" )) {
64
+ struct patch patch = { 0 };
65
+ struct strbuf root = STRBUF_INIT ;
66
+ int linenr = 0 ;
67
+ int orig_len ;
68
+
69
+ in_diff = 1 ;
70
+ strbuf_addch (& buf , '\n' );
71
+ if (!util ) {
72
+ util = xcalloc (sizeof (* util ), 1 );
73
+ oidcpy (& util -> oid , & null_oid );
74
+ util -> matching = -1 ;
75
+ author = subject = NULL ;
76
+ }
77
+ if (!util -> diff_offset )
78
+ util -> diff_offset = buf .len ;
79
+ line [len - 1 ] = '\n' ;
80
+ orig_len = len ;
81
+ len = parse_git_diff_header (& root , & linenr , 0 , line ,
82
+ len , size , & patch );
83
+ if (len < 0 ) {
84
+ error (_ ("could not parse git header '%.*s'" ),
85
+ orig_len , line );
86
+ free (util );
87
+ free (current_filename );
88
+ string_list_clear (list , 1 );
89
+ strbuf_release (& buf );
90
+ strbuf_release (& contents );
91
+ return -1 ;
92
+ }
93
+
94
+ if (patch .old_name )
95
+ skip_prefix (patch .old_name , "a/" ,
96
+ (const char * * )& patch .old_name );
97
+ if (patch .new_name )
98
+ skip_prefix (patch .new_name , "b/" ,
99
+ (const char * * )& patch .new_name );
100
+
101
+ strbuf_addstr (& buf , " ## " );
102
+ if (patch .is_new > 0 )
103
+ strbuf_addf (& buf , "%s (new)" , patch .new_name );
104
+ else if (patch .is_delete > 0 )
105
+ strbuf_addf (& buf , "%s (deleted)" , patch .old_name );
106
+ else if (patch .is_rename )
107
+ strbuf_addf (& buf , "%s => %s" , patch .old_name , patch .new_name );
108
+ else
109
+ strbuf_addstr (& buf , patch .new_name );
110
+
111
+ free (current_filename );
112
+ if (patch .is_delete > 0 )
113
+ current_filename = xstrdup (patch .old_name );
114
+ else
115
+ current_filename = xstrdup (patch .new_name );
116
+
117
+ if (patch .new_mode && patch .old_mode &&
118
+ patch .old_mode != patch .new_mode )
119
+ strbuf_addf (& buf , " (mode change %06o => %06o)" ,
120
+ patch .old_mode , patch .new_mode );
121
+
122
+ strbuf_addstr (& buf , " ##\n" );
123
+ util -> diffsize ++ ;
124
+ continue ;
125
+ } else if (in_diff ) {
126
+ switch (line [0 ]) {
127
+ case '\0' :
128
+ continue ; /* ignore empty lines after diff */
129
+ case '+' :
130
+ case '-' :
131
+ case ' ' :
132
+ case '\\' :
133
+ strbuf_addstr (& buf , line );
134
+ strbuf_addch (& buf , '\n' );
135
+ util -> diffsize ++ ;
136
+ continue ;
137
+ case '@' :
138
+ if (skip_prefix (line , "@@ " , & p )) {
139
+ p = strstr (p , "@@" );
140
+ strbuf_addstr (& buf , "@@" );
141
+ if (current_filename && p [2 ])
142
+ strbuf_addf (& buf , " %s:" ,
143
+ current_filename );
144
+ if (p )
145
+ strbuf_addstr (& buf , p + 2 );
146
+
147
+ strbuf_addch (& buf , '\n' );
148
+ util -> diffsize ++ ;
149
+ continue ;
150
+ }
151
+ break ;
152
+ }
153
+
154
+ if (util ) {
155
+ string_list_append (list , buf .buf )-> util = util ;
156
+ strbuf_reset (& buf );
157
+ }
158
+ util = xcalloc (sizeof (* util ), 1 );
159
+ oidcpy (& util -> oid , & null_oid );
160
+ util -> matching = -1 ;
161
+ author = subject = NULL ;
162
+ in_header = 1 ;
163
+ in_diff = 0 ;
164
+ }
165
+
166
+ if (in_header ) {
167
+ if (!line [0 ]) {
168
+ in_header = 0 ;
169
+ strbuf_addstr (& buf , " ## Metadata ##\n" );
170
+ if (author )
171
+ strbuf_addf (& buf , "Author: %s\n" , author );
172
+ strbuf_addstr (& buf , "\n ## Commit message ##\n" );
173
+ if (subject )
174
+ strbuf_addf (& buf , "%s\n" , subject );
175
+ } else if (skip_prefix (line , "From: " , & p )) {
176
+ author = p ;
177
+ } else if (skip_prefix (line , "Subject: " , & p )) {
178
+ const char * q ;
179
+
180
+ subject = p ;
181
+
182
+ if (starts_with (p , "[PATCH" ) &&
183
+ (q = strchr (p , ']' ))) {
184
+ while (isspace (* q ))
185
+ q ++ ;
186
+ subject = q ;
187
+ }
188
+ }
189
+ } else {
190
+ strbuf_addch (& buf , ' ' );
191
+ strbuf_addstr (& buf , line );
192
+ }
193
+ }
194
+ strbuf_release (& contents );
195
+
196
+ if (util )
197
+ string_list_append (list , buf .buf )-> util = util ;
198
+ strbuf_release (& buf );
199
+ free (current_filename );
200
+
201
+ return 0 ;
202
+ }
203
+
39
204
/*
40
205
* Reads the patches into a string list, with the `util` field being populated
41
206
* as struct object_id (will need to be free()d).
@@ -50,6 +215,10 @@ static int read_patches(const char *range, struct string_list *list,
50
215
char * line , * current_filename = NULL ;
51
216
int offset , len ;
52
217
size_t size ;
218
+ const char * path ;
219
+
220
+ if (skip_prefix (range , "mbox:" , & path ))
221
+ return read_mbox (path , list );
53
222
54
223
strvec_pushl (& cp .args , "log" , "--no-color" , "-p" , "--no-merges" ,
55
224
"--reverse" , "--date-order" , "--decorate=no" ,
@@ -534,6 +703,9 @@ int show_range_diff(const char *range1, const char *range2,
534
703
struct string_list branch1 = STRING_LIST_INIT_DUP ;
535
704
struct string_list branch2 = STRING_LIST_INIT_DUP ;
536
705
706
+ if (!strcmp (range1 , "mbox:-" ) && !strcmp (range2 , "mbox:-" ))
707
+ res = error (_ ("only one mbox can be read from stdin" ));
708
+
537
709
if (read_patches (range1 , & branch1 , other_arg ))
538
710
res = error (_ ("could not parse log for '%s'" ), range1 );
539
711
if (!res && read_patches (range2 , & branch2 , other_arg ))
0 commit comments