@@ -28,7 +28,7 @@ struct hunk_header {
28
28
};
29
29
30
30
struct hunk {
31
- size_t start , end , colored_start , colored_end ;
31
+ size_t start , end , colored_start , colored_end , splittable_into ;
32
32
enum { UNDECIDED_HUNK = 0 , SKIP_HUNK , USE_HUNK } use ;
33
33
struct hunk_header header ;
34
34
};
@@ -152,7 +152,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
152
152
struct argv_array args = ARGV_ARRAY_INIT ;
153
153
struct strbuf * plain = & s -> plain , * colored = NULL ;
154
154
struct child_process cp = CHILD_PROCESS_INIT ;
155
- char * p , * pend , * colored_p = NULL , * colored_pend = NULL ;
155
+ char * p , * pend , * colored_p = NULL , * colored_pend = NULL , marker = '\0' ;
156
156
size_t file_diff_alloc = 0 , i , color_arg_index ;
157
157
struct file_diff * file_diff = NULL ;
158
158
struct hunk * hunk = NULL ;
@@ -222,6 +222,13 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
222
222
else if (starts_with (p , "@@ " ) ||
223
223
(hunk == & file_diff -> head &&
224
224
skip_prefix (p , "deleted file" , & deleted ))) {
225
+ if (marker == '-' || marker == '+' )
226
+ /*
227
+ * Should not happen; previous hunk did not end
228
+ * in a context line? Handle it anyway.
229
+ */
230
+ hunk -> splittable_into ++ ;
231
+
225
232
file_diff -> hunk_nr ++ ;
226
233
ALLOC_GROW (file_diff -> hunk , file_diff -> hunk_nr ,
227
234
file_diff -> hunk_alloc );
@@ -236,6 +243,12 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
236
243
file_diff -> deleted = 1 ;
237
244
else if (parse_hunk_header (s , hunk ) < 0 )
238
245
return -1 ;
246
+
247
+ /*
248
+ * Start counting into how many hunks this one can be
249
+ * split
250
+ */
251
+ marker = * p ;
239
252
} else if (hunk == & file_diff -> head &&
240
253
((skip_prefix (p , "old mode " , & mode_change ) ||
241
254
skip_prefix (p , "new mode " , & mode_change )) &&
@@ -260,6 +273,12 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
260
273
(int )(eol - (plain -> buf + file_diff -> head .start )),
261
274
plain -> buf + file_diff -> head .start );
262
275
276
+ if ((marker == '-' || marker == '+' ) &&
277
+ (* p == ' ' || * p == '\\' ))
278
+ hunk -> splittable_into ++ ;
279
+ if (marker )
280
+ marker = * p ;
281
+
263
282
p = eol == pend ? pend : eol + 1 ;
264
283
hunk -> end = p - plain -> buf ;
265
284
@@ -282,9 +301,25 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
282
301
}
283
302
}
284
303
304
+ if (marker == '-' || marker == '+' )
305
+ /*
306
+ * Last hunk ended in non-context line (i.e. it appended lines
307
+ * to the file, so there are no trailing context lines).
308
+ */
309
+ hunk -> splittable_into ++ ;
310
+
285
311
return 0 ;
286
312
}
287
313
314
+ static size_t find_next_line (struct strbuf * sb , size_t offset )
315
+ {
316
+ char * eol = memchr (sb -> buf + offset , '\n' , sb -> len - offset );
317
+
318
+ if (!eol )
319
+ return sb -> len ;
320
+ return eol - sb -> buf + 1 ;
321
+ }
322
+
288
323
static void render_hunk (struct add_p_state * s , struct hunk * hunk ,
289
324
ssize_t delta , int colored , struct strbuf * out )
290
325
{
@@ -380,6 +415,139 @@ static void reassemble_patch(struct add_p_state *s,
380
415
}
381
416
}
382
417
418
+ static int split_hunk (struct add_p_state * s , struct file_diff * file_diff ,
419
+ size_t hunk_index )
420
+ {
421
+ int colored = !!s -> colored .len , first = 1 ;
422
+ struct hunk * hunk = file_diff -> hunk + hunk_index ;
423
+ size_t splittable_into ;
424
+ size_t end , colored_end , current , colored_current = 0 , context_line_count ;
425
+ struct hunk_header remaining , * header ;
426
+ char marker , ch ;
427
+
428
+ if (hunk_index >= file_diff -> hunk_nr )
429
+ BUG ("invalid hunk index: %d (must be >= 0 and < %d)" ,
430
+ (int )hunk_index , (int )file_diff -> hunk_nr );
431
+
432
+ if (hunk -> splittable_into < 2 )
433
+ return 0 ;
434
+ splittable_into = hunk -> splittable_into ;
435
+
436
+ end = hunk -> end ;
437
+ colored_end = hunk -> colored_end ;
438
+
439
+ memcpy (& remaining , & hunk -> header , sizeof (remaining ));
440
+
441
+ file_diff -> hunk_nr += splittable_into - 1 ;
442
+ ALLOC_GROW (file_diff -> hunk , file_diff -> hunk_nr , file_diff -> hunk_alloc );
443
+ if (hunk_index + splittable_into < file_diff -> hunk_nr )
444
+ memmove (file_diff -> hunk + hunk_index + splittable_into ,
445
+ file_diff -> hunk + hunk_index + 1 ,
446
+ (file_diff -> hunk_nr - hunk_index - splittable_into )
447
+ * sizeof (* hunk ));
448
+ hunk = file_diff -> hunk + hunk_index ;
449
+ hunk -> splittable_into = 1 ;
450
+ memset (hunk + 1 , 0 , (splittable_into - 1 ) * sizeof (* hunk ));
451
+
452
+ header = & hunk -> header ;
453
+ header -> old_count = header -> new_count = 0 ;
454
+
455
+ current = hunk -> start ;
456
+ if (colored )
457
+ colored_current = hunk -> colored_start ;
458
+ marker = '\0' ;
459
+ context_line_count = 0 ;
460
+
461
+ while (splittable_into > 1 ) {
462
+ ch = s -> plain .buf [current ];
463
+ if ((marker == '-' || marker == '+' ) && ch == ' ' ) {
464
+ first = 0 ;
465
+ hunk [1 ].start = current ;
466
+ if (colored )
467
+ hunk [1 ].colored_start = colored_current ;
468
+ context_line_count = 0 ;
469
+ }
470
+
471
+ if (marker != ' ' || (ch != '-' && ch != '+' )) {
472
+ next_hunk_line :
473
+ /* current hunk not done yet */
474
+ if (ch == ' ' )
475
+ context_line_count ++ ;
476
+ else if (ch == '-' )
477
+ header -> old_count ++ ;
478
+ else if (ch == '+' )
479
+ header -> new_count ++ ;
480
+ else
481
+ BUG ("unhandled diff marker: '%c'" , ch );
482
+ marker = ch ;
483
+ current = find_next_line (& s -> plain , current );
484
+ if (colored )
485
+ colored_current =
486
+ find_next_line (& s -> colored ,
487
+ colored_current );
488
+ continue ;
489
+ }
490
+
491
+ if (first ) {
492
+ if (header -> old_count || header -> new_count )
493
+ BUG ("counts are off: %d/%d" ,
494
+ (int )header -> old_count ,
495
+ (int )header -> new_count );
496
+
497
+ header -> old_count = context_line_count ;
498
+ header -> new_count = context_line_count ;
499
+ context_line_count = 0 ;
500
+ first = 0 ;
501
+ goto next_hunk_line ;
502
+ }
503
+
504
+ remaining .old_offset += header -> old_count ;
505
+ remaining .old_count -= header -> old_count ;
506
+ remaining .new_offset += header -> new_count ;
507
+ remaining .new_count -= header -> new_count ;
508
+
509
+ /* initialize next hunk header's offsets */
510
+ hunk [1 ].header .old_offset =
511
+ header -> old_offset + header -> old_count ;
512
+ hunk [1 ].header .new_offset =
513
+ header -> new_offset + header -> new_count ;
514
+
515
+ /* add one split hunk */
516
+ header -> old_count += context_line_count ;
517
+ header -> new_count += context_line_count ;
518
+
519
+ hunk -> end = current ;
520
+ if (colored )
521
+ hunk -> colored_end = colored_current ;
522
+
523
+ hunk ++ ;
524
+ hunk -> splittable_into = 1 ;
525
+ hunk -> use = hunk [-1 ].use ;
526
+ header = & hunk -> header ;
527
+
528
+ header -> old_count = header -> new_count = context_line_count ;
529
+ context_line_count = 0 ;
530
+
531
+ splittable_into -- ;
532
+ marker = ch ;
533
+ }
534
+
535
+ /* last hunk simply gets the rest */
536
+ if (header -> old_offset != remaining .old_offset )
537
+ BUG ("miscounted old_offset: %lu != %lu" ,
538
+ header -> old_offset , remaining .old_offset );
539
+ if (header -> new_offset != remaining .new_offset )
540
+ BUG ("miscounted new_offset: %lu != %lu" ,
541
+ header -> new_offset , remaining .new_offset );
542
+ header -> old_count = remaining .old_count ;
543
+ header -> new_count = remaining .new_count ;
544
+ hunk -> end = end ;
545
+ if (colored )
546
+ hunk -> colored_end = colored_end ;
547
+
548
+ return 0 ;
549
+ }
550
+
383
551
static const char help_patch_text [] =
384
552
N_ ("y - stage this hunk\n"
385
553
"n - do not stage this hunk\n"
@@ -389,6 +557,7 @@ N_("y - stage this hunk\n"
389
557
"J - leave this hunk undecided, see next hunk\n"
390
558
"k - leave this hunk undecided, see previous undecided hunk\n"
391
559
"K - leave this hunk undecided, see previous hunk\n"
560
+ "s - split the current hunk into smaller hunks\n"
392
561
"? - print help\n" );
393
562
394
563
static int patch_update_file (struct add_p_state * s ,
@@ -445,6 +614,8 @@ static int patch_update_file(struct add_p_state *s,
445
614
strbuf_addstr (& s -> buf , ",j" );
446
615
if (hunk_index + 1 < file_diff -> hunk_nr )
447
616
strbuf_addstr (& s -> buf , ",J" );
617
+ if (hunk -> splittable_into > 1 )
618
+ strbuf_addstr (& s -> buf , ",s" );
448
619
449
620
if (file_diff -> deleted )
450
621
prompt_mode_type = PROMPT_DELETION ;
@@ -505,6 +676,15 @@ static int patch_update_file(struct add_p_state *s,
505
676
hunk_index = undecided_next ;
506
677
else
507
678
err (s , _ ("No next hunk" ));
679
+ } else if (s -> answer .buf [0 ] == 's' ) {
680
+ size_t splittable_into = hunk -> splittable_into ;
681
+ if (splittable_into < 2 )
682
+ err (s , _ ("Sorry, cannot split this hunk" ));
683
+ else if (!split_hunk (s , file_diff ,
684
+ hunk - file_diff -> hunk ))
685
+ color_fprintf_ln (stdout , s -> s .header_color ,
686
+ _ ("Split into %d hunks." ),
687
+ (int )splittable_into );
508
688
} else
509
689
color_fprintf (stdout , s -> s .help_color ,
510
690
_ (help_patch_text ));
0 commit comments