built-in add -p: coalesce hunks after splitting them
[git] / add-patch.c
1 #include "cache.h"
2 #include "add-interactive.h"
3 #include "strbuf.h"
4 #include "run-command.h"
5 #include "argv-array.h"
6 #include "pathspec.h"
7 #include "color.h"
8 #include "diff.h"
9
10 enum prompt_mode_type {
11         PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK
12 };
13
14 static const char *prompt_mode[] = {
15         N_("Stage mode change [y,n,a,d%s,?]? "),
16         N_("Stage deletion [y,n,a,d%s,?]? "),
17         N_("Stage this hunk [y,n,a,d%s,?]? ")
18 };
19
20 struct hunk_header {
21         unsigned long old_offset, old_count, new_offset, new_count;
22         /*
23          * Start/end offsets to the extra text after the second `@@` in the
24          * hunk header, e.g. the function signature. This is expected to
25          * include the newline.
26          */
27         size_t extra_start, extra_end, colored_extra_start, colored_extra_end;
28 };
29
30 struct hunk {
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 };
35
36 struct add_p_state {
37         struct add_i_state s;
38         struct strbuf answer, buf;
39
40         /* parsed diff */
41         struct strbuf plain, colored;
42         struct file_diff {
43                 struct hunk head;
44                 struct hunk *hunk;
45                 size_t hunk_nr, hunk_alloc;
46                 unsigned deleted:1, mode_change:1;
47         } *file_diff;
48         size_t file_diff_nr;
49 };
50
51 static void err(struct add_p_state *s, const char *fmt, ...)
52 {
53         va_list args;
54
55         va_start(args, fmt);
56         fputs(s->s.error_color, stderr);
57         vfprintf(stderr, fmt, args);
58         fputs(s->s.reset_color, stderr);
59         fputc('\n', stderr);
60         va_end(args);
61 }
62
63 static void setup_child_process(struct add_p_state *s,
64                                 struct child_process *cp, ...)
65 {
66         va_list ap;
67         const char *arg;
68
69         va_start(ap, cp);
70         while ((arg = va_arg(ap, const char *)))
71                 argv_array_push(&cp->args, arg);
72         va_end(ap);
73
74         cp->git_cmd = 1;
75         argv_array_pushf(&cp->env_array,
76                          INDEX_ENVIRONMENT "=%s", s->s.r->index_file);
77 }
78
79 static int parse_range(const char **p,
80                        unsigned long *offset, unsigned long *count)
81 {
82         char *pend;
83
84         *offset = strtoul(*p, &pend, 10);
85         if (pend == *p)
86                 return -1;
87         if (*pend != ',') {
88                 *count = 1;
89                 *p = pend;
90                 return 0;
91         }
92         *count = strtoul(pend + 1, (char **)p, 10);
93         return *p == pend + 1 ? -1 : 0;
94 }
95
96 static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk)
97 {
98         struct hunk_header *header = &hunk->header;
99         const char *line = s->plain.buf + hunk->start, *p = line;
100         char *eol = memchr(p, '\n', s->plain.len - hunk->start);
101
102         if (!eol)
103                 eol = s->plain.buf + s->plain.len;
104
105         if (!skip_prefix(p, "@@ -", &p) ||
106             parse_range(&p, &header->old_offset, &header->old_count) < 0 ||
107             !skip_prefix(p, " +", &p) ||
108             parse_range(&p, &header->new_offset, &header->new_count) < 0 ||
109             !skip_prefix(p, " @@", &p))
110                 return error(_("could not parse hunk header '%.*s'"),
111                              (int)(eol - line), line);
112
113         hunk->start = eol - s->plain.buf + (*eol == '\n');
114         header->extra_start = p - s->plain.buf;
115         header->extra_end = hunk->start;
116
117         if (!s->colored.len) {
118                 header->colored_extra_start = header->colored_extra_end = 0;
119                 return 0;
120         }
121
122         /* Now find the extra text in the colored diff */
123         line = s->colored.buf + hunk->colored_start;
124         eol = memchr(line, '\n', s->colored.len - hunk->colored_start);
125         if (!eol)
126                 eol = s->colored.buf + s->colored.len;
127         p = memmem(line, eol - line, "@@ -", 4);
128         if (!p)
129                 return error(_("could not parse colored hunk header '%.*s'"),
130                              (int)(eol - line), line);
131         p = memmem(p + 4, eol - p - 4, " @@", 3);
132         if (!p)
133                 return error(_("could not parse colored hunk header '%.*s'"),
134                              (int)(eol - line), line);
135         hunk->colored_start = eol - s->colored.buf + (*eol == '\n');
136         header->colored_extra_start = p + 3 - s->colored.buf;
137         header->colored_extra_end = hunk->colored_start;
138
139         return 0;
140 }
141
142 static int is_octal(const char *p, size_t len)
143 {
144         if (!len)
145                 return 0;
146
147         while (len--)
148                 if (*p < '0' || *(p++) > '7')
149                         return 0;
150         return 1;
151 }
152
153 static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
154 {
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, 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;
162         int res;
163
164         /* Use `--no-color` explicitly, just in case `diff.color = always`. */
165         argv_array_pushl(&args, "diff-files", "-p", "--no-color", "--", NULL);
166         color_arg_index = args.argc - 2;
167         for (i = 0; i < ps->nr; i++)
168                 argv_array_push(&args, ps->items[i].original);
169
170         setup_child_process(s, &cp, NULL);
171         cp.argv = args.argv;
172         res = capture_command(&cp, plain, 0);
173         if (res) {
174                 argv_array_clear(&args);
175                 return error(_("could not parse diff"));
176         }
177         if (!plain->len) {
178                 argv_array_clear(&args);
179                 return 0;
180         }
181         strbuf_complete_line(plain);
182
183         if (want_color_fd(1, -1)) {
184                 struct child_process colored_cp = CHILD_PROCESS_INIT;
185
186                 setup_child_process(s, &colored_cp, NULL);
187                 xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
188                 colored_cp.argv = args.argv;
189                 colored = &s->colored;
190                 res = capture_command(&colored_cp, colored, 0);
191                 argv_array_clear(&args);
192                 if (res)
193                         return error(_("could not parse colored diff"));
194                 strbuf_complete_line(colored);
195                 colored_p = colored->buf;
196                 colored_pend = colored_p + colored->len;
197         }
198         argv_array_clear(&args);
199
200         /* parse files and hunks */
201         p = plain->buf;
202         pend = p + plain->len;
203         while (p != pend) {
204                 char *eol = memchr(p, '\n', pend - p);
205                 const char *deleted = NULL, *mode_change = NULL;
206
207                 if (!eol)
208                         eol = pend;
209
210                 if (starts_with(p, "diff ")) {
211                         s->file_diff_nr++;
212                         ALLOC_GROW(s->file_diff, s->file_diff_nr,
213                                    file_diff_alloc);
214                         file_diff = s->file_diff + s->file_diff_nr - 1;
215                         memset(file_diff, 0, sizeof(*file_diff));
216                         hunk = &file_diff->head;
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);
224                 else if (file_diff->deleted)
225                         ; /* keep the rest of the file in a single "hunk" */
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);
239                         hunk = file_diff->hunk + file_diff->hunk_nr - 1;
240                         memset(hunk, 0, sizeof(*hunk));
241
242                         hunk->start = p - plain->buf;
243                         if (colored)
244                                 hunk->colored_start = colored_p - colored->buf;
245
246                         if (deleted)
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)) {
259                         if (file_diff->mode_change)
260                                 BUG("double mode change?\n\n%.*s",
261                                     (int)(eol - plain->buf), plain->buf);
262                         if (file_diff->hunk_nr++)
263                                 BUG("mode change in the middle?\n\n%.*s",
264                                     (int)(eol - plain->buf), plain->buf);
265
266                         /*
267                          * Do *not* change `hunk`: the mode change pseudo-hunk
268                          * is _part of_ the header "hunk".
269                          */
270                         file_diff->mode_change = 1;
271                         ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
272                                    file_diff->hunk_alloc);
273                         memset(file_diff->hunk, 0, sizeof(struct hunk));
274                         file_diff->hunk->start = p - plain->buf;
275                         if (colored_p)
276                                 file_diff->hunk->colored_start =
277                                         colored_p - colored->buf;
278                 } else if (hunk == &file_diff->head &&
279                            skip_prefix(p, "new mode ", &mode_change) &&
280                            is_octal(mode_change, eol - mode_change)) {
281
282                         /*
283                          * Extend the "mode change" pseudo-hunk to include also
284                          * the "new mode" line.
285                          */
286                         if (!file_diff->mode_change)
287                                 BUG("'new mode' without 'old mode'?\n\n%.*s",
288                                     (int)(eol - plain->buf), plain->buf);
289                         if (file_diff->hunk_nr != 1)
290                                 BUG("mode change in the middle?\n\n%.*s",
291                                     (int)(eol - plain->buf), plain->buf);
292                         if (p - plain->buf != file_diff->hunk->end)
293                                 BUG("'new mode' does not immediately follow "
294                                     "'old mode'?\n\n%.*s",
295                                     (int)(eol - plain->buf), plain->buf);
296                 }
297
298                 if (file_diff->deleted && file_diff->mode_change)
299                         BUG("diff contains delete *and* a mode change?!?\n%.*s",
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
311                 if (colored) {
312                         char *colored_eol = memchr(colored_p, '\n',
313                                                    colored_pend - colored_p);
314                         if (colored_eol)
315                                 colored_p = colored_eol + 1;
316                         else
317                                 colored_p = colored_pend;
318
319                         hunk->colored_end = colored_p - colored->buf;
320                 }
321
322                 if (mode_change) {
323                         if (file_diff->hunk_nr != 1)
324                                 BUG("mode change in hunk #%d???",
325                                     (int)file_diff->hunk_nr);
326                         /* Adjust the end of the "mode change" pseudo-hunk */
327                         file_diff->hunk->end = hunk->end;
328                         if (colored)
329                                 file_diff->hunk->colored_end = hunk->colored_end;
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 {
360         struct hunk_header *header = &hunk->header;
361
362         if (hunk->header.old_offset != 0 || hunk->header.new_offset != 0) {
363                 /*
364                  * Generate the hunk header dynamically, except for special
365                  * hunks (such as the diff header).
366                  */
367                 const char *p;
368                 size_t len;
369                 unsigned long old_offset = header->old_offset;
370                 unsigned long new_offset = header->new_offset;
371
372                 if (!colored) {
373                         p = s->plain.buf + header->extra_start;
374                         len = header->extra_end - header->extra_start;
375                 } else {
376                         strbuf_addstr(out, s->s.fraginfo_color);
377                         p = s->colored.buf + header->colored_extra_start;
378                         len = header->colored_extra_end
379                                 - header->colored_extra_start;
380                 }
381
382                 new_offset += delta;
383
384                 strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@",
385                             old_offset, header->old_count,
386                             new_offset, header->new_count);
387                 if (len)
388                         strbuf_add(out, p, len);
389                 else if (colored)
390                         strbuf_addf(out, "%s\n", GIT_COLOR_RESET);
391                 else
392                         strbuf_addch(out, '\n');
393         }
394
395         if (colored)
396                 strbuf_add(out, s->colored.buf + hunk->colored_start,
397                            hunk->colored_end - hunk->colored_start);
398         else
399                 strbuf_add(out, s->plain.buf + hunk->start,
400                            hunk->end - hunk->start);
401 }
402
403 static void render_diff_header(struct add_p_state *s,
404                                struct file_diff *file_diff, int colored,
405                                struct strbuf *out)
406 {
407         /*
408          * If there was a mode change, the first hunk is a pseudo hunk that
409          * corresponds to the mode line in the header. If the user did not want
410          * to stage that "hunk", we actually have to cut it out from the header.
411          */
412         int skip_mode_change =
413                 file_diff->mode_change && file_diff->hunk->use != USE_HUNK;
414         struct hunk *head = &file_diff->head, *first = file_diff->hunk;
415
416         if (!skip_mode_change) {
417                 render_hunk(s, head, 0, colored, out);
418                 return;
419         }
420
421         if (colored) {
422                 const char *p = s->colored.buf;
423
424                 strbuf_add(out, p + head->colored_start,
425                             first->colored_start - head->colored_start);
426                 strbuf_add(out, p + first->colored_end,
427                             head->colored_end - first->colored_end);
428         } else {
429                 const char *p = s->plain.buf;
430
431                 strbuf_add(out, p + head->start, first->start - head->start);
432                 strbuf_add(out, p + first->end, head->end - first->end);
433         }
434 }
435
436 /* Coalesce hunks again that were split */
437 static int merge_hunks(struct add_p_state *s, struct file_diff *file_diff,
438                        size_t *hunk_index, struct hunk *merged)
439 {
440         size_t i = *hunk_index;
441         struct hunk *hunk = file_diff->hunk + i;
442         /* `header` corresponds to the merged hunk */
443         struct hunk_header *header = &merged->header, *next;
444
445         if (hunk->use != USE_HUNK)
446                 return 0;
447
448         *merged = *hunk;
449         /* We simply skip the colored part (if any) when merging hunks */
450         merged->colored_start = merged->colored_end = 0;
451
452         for (; i + 1 < file_diff->hunk_nr; i++) {
453                 hunk++;
454                 next = &hunk->header;
455
456                 /*
457                  * Stop merging hunks when:
458                  *
459                  * - the hunk is not selected for use, or
460                  * - the hunk does not overlap with the already-merged hunk(s)
461                  */
462                 if (hunk->use != USE_HUNK ||
463                     header->new_offset >= next->new_offset ||
464                     header->new_offset + header->new_count < next->new_offset ||
465                     merged->start >= hunk->start ||
466                     merged->end < hunk->start)
467                         break;
468
469                 merged->end = hunk->end;
470                 merged->colored_end = hunk->colored_end;
471
472                 header->old_count = next->old_offset + next->old_count
473                         - header->old_offset;
474                 header->new_count = next->new_offset + next->new_count
475                         - header->new_offset;
476         }
477
478         if (i == *hunk_index)
479                 return 0;
480
481         *hunk_index = i;
482         return 1;
483 }
484
485 static void reassemble_patch(struct add_p_state *s,
486                              struct file_diff *file_diff, struct strbuf *out)
487 {
488         struct hunk *hunk;
489         size_t i;
490         ssize_t delta = 0;
491
492         render_diff_header(s, file_diff, 0, out);
493
494         for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) {
495                 struct hunk merged = { 0 };
496
497                 hunk = file_diff->hunk + i;
498                 if (hunk->use != USE_HUNK)
499                         delta += hunk->header.old_count
500                                 - hunk->header.new_count;
501                 else {
502                         /* merge overlapping hunks into a temporary hunk */
503                         if (merge_hunks(s, file_diff, &i, &merged))
504                                 hunk = &merged;
505
506                         render_hunk(s, hunk, delta, 0, out);
507                 }
508         }
509 }
510
511 static int split_hunk(struct add_p_state *s, struct file_diff *file_diff,
512                        size_t hunk_index)
513 {
514         int colored = !!s->colored.len, first = 1;
515         struct hunk *hunk = file_diff->hunk + hunk_index;
516         size_t splittable_into;
517         size_t end, colored_end, current, colored_current = 0, context_line_count;
518         struct hunk_header remaining, *header;
519         char marker, ch;
520
521         if (hunk_index >= file_diff->hunk_nr)
522                 BUG("invalid hunk index: %d (must be >= 0 and < %d)",
523                     (int)hunk_index, (int)file_diff->hunk_nr);
524
525         if (hunk->splittable_into < 2)
526                 return 0;
527         splittable_into = hunk->splittable_into;
528
529         end = hunk->end;
530         colored_end = hunk->colored_end;
531
532         remaining = hunk->header;
533
534         file_diff->hunk_nr += splittable_into - 1;
535         ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr, file_diff->hunk_alloc);
536         if (hunk_index + splittable_into < file_diff->hunk_nr)
537                 memmove(file_diff->hunk + hunk_index + splittable_into,
538                         file_diff->hunk + hunk_index + 1,
539                         (file_diff->hunk_nr - hunk_index - splittable_into)
540                         * sizeof(*hunk));
541         hunk = file_diff->hunk + hunk_index;
542         hunk->splittable_into = 1;
543         memset(hunk + 1, 0, (splittable_into - 1) * sizeof(*hunk));
544
545         header = &hunk->header;
546         header->old_count = header->new_count = 0;
547
548         current = hunk->start;
549         if (colored)
550                 colored_current = hunk->colored_start;
551         marker = '\0';
552         context_line_count = 0;
553
554         while (splittable_into > 1) {
555                 ch = s->plain.buf[current];
556
557                 if (!ch)
558                         BUG("buffer overrun while splitting hunks");
559
560                 /*
561                  * Is this the first context line after a chain of +/- lines?
562                  * Then record the start of the next split hunk.
563                  */
564                 if ((marker == '-' || marker == '+') && ch == ' ') {
565                         first = 0;
566                         hunk[1].start = current;
567                         if (colored)
568                                 hunk[1].colored_start = colored_current;
569                         context_line_count = 0;
570                 }
571
572                 /*
573                  * Was the previous line a +/- one? Alternatively, is this the
574                  * first line (and not a +/- one)?
575                  *
576                  * Then just increment the appropriate counter and continue
577                  * with the next line.
578                  */
579                 if (marker != ' ' || (ch != '-' && ch != '+')) {
580 next_hunk_line:
581                         /* Comment lines are attached to the previous line */
582                         if (ch == '\\')
583                                 ch = marker ? marker : ' ';
584
585                         /* current hunk not done yet */
586                         if (ch == ' ')
587                                 context_line_count++;
588                         else if (ch == '-')
589                                 header->old_count++;
590                         else if (ch == '+')
591                                 header->new_count++;
592                         else
593                                 BUG("unhandled diff marker: '%c'", ch);
594                         marker = ch;
595                         current = find_next_line(&s->plain, current);
596                         if (colored)
597                                 colored_current =
598                                         find_next_line(&s->colored,
599                                                        colored_current);
600                         continue;
601                 }
602
603                 /*
604                  * We got us the start of a new hunk!
605                  *
606                  * This is a context line, so it is shared with the previous
607                  * hunk, if any.
608                  */
609
610                 if (first) {
611                         if (header->old_count || header->new_count)
612                                 BUG("counts are off: %d/%d",
613                                     (int)header->old_count,
614                                     (int)header->new_count);
615
616                         header->old_count = context_line_count;
617                         header->new_count = context_line_count;
618                         context_line_count = 0;
619                         first = 0;
620                         goto next_hunk_line;
621                 }
622
623                 remaining.old_offset += header->old_count;
624                 remaining.old_count -= header->old_count;
625                 remaining.new_offset += header->new_count;
626                 remaining.new_count -= header->new_count;
627
628                 /* initialize next hunk header's offsets */
629                 hunk[1].header.old_offset =
630                         header->old_offset + header->old_count;
631                 hunk[1].header.new_offset =
632                         header->new_offset + header->new_count;
633
634                 /* add one split hunk */
635                 header->old_count += context_line_count;
636                 header->new_count += context_line_count;
637
638                 hunk->end = current;
639                 if (colored)
640                         hunk->colored_end = colored_current;
641
642                 hunk++;
643                 hunk->splittable_into = 1;
644                 hunk->use = hunk[-1].use;
645                 header = &hunk->header;
646
647                 header->old_count = header->new_count = context_line_count;
648                 context_line_count = 0;
649
650                 splittable_into--;
651                 marker = ch;
652         }
653
654         /* last hunk simply gets the rest */
655         if (header->old_offset != remaining.old_offset)
656                 BUG("miscounted old_offset: %lu != %lu",
657                     header->old_offset, remaining.old_offset);
658         if (header->new_offset != remaining.new_offset)
659                 BUG("miscounted new_offset: %lu != %lu",
660                     header->new_offset, remaining.new_offset);
661         header->old_count = remaining.old_count;
662         header->new_count = remaining.new_count;
663         hunk->end = end;
664         if (colored)
665                 hunk->colored_end = colored_end;
666
667         return 0;
668 }
669
670 static const char help_patch_text[] =
671 N_("y - stage this hunk\n"
672    "n - do not stage this hunk\n"
673    "a - stage this and all the remaining hunks\n"
674    "d - do not stage this hunk nor any of the remaining hunks\n"
675    "j - leave this hunk undecided, see next undecided hunk\n"
676    "J - leave this hunk undecided, see next hunk\n"
677    "k - leave this hunk undecided, see previous undecided hunk\n"
678    "K - leave this hunk undecided, see previous hunk\n"
679    "s - split the current hunk into smaller hunks\n"
680    "? - print help\n");
681
682 static int patch_update_file(struct add_p_state *s,
683                              struct file_diff *file_diff)
684 {
685         size_t hunk_index = 0;
686         ssize_t i, undecided_previous, undecided_next;
687         struct hunk *hunk;
688         char ch;
689         struct child_process cp = CHILD_PROCESS_INIT;
690         int colored = !!s->colored.len;
691         enum prompt_mode_type prompt_mode_type;
692
693         if (!file_diff->hunk_nr)
694                 return 0;
695
696         strbuf_reset(&s->buf);
697         render_diff_header(s, file_diff, colored, &s->buf);
698         fputs(s->buf.buf, stdout);
699         for (;;) {
700                 if (hunk_index >= file_diff->hunk_nr)
701                         hunk_index = 0;
702                 hunk = file_diff->hunk + hunk_index;
703
704                 undecided_previous = -1;
705                 for (i = hunk_index - 1; i >= 0; i--)
706                         if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
707                                 undecided_previous = i;
708                                 break;
709                         }
710
711                 undecided_next = -1;
712                 for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
713                         if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
714                                 undecided_next = i;
715                                 break;
716                         }
717
718                 /* Everything decided? */
719                 if (undecided_previous < 0 && undecided_next < 0 &&
720                     hunk->use != UNDECIDED_HUNK)
721                         break;
722
723                 strbuf_reset(&s->buf);
724                 render_hunk(s, hunk, 0, colored, &s->buf);
725                 fputs(s->buf.buf, stdout);
726
727                 strbuf_reset(&s->buf);
728                 if (undecided_previous >= 0)
729                         strbuf_addstr(&s->buf, ",k");
730                 if (hunk_index)
731                         strbuf_addstr(&s->buf, ",K");
732                 if (undecided_next >= 0)
733                         strbuf_addstr(&s->buf, ",j");
734                 if (hunk_index + 1 < file_diff->hunk_nr)
735                         strbuf_addstr(&s->buf, ",J");
736                 if (hunk->splittable_into > 1)
737                         strbuf_addstr(&s->buf, ",s");
738
739                 if (file_diff->deleted)
740                         prompt_mode_type = PROMPT_DELETION;
741                 else if (file_diff->mode_change && !hunk_index)
742                         prompt_mode_type = PROMPT_MODE_CHANGE;
743                 else
744                         prompt_mode_type = PROMPT_HUNK;
745
746                 color_fprintf(stdout, s->s.prompt_color,
747                               "(%"PRIuMAX"/%"PRIuMAX") ",
748                               (uintmax_t)hunk_index + 1,
749                               (uintmax_t)file_diff->hunk_nr);
750                 color_fprintf(stdout, s->s.prompt_color,
751                               _(prompt_mode[prompt_mode_type]), s->buf.buf);
752                 fflush(stdout);
753                 if (strbuf_getline(&s->answer, stdin) == EOF)
754                         break;
755                 strbuf_trim_trailing_newline(&s->answer);
756
757                 if (!s->answer.len)
758                         continue;
759                 ch = tolower(s->answer.buf[0]);
760                 if (ch == 'y') {
761                         hunk->use = USE_HUNK;
762 soft_increment:
763                         hunk_index = undecided_next < 0 ?
764                                 file_diff->hunk_nr : undecided_next;
765                 } else if (ch == 'n') {
766                         hunk->use = SKIP_HUNK;
767                         goto soft_increment;
768                 } else if (ch == 'a') {
769                         for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
770                                 hunk = file_diff->hunk + hunk_index;
771                                 if (hunk->use == UNDECIDED_HUNK)
772                                         hunk->use = USE_HUNK;
773                         }
774                 } else if (ch == 'd') {
775                         for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
776                                 hunk = file_diff->hunk + hunk_index;
777                                 if (hunk->use == UNDECIDED_HUNK)
778                                         hunk->use = SKIP_HUNK;
779                         }
780                 } else if (s->answer.buf[0] == 'K') {
781                         if (hunk_index)
782                                 hunk_index--;
783                         else
784                                 err(s, _("No previous hunk"));
785                 } else if (s->answer.buf[0] == 'J') {
786                         if (hunk_index + 1 < file_diff->hunk_nr)
787                                 hunk_index++;
788                         else
789                                 err(s, _("No next hunk"));
790                 } else if (s->answer.buf[0] == 'k') {
791                         if (undecided_previous >= 0)
792                                 hunk_index = undecided_previous;
793                         else
794                                 err(s, _("No previous hunk"));
795                 } else if (s->answer.buf[0] == 'j') {
796                         if (undecided_next >= 0)
797                                 hunk_index = undecided_next;
798                         else
799                                 err(s, _("No next hunk"));
800                 } else if (s->answer.buf[0] == 's') {
801                         size_t splittable_into = hunk->splittable_into;
802                         if (splittable_into < 2)
803                                 err(s, _("Sorry, cannot split this hunk"));
804                         else if (!split_hunk(s, file_diff,
805                                              hunk - file_diff->hunk))
806                                 color_fprintf_ln(stdout, s->s.header_color,
807                                                  _("Split into %d hunks."),
808                                                  (int)splittable_into);
809                 } else
810                         color_fprintf(stdout, s->s.help_color,
811                                       _(help_patch_text));
812         }
813
814         /* Any hunk to be used? */
815         for (i = 0; i < file_diff->hunk_nr; i++)
816                 if (file_diff->hunk[i].use == USE_HUNK)
817                         break;
818
819         if (i < file_diff->hunk_nr) {
820                 /* At least one hunk selected: apply */
821                 strbuf_reset(&s->buf);
822                 reassemble_patch(s, file_diff, &s->buf);
823
824                 discard_index(s->s.r->index);
825                 setup_child_process(s, &cp, "apply", "--cached", NULL);
826                 if (pipe_command(&cp, s->buf.buf, s->buf.len,
827                                  NULL, 0, NULL, 0))
828                         error(_("'git apply --cached' failed"));
829                 if (!repo_read_index(s->s.r))
830                         repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
831                                                      1, NULL, NULL, NULL);
832         }
833
834         putchar('\n');
835         return 0;
836 }
837
838 int run_add_p(struct repository *r, const struct pathspec *ps)
839 {
840         struct add_p_state s = {
841                 { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
842         };
843         size_t i;
844
845         init_add_i_state(&s.s, r);
846
847         if (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||
848             repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
849                                          NULL, NULL, NULL) < 0 ||
850             parse_diff(&s, ps) < 0) {
851                 strbuf_release(&s.plain);
852                 strbuf_release(&s.colored);
853                 return -1;
854         }
855
856         for (i = 0; i < s.file_diff_nr; i++)
857                 if (patch_update_file(&s, s.file_diff + i))
858                         break;
859
860         strbuf_release(&s.answer);
861         strbuf_release(&s.buf);
862         strbuf_release(&s.plain);
863         strbuf_release(&s.colored);
864         return 0;
865 }