2 #include "add-interactive.h"
4 #include "run-command.h"
5 #include "argv-array.h"
10 enum prompt_mode_type
{
11 PROMPT_MODE_CHANGE
= 0, PROMPT_DELETION
, PROMPT_HUNK
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,?]? ")
21 unsigned long old_offset
, old_count
, new_offset
, new_count
;
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.
27 size_t extra_start
, extra_end
, colored_extra_start
, colored_extra_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
;
38 struct strbuf answer
, buf
;
41 struct strbuf plain
, colored
;
45 size_t hunk_nr
, hunk_alloc
;
46 unsigned deleted
:1, mode_change
:1;
51 static void err(struct add_p_state
*s
, const char *fmt
, ...)
56 fputs(s
->s
.error_color
, stderr
);
57 vfprintf(stderr
, fmt
, args
);
58 fputs(s
->s
.reset_color
, stderr
);
63 static void setup_child_process(struct add_p_state
*s
,
64 struct child_process
*cp
, ...)
70 while ((arg
= va_arg(ap
, const char *)))
71 argv_array_push(&cp
->args
, arg
);
75 argv_array_pushf(&cp
->env_array
,
76 INDEX_ENVIRONMENT
"=%s", s
->s
.r
->index_file
);
79 static int parse_range(const char **p
,
80 unsigned long *offset
, unsigned long *count
)
84 *offset
= strtoul(*p
, &pend
, 10);
92 *count
= strtoul(pend
+ 1, (char **)p
, 10);
93 return *p
== pend
+ 1 ? -1 : 0;
96 static int parse_hunk_header(struct add_p_state
*s
, struct hunk
*hunk
)
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
);
103 eol
= s
->plain
.buf
+ s
->plain
.len
;
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
);
113 hunk
->start
= eol
- s
->plain
.buf
+ (*eol
== '\n');
114 header
->extra_start
= p
- s
->plain
.buf
;
115 header
->extra_end
= hunk
->start
;
117 if (!s
->colored
.len
) {
118 header
->colored_extra_start
= header
->colored_extra_end
= 0;
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
);
126 eol
= s
->colored
.buf
+ s
->colored
.len
;
127 p
= memmem(line
, eol
- line
, "@@ -", 4);
129 return error(_("could not parse colored hunk header '%.*s'"),
130 (int)(eol
- line
), line
);
131 p
= memmem(p
+ 4, eol
- p
- 4, " @@", 3);
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
;
142 static int is_octal(const char *p
, size_t len
)
148 if (*p
< '0' || *(p
++) > '7')
153 static int parse_diff(struct add_p_state
*s
, const struct pathspec
*ps
)
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
;
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
);
170 setup_child_process(s
, &cp
, NULL
);
172 res
= capture_command(&cp
, plain
, 0);
174 argv_array_clear(&args
);
175 return error(_("could not parse diff"));
178 argv_array_clear(&args
);
181 strbuf_complete_line(plain
);
183 if (want_color_fd(1, -1)) {
184 struct child_process colored_cp
= CHILD_PROCESS_INIT
;
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
);
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
;
198 argv_array_clear(&args
);
200 /* parse files and hunks */
202 pend
= p
+ plain
->len
;
204 char *eol
= memchr(p
, '\n', pend
- p
);
205 const char *deleted
= NULL
, *mode_change
= NULL
;
210 if (starts_with(p
, "diff ")) {
212 ALLOC_GROW(s
->file_diff
, s
->file_diff_nr
,
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
;
219 hunk
->colored_start
= colored_p
- colored
->buf
;
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
== '+')
231 * Should not happen; previous hunk did not end
232 * in a context line? Handle it anyway.
234 hunk
->splittable_into
++;
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
));
242 hunk
->start
= p
- plain
->buf
;
244 hunk
->colored_start
= colored_p
- colored
->buf
;
247 file_diff
->deleted
= 1;
248 else if (parse_hunk_header(s
, hunk
) < 0)
252 * Start counting into how many hunks this one can be
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
);
267 * Do *not* change `hunk`: the mode change pseudo-hunk
268 * is _part of_ the header "hunk".
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
;
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
)) {
283 * Extend the "mode change" pseudo-hunk to include also
284 * the "new mode" line.
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
);
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
);
303 if ((marker
== '-' || marker
== '+') && *p
== ' ')
304 hunk
->splittable_into
++;
305 if (marker
&& *p
!= '\\')
308 p
= eol
== pend
? pend
: eol
+ 1;
309 hunk
->end
= p
- plain
->buf
;
312 char *colored_eol
= memchr(colored_p
, '\n',
313 colored_pend
- colored_p
);
315 colored_p
= colored_eol
+ 1;
317 colored_p
= colored_pend
;
319 hunk
->colored_end
= colored_p
- colored
->buf
;
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
;
329 file_diff
->hunk
->colored_end
= hunk
->colored_end
;
333 if (marker
== '-' || marker
== '+')
335 * Last hunk ended in non-context line (i.e. it appended lines
336 * to the file, so there are no trailing context lines).
338 hunk
->splittable_into
++;
343 static size_t find_next_line(struct strbuf
*sb
, size_t offset
)
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
);
351 eol
= memchr(sb
->buf
+ offset
, '\n', sb
->len
- offset
);
354 return eol
- sb
->buf
+ 1;
357 static void render_hunk(struct add_p_state
*s
, struct hunk
*hunk
,
358 ssize_t delta
, int colored
, struct strbuf
*out
)
360 struct hunk_header
*header
= &hunk
->header
;
362 if (hunk
->header
.old_offset
!= 0 || hunk
->header
.new_offset
!= 0) {
364 * Generate the hunk header dynamically, except for special
365 * hunks (such as the diff header).
369 unsigned long old_offset
= header
->old_offset
;
370 unsigned long new_offset
= header
->new_offset
;
373 p
= s
->plain
.buf
+ header
->extra_start
;
374 len
= header
->extra_end
- header
->extra_start
;
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
;
384 strbuf_addf(out
, "@@ -%lu,%lu +%lu,%lu @@",
385 old_offset
, header
->old_count
,
386 new_offset
, header
->new_count
);
388 strbuf_add(out
, p
, len
);
390 strbuf_addf(out
, "%s\n", GIT_COLOR_RESET
);
392 strbuf_addch(out
, '\n');
396 strbuf_add(out
, s
->colored
.buf
+ hunk
->colored_start
,
397 hunk
->colored_end
- hunk
->colored_start
);
399 strbuf_add(out
, s
->plain
.buf
+ hunk
->start
,
400 hunk
->end
- hunk
->start
);
403 static void render_diff_header(struct add_p_state
*s
,
404 struct file_diff
*file_diff
, int colored
,
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.
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
;
416 if (!skip_mode_change
) {
417 render_hunk(s
, head
, 0, colored
, out
);
422 const char *p
= s
->colored
.buf
;
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
);
429 const char *p
= s
->plain
.buf
;
431 strbuf_add(out
, p
+ head
->start
, first
->start
- head
->start
);
432 strbuf_add(out
, p
+ first
->end
, head
->end
- first
->end
);
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
)
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
;
445 if (hunk
->use
!= USE_HUNK
)
449 /* We simply skip the colored part (if any) when merging hunks */
450 merged
->colored_start
= merged
->colored_end
= 0;
452 for (; i
+ 1 < file_diff
->hunk_nr
; i
++) {
454 next
= &hunk
->header
;
457 * Stop merging hunks when:
459 * - the hunk is not selected for use, or
460 * - the hunk does not overlap with the already-merged hunk(s)
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
)
469 merged
->end
= hunk
->end
;
470 merged
->colored_end
= hunk
->colored_end
;
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
;
478 if (i
== *hunk_index
)
485 static void reassemble_patch(struct add_p_state
*s
,
486 struct file_diff
*file_diff
, struct strbuf
*out
)
492 render_diff_header(s
, file_diff
, 0, out
);
494 for (i
= file_diff
->mode_change
; i
< file_diff
->hunk_nr
; i
++) {
495 struct hunk merged
= { 0 };
497 hunk
= file_diff
->hunk
+ i
;
498 if (hunk
->use
!= USE_HUNK
)
499 delta
+= hunk
->header
.old_count
500 - hunk
->header
.new_count
;
502 /* merge overlapping hunks into a temporary hunk */
503 if (merge_hunks(s
, file_diff
, &i
, &merged
))
506 render_hunk(s
, hunk
, delta
, 0, out
);
511 static int split_hunk(struct add_p_state
*s
, struct file_diff
*file_diff
,
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
;
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
);
525 if (hunk
->splittable_into
< 2)
527 splittable_into
= hunk
->splittable_into
;
530 colored_end
= hunk
->colored_end
;
532 remaining
= hunk
->header
;
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
)
541 hunk
= file_diff
->hunk
+ hunk_index
;
542 hunk
->splittable_into
= 1;
543 memset(hunk
+ 1, 0, (splittable_into
- 1) * sizeof(*hunk
));
545 header
= &hunk
->header
;
546 header
->old_count
= header
->new_count
= 0;
548 current
= hunk
->start
;
550 colored_current
= hunk
->colored_start
;
552 context_line_count
= 0;
554 while (splittable_into
> 1) {
555 ch
= s
->plain
.buf
[current
];
558 BUG("buffer overrun while splitting hunks");
561 * Is this the first context line after a chain of +/- lines?
562 * Then record the start of the next split hunk.
564 if ((marker
== '-' || marker
== '+') && ch
== ' ') {
566 hunk
[1].start
= current
;
568 hunk
[1].colored_start
= colored_current
;
569 context_line_count
= 0;
573 * Was the previous line a +/- one? Alternatively, is this the
574 * first line (and not a +/- one)?
576 * Then just increment the appropriate counter and continue
577 * with the next line.
579 if (marker
!= ' ' || (ch
!= '-' && ch
!= '+')) {
581 /* Comment lines are attached to the previous line */
583 ch
= marker
? marker
: ' ';
585 /* current hunk not done yet */
587 context_line_count
++;
593 BUG("unhandled diff marker: '%c'", ch
);
595 current
= find_next_line(&s
->plain
, current
);
598 find_next_line(&s
->colored
,
604 * We got us the start of a new hunk!
606 * This is a context line, so it is shared with the previous
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
);
616 header
->old_count
= context_line_count
;
617 header
->new_count
= context_line_count
;
618 context_line_count
= 0;
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
;
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
;
634 /* add one split hunk */
635 header
->old_count
+= context_line_count
;
636 header
->new_count
+= context_line_count
;
640 hunk
->colored_end
= colored_current
;
643 hunk
->splittable_into
= 1;
644 hunk
->use
= hunk
[-1].use
;
645 header
= &hunk
->header
;
647 header
->old_count
= header
->new_count
= context_line_count
;
648 context_line_count
= 0;
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
;
665 hunk
->colored_end
= colored_end
;
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"
682 static int patch_update_file(struct add_p_state
*s
,
683 struct file_diff
*file_diff
)
685 size_t hunk_index
= 0;
686 ssize_t i
, undecided_previous
, undecided_next
;
689 struct child_process cp
= CHILD_PROCESS_INIT
;
690 int colored
= !!s
->colored
.len
;
691 enum prompt_mode_type prompt_mode_type
;
693 if (!file_diff
->hunk_nr
)
696 strbuf_reset(&s
->buf
);
697 render_diff_header(s
, file_diff
, colored
, &s
->buf
);
698 fputs(s
->buf
.buf
, stdout
);
700 if (hunk_index
>= file_diff
->hunk_nr
)
702 hunk
= file_diff
->hunk
+ hunk_index
;
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
;
712 for (i
= hunk_index
+ 1; i
< file_diff
->hunk_nr
; i
++)
713 if (file_diff
->hunk
[i
].use
== UNDECIDED_HUNK
) {
718 /* Everything decided? */
719 if (undecided_previous
< 0 && undecided_next
< 0 &&
720 hunk
->use
!= UNDECIDED_HUNK
)
723 strbuf_reset(&s
->buf
);
724 render_hunk(s
, hunk
, 0, colored
, &s
->buf
);
725 fputs(s
->buf
.buf
, stdout
);
727 strbuf_reset(&s
->buf
);
728 if (undecided_previous
>= 0)
729 strbuf_addstr(&s
->buf
, ",k");
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");
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
;
744 prompt_mode_type
= PROMPT_HUNK
;
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
);
753 if (strbuf_getline(&s
->answer
, stdin
) == EOF
)
755 strbuf_trim_trailing_newline(&s
->answer
);
759 ch
= tolower(s
->answer
.buf
[0]);
761 hunk
->use
= USE_HUNK
;
763 hunk_index
= undecided_next
< 0 ?
764 file_diff
->hunk_nr
: undecided_next
;
765 } else if (ch
== 'n') {
766 hunk
->use
= SKIP_HUNK
;
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
;
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
;
780 } else if (s
->answer
.buf
[0] == 'K') {
784 err(s
, _("No previous hunk"));
785 } else if (s
->answer
.buf
[0] == 'J') {
786 if (hunk_index
+ 1 < file_diff
->hunk_nr
)
789 err(s
, _("No next hunk"));
790 } else if (s
->answer
.buf
[0] == 'k') {
791 if (undecided_previous
>= 0)
792 hunk_index
= undecided_previous
;
794 err(s
, _("No previous hunk"));
795 } else if (s
->answer
.buf
[0] == 'j') {
796 if (undecided_next
>= 0)
797 hunk_index
= undecided_next
;
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
);
810 color_fprintf(stdout
, s
->s
.help_color
,
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
)
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
);
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
,
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
);
838 int run_add_p(struct repository
*r
, const struct pathspec
*ps
)
840 struct add_p_state s
= {
841 { r
}, STRBUF_INIT
, STRBUF_INIT
, STRBUF_INIT
, STRBUF_INIT
845 init_add_i_state(&s
.s
, r
);
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
);
856 for (i
= 0; i
< s
.file_diff_nr
; i
++)
857 if (patch_update_file(&s
, s
.file_diff
+ i
))
860 strbuf_release(&s
.answer
);
861 strbuf_release(&s
.buf
);
862 strbuf_release(&s
.plain
);
863 strbuf_release(&s
.colored
);