28
};
29
30
struct hunk {
31
- size_t start, end, colored_start, colored_end;
31
+ size_t start, end, colored_start, colored_end, splittable_into;
32
enum { UNDECIDED_HUNK = 0, SKIP_HUNK, USE_HUNK } use;
33
struct hunk_header header;
34
};
155
struct argv_array args = ARGV_ARRAY_INIT;
156
struct strbuf *plain = &s->plain, *colored = NULL;
157
struct child_process cp = CHILD_PROCESS_INIT;
158
- char *p, *pend, *colored_p = NULL, *colored_pend = NULL;
158
+ char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
159
size_t file_diff_alloc = 0, i, color_arg_index;
160
struct file_diff *file_diff = NULL;
161
struct hunk *hunk = NULL;
217
hunk->start = p - plain->buf;
218
if (colored_p)
219
hunk->colored_start = colored_p - colored->buf;
220
+ marker = '\0';
221
} else if (p == plain->buf)
222
BUG("diff starts with unexpected line:\n"
223
"%.*s\n", (int)(eol - p), p);
226
else if (starts_with(p, "@@ ") ||
227
(hunk == &file_diff->head &&
228
skip_prefix(p, "deleted file", &deleted))) {
229
+ if (marker == '-' || marker == '+')
230
+ /*
231
+ * Should not happen; previous hunk did not end
232
+ * in a context line? Handle it anyway.
233
+ */
234
+ hunk->splittable_into++;
235
+
236
file_diff->hunk_nr++;
237
ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
238
file_diff->hunk_alloc);
247
file_diff->deleted = 1;
248
else if (parse_hunk_header(s, hunk) < 0)
249
return -1;
250
+
251
+ /*
252
+ * Start counting into how many hunks this one can be
253
+ * split
254
+ */
255
+ marker = *p;
256
} else if (hunk == &file_diff->head &&
257
skip_prefix(p, "old mode ", &mode_change) &&
258
is_octal(mode_change, eol - mode_change)) {
300
(int)(eol - (plain->buf + file_diff->head.start)),
301
plain->buf + file_diff->head.start);
302
303
+ if ((marker == '-' || marker == '+') && *p == ' ')
304
+ hunk->splittable_into++;
305
+ if (marker && *p != '\\')
306
+ marker = *p;
307
+
308
p = eol == pend ? pend : eol + 1;
309
hunk->end = p - plain->buf;
310
330
}
331
}
332
333
+ if (marker == '-' || marker == '+')
334
+ /*
335
+ * Last hunk ended in non-context line (i.e. it appended lines
336
+ * to the file, so there are no trailing context lines).
337
+ */
338
+ hunk->splittable_into++;
339
+
340
return 0;
341
}
342
343
+static size_t find_next_line(struct strbuf *sb, size_t offset)
344
+{
345
+ char *eol;
346
+
347
+ if (offset >= sb->len)
348
+ BUG("looking for next line beyond buffer (%d >= %d)\n%s",
349
+ (int)offset, (int)sb->len, sb->buf);
350
+
351
+ eol = memchr(sb->buf + offset, '\n', sb->len - offset);
352
+ if (!eol)
353
+ return sb->len;
354
+ return eol - sb->buf + 1;
355
+}
356
+
357
static void render_hunk(struct add_p_state *s, struct hunk *hunk,
358
ssize_t delta, int colored, struct strbuf *out)
359
{
452
}
453
}
454
455
+static int split_hunk(struct add_p_state *s, struct file_diff *file_diff,
456
+ size_t hunk_index)
457
+{
458
+ int colored = !!s->colored.len, first = 1;
459
+ struct hunk *hunk = file_diff->hunk + hunk_index;
460
+ size_t splittable_into;
461
+ size_t end, colored_end, current, colored_current = 0, context_line_count;
462
+ struct hunk_header remaining, *header;
463
+ char marker, ch;
464
+
465
+ if (hunk_index >= file_diff->hunk_nr)
466
+ BUG("invalid hunk index: %d (must be >= 0 and < %d)",
467
+ (int)hunk_index, (int)file_diff->hunk_nr);
468
+
469
+ if (hunk->splittable_into < 2)
470
+ return 0;
471
+ splittable_into = hunk->splittable_into;
472
+
473
+ end = hunk->end;
474
+ colored_end = hunk->colored_end;
475
+
476
+ remaining = hunk->header;
477
+
478
+ file_diff->hunk_nr += splittable_into - 1;
479
+ ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr, file_diff->hunk_alloc);
480
+ if (hunk_index + splittable_into < file_diff->hunk_nr)
481
+ memmove(file_diff->hunk + hunk_index + splittable_into,
482
+ file_diff->hunk + hunk_index + 1,
483
+ (file_diff->hunk_nr - hunk_index - splittable_into)
484
+ * sizeof(*hunk));
485
+ hunk = file_diff->hunk + hunk_index;
486
+ hunk->splittable_into = 1;
487
+ memset(hunk + 1, 0, (splittable_into - 1) * sizeof(*hunk));
488
+
489
+ header = &hunk->header;
490
+ header->old_count = header->new_count = 0;
491
+
492
+ current = hunk->start;
493
+ if (colored)
494
+ colored_current = hunk->colored_start;
495
+ marker = '\0';
496
+ context_line_count = 0;
497
+
498
+ while (splittable_into > 1) {
499
+ ch = s->plain.buf[current];
500
+
501
+ if (!ch)
502
+ BUG("buffer overrun while splitting hunks");
503
+
504
+ /*
505
+ * Is this the first context line after a chain of +/- lines?
506
+ * Then record the start of the next split hunk.
507
+ */
508
+ if ((marker == '-' || marker == '+') && ch == ' ') {
509
+ first = 0;
510
+ hunk[1].start = current;
511
+ if (colored)
512
+ hunk[1].colored_start = colored_current;
513
+ context_line_count = 0;
514
+ }
515
+
516
+ /*
517
+ * Was the previous line a +/- one? Alternatively, is this the
518
+ * first line (and not a +/- one)?
519
+ *
520
+ * Then just increment the appropriate counter and continue
521
+ * with the next line.
522
+ */
523
+ if (marker != ' ' || (ch != '-' && ch != '+')) {
524
+next_hunk_line:
525
+ /* Comment lines are attached to the previous line */
526
+ if (ch == '\\')
527
+ ch = marker ? marker : ' ';
528
+
529
+ /* current hunk not done yet */
530
+ if (ch == ' ')
531
+ context_line_count++;
532
+ else if (ch == '-')
533
+ header->old_count++;
534
+ else if (ch == '+')
535
+ header->new_count++;
536
+ else
537
+ BUG("unhandled diff marker: '%c'", ch);
538
+ marker = ch;
539
+ current = find_next_line(&s->plain, current);
540
+ if (colored)
541
+ colored_current =
542
+ find_next_line(&s->colored,
543
+ colored_current);
544
+ continue;
545
+ }
546
+
547
+ /*
548
+ * We got us the start of a new hunk!
549
+ *
550
+ * This is a context line, so it is shared with the previous
551
+ * hunk, if any.
552
+ */
553
+
554
+ if (first) {
555
+ if (header->old_count || header->new_count)
556
+ BUG("counts are off: %d/%d",
557
+ (int)header->old_count,
558
+ (int)header->new_count);
559
+
560
+ header->old_count = context_line_count;
561
+ header->new_count = context_line_count;
562
+ context_line_count = 0;
563
+ first = 0;
564
+ goto next_hunk_line;
565
+ }
566
+
567
+ remaining.old_offset += header->old_count;
568
+ remaining.old_count -= header->old_count;
569
+ remaining.new_offset += header->new_count;
570
+ remaining.new_count -= header->new_count;
571
+
572
+ /* initialize next hunk header's offsets */
573
+ hunk[1].header.old_offset =
574
+ header->old_offset + header->old_count;
575
+ hunk[1].header.new_offset =
576
+ header->new_offset + header->new_count;
577
+
578
+ /* add one split hunk */
579
+ header->old_count += context_line_count;
580
+ header->new_count += context_line_count;
581
+
582
+ hunk->end = current;
583
+ if (colored)
584
+ hunk->colored_end = colored_current;
585
+
586
+ hunk++;
587
+ hunk->splittable_into = 1;
588
+ hunk->use = hunk[-1].use;
589
+ header = &hunk->header;
590
+
591
+ header->old_count = header->new_count = context_line_count;
592
+ context_line_count = 0;
593
+
594
+ splittable_into--;
595
+ marker = ch;
596
+ }
597
+
598
+ /* last hunk simply gets the rest */
599
+ if (header->old_offset != remaining.old_offset)
600
+ BUG("miscounted old_offset: %lu != %lu",
601
+ header->old_offset, remaining.old_offset);
602
+ if (header->new_offset != remaining.new_offset)
603
+ BUG("miscounted new_offset: %lu != %lu",
604
+ header->new_offset, remaining.new_offset);
605
+ header->old_count = remaining.old_count;
606
+ header->new_count = remaining.new_count;
607
+ hunk->end = end;
608
+ if (colored)
609
+ hunk->colored_end = colored_end;
610
+
611
+ return 0;
612
+}
613
+
614
static const char help_patch_text[] =
615
N_("y - stage this hunk\n"
616
"n - do not stage this hunk\n"
620
"J - leave this hunk undecided, see next hunk\n"
621
"k - leave this hunk undecided, see previous undecided hunk\n"
622
"K - leave this hunk undecided, see previous hunk\n"
623
+ "s - split the current hunk into smaller hunks\n"
624
"? - print help\n");
625
626
static int patch_update_file(struct add_p_state *s,
677
strbuf_addstr(&s->buf, ",j");
678
if (hunk_index + 1 < file_diff->hunk_nr)
679
strbuf_addstr(&s->buf, ",J");
680
+ if (hunk->splittable_into > 1)
681
+ strbuf_addstr(&s->buf, ",s");
682
683
if (file_diff->deleted)
684
prompt_mode_type = PROMPT_DELETION;
741
hunk_index = undecided_next;
742
else
743
err(s, _("No next hunk"));
744
+ } else if (s->answer.buf[0] == 's') {
745
+ size_t splittable_into = hunk->splittable_into;
746
+ if (splittable_into < 2)
747
+ err(s, _("Sorry, cannot split this hunk"));
748
+ else if (!split_hunk(s, file_diff,
749
+ hunk - file_diff->hunk))
750
+ color_fprintf_ln(stdout, s->s.header_color,
751
+ _("Split into %d hunks."),
752
+ (int)splittable_into);
753
} else
754
color_fprintf(stdout, s->s.help_color,
755
_(help_patch_text));