@@ -33,7 +33,7 @@ struct add_p_state {
33
33
struct hunk head ;
34
34
struct hunk * hunk ;
35
35
size_t hunk_nr , hunk_alloc ;
36
- unsigned deleted :1 ;
36
+ unsigned deleted :1 , mode_change : 1 ;
37
37
} * file_diff ;
38
38
size_t file_diff_nr ;
39
39
};
@@ -129,6 +129,14 @@ static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk)
129
129
return 0 ;
130
130
}
131
131
132
+ static int is_octal (const char * p , size_t len )
133
+ {
134
+ while (len -- )
135
+ if (* p < '0' || * (p ++ ) > '7' )
136
+ return 0 ;
137
+ return 1 ;
138
+ }
139
+
132
140
static int parse_diff (struct add_p_state * s , const struct pathspec * ps )
133
141
{
134
142
struct argv_array args = ARGV_ARRAY_INIT ;
@@ -181,7 +189,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
181
189
pend = p + plain -> len ;
182
190
while (p != pend ) {
183
191
char * eol = memchr (p , '\n' , pend - p );
184
- const char * deleted = NULL ;
192
+ const char * deleted = NULL , * mode_change = NULL ;
185
193
186
194
if (!eol )
187
195
eol = pend ;
@@ -218,8 +226,30 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
218
226
file_diff -> deleted = 1 ;
219
227
else if (parse_hunk_header (s , hunk ) < 0 )
220
228
return -1 ;
229
+ } else if (hunk == & file_diff -> head &&
230
+ ((skip_prefix (p , "old mode " , & mode_change ) ||
231
+ skip_prefix (p , "new mode " , & mode_change )) &&
232
+ is_octal (mode_change , eol - mode_change ))) {
233
+ if (!file_diff -> mode_change ) {
234
+ if (file_diff -> hunk_nr ++ )
235
+ BUG ("mode change before first hunk" );
236
+ ALLOC_GROW (file_diff -> hunk , file_diff -> hunk_nr ,
237
+ file_diff -> hunk_alloc );
238
+ memset (file_diff -> hunk , 0 , sizeof (struct hunk ));
239
+ file_diff -> hunk -> start = p - plain -> buf ;
240
+ if (colored_p )
241
+ file_diff -> hunk -> colored_start =
242
+ colored_p - colored -> buf ;
243
+ file_diff -> mode_change = 1 ;
244
+ } else if (file_diff -> hunk_nr != 1 )
245
+ BUG ("mode change after first hunk?" );
221
246
}
222
247
248
+ if (file_diff -> deleted && file_diff -> mode_change )
249
+ BUG ("diff contains delete *and* a mode change?!?\n%.*s" ,
250
+ (int )(eol - (plain -> buf + file_diff -> head .start )),
251
+ plain -> buf + file_diff -> head .start );
252
+
223
253
p = eol == pend ? pend : eol + 1 ;
224
254
hunk -> end = p - plain -> buf ;
225
255
@@ -233,6 +263,13 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
233
263
234
264
hunk -> colored_end = colored_p - colored -> buf ;
235
265
}
266
+
267
+ if (mode_change ) {
268
+ file_diff -> hunk -> end = hunk -> end ;
269
+ if (colored_p )
270
+ file_diff -> hunk -> colored_end =
271
+ hunk -> colored_end ;
272
+ }
236
273
}
237
274
238
275
return 0 ;
@@ -281,16 +318,49 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
281
318
hunk -> end - hunk -> start );
282
319
}
283
320
321
+ static void render_diff_header (struct add_p_state * s ,
322
+ struct file_diff * file_diff , int colored ,
323
+ struct strbuf * out )
324
+ {
325
+ /*
326
+ * If there was a mode change, the first hunk is a pseudo hunk that
327
+ * corresponds to the mode line in the header. If the user did not want
328
+ * to stage that "hunk", we actually have to cut it out from the header.
329
+ */
330
+ int skip_mode_change =
331
+ file_diff -> mode_change && file_diff -> hunk -> use != USE_HUNK ;
332
+ struct hunk * head = & file_diff -> head , * first = file_diff -> hunk ;
333
+
334
+ if (!skip_mode_change ) {
335
+ render_hunk (s , head , 0 , colored , out );
336
+ return ;
337
+ }
338
+
339
+ if (colored ) {
340
+ const char * p = s -> colored .buf ;
341
+
342
+ strbuf_add (out , p + head -> colored_start ,
343
+ first -> colored_start - head -> colored_start );
344
+ strbuf_add (out , p + first -> colored_end ,
345
+ head -> colored_end - first -> colored_end );
346
+ } else {
347
+ const char * p = s -> plain .buf ;
348
+
349
+ strbuf_add (out , p + head -> start , first -> start - head -> start );
350
+ strbuf_add (out , p + first -> end , head -> end - first -> end );
351
+ }
352
+ }
353
+
284
354
static void reassemble_patch (struct add_p_state * s ,
285
355
struct file_diff * file_diff , struct strbuf * out )
286
356
{
287
357
struct hunk * hunk ;
288
358
size_t i ;
289
359
ssize_t delta = 0 ;
290
360
291
- render_hunk (s , & file_diff -> head , 0 , 0 , out );
361
+ render_diff_header (s , file_diff , 0 , out );
292
362
293
- for (i = 0 ; i < file_diff -> hunk_nr ; i ++ ) {
363
+ for (i = file_diff -> mode_change ; i < file_diff -> hunk_nr ; i ++ ) {
294
364
hunk = file_diff -> hunk + i ;
295
365
if (hunk -> use != USE_HUNK )
296
366
delta += hunk -> header .old_count
@@ -325,7 +395,7 @@ static int patch_update_file(struct add_p_state *s,
325
395
return 0 ;
326
396
327
397
strbuf_reset (& s -> buf );
328
- render_hunk (s , & file_diff -> head , 0 , colored , & s -> buf );
398
+ render_diff_header (s , file_diff , colored , & s -> buf );
329
399
fputs (s -> buf .buf , stdout );
330
400
for (;;) {
331
401
if (hunk_index >= file_diff -> hunk_nr )
0 commit comments