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