@@ -33,7 +33,7 @@ struct add_p_state {
3333 struct hunk head ;
3434 struct hunk * hunk ;
3535 size_t hunk_nr , hunk_alloc ;
36- unsigned deleted :1 ;
36+ unsigned deleted :1 , mode_change : 1 ;
3737 } * file_diff ;
3838 size_t file_diff_nr ;
3939};
@@ -129,6 +129,14 @@ static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk)
129129 return 0 ;
130130}
131131
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+
132140static int parse_diff (struct add_p_state * s , const struct pathspec * ps )
133141{
134142 struct argv_array args = ARGV_ARRAY_INIT ;
@@ -181,7 +189,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
181189 pend = p + plain -> len ;
182190 while (p != pend ) {
183191 char * eol = memchr (p , '\n' , pend - p );
184- const char * deleted = NULL ;
192+ const char * deleted = NULL , * mode_change = NULL ;
185193
186194 if (!eol )
187195 eol = pend ;
@@ -218,8 +226,30 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
218226 file_diff -> deleted = 1 ;
219227 else if (parse_hunk_header (s , hunk ) < 0 )
220228 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?" );
221246 }
222247
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+
223253 p = eol == pend ? pend : eol + 1 ;
224254 hunk -> end = p - plain -> buf ;
225255
@@ -233,6 +263,13 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
233263
234264 hunk -> colored_end = colored_p - colored -> buf ;
235265 }
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+ }
236273 }
237274
238275 return 0 ;
@@ -281,16 +318,49 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
281318 hunk -> end - hunk -> start );
282319}
283320
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+
284354static void reassemble_patch (struct add_p_state * s ,
285355 struct file_diff * file_diff , struct strbuf * out )
286356{
287357 struct hunk * hunk ;
288358 size_t i ;
289359 ssize_t delta = 0 ;
290360
291- render_hunk (s , & file_diff -> head , 0 , 0 , out );
361+ render_diff_header (s , file_diff , 0 , out );
292362
293- for (i = 0 ; i < file_diff -> hunk_nr ; i ++ ) {
363+ for (i = file_diff -> mode_change ; i < file_diff -> hunk_nr ; i ++ ) {
294364 hunk = file_diff -> hunk + i ;
295365 if (hunk -> use != USE_HUNK )
296366 delta += hunk -> header .old_count
@@ -325,7 +395,7 @@ static int patch_update_file(struct add_p_state *s,
325395 return 0 ;
326396
327397 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 );
329399 fputs (s -> buf .buf , stdout );
330400 for (;;) {
331401 if (hunk_index >= file_diff -> hunk_nr )
0 commit comments