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