]> git.ipfire.org Git - thirdparty/git.git/blame - add-patch.c
built-in stash: use the built-in `git add -p` if so configured
[thirdparty/git.git] / add-patch.c
CommitLineData
f6aa7ecc
JS
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"
e3bd11b4 7#include "color.h"
25ea47af
JS
8#include "diff.h"
9
0ecd9d27 10enum prompt_mode_type {
d2a233cb
JS
11 PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,
12 PROMPT_MODE_MAX, /* must be last */
0ecd9d27
JS
13};
14
d2a233cb
JS
15struct patch_mode {
16 /*
17 * The magic constant 4 is chosen such that all patch modes
18 * provide enough space for three command-line arguments followed by a
19 * trailing `NULL`.
20 */
21 const char *diff_cmd[4], *apply_args[4], *apply_check_args[4];
36bae1dc 22 unsigned is_reverse:1, index_only:1, apply_for_checkout:1;
d2a233cb
JS
23 const char *prompt_mode[PROMPT_MODE_MAX];
24 const char *edit_hunk_hint, *help_patch_text;
25};
26
27static struct patch_mode patch_mode_add = {
28 .diff_cmd = { "diff-files", NULL },
29 .apply_args = { "--cached", NULL },
30 .apply_check_args = { "--cached", NULL },
31 .prompt_mode = {
32 N_("Stage mode change [y,n,q,a,d%s,?]? "),
33 N_("Stage deletion [y,n,q,a,d%s,?]? "),
34 N_("Stage this hunk [y,n,q,a,d%s,?]? ")
35 },
36 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
37 "will immediately be marked for staging."),
38 .help_patch_text =
39 N_("y - stage this hunk\n"
40 "n - do not stage this hunk\n"
41 "q - quit; do not stage this hunk or any of the remaining "
42 "ones\n"
43 "a - stage this hunk and all later hunks in the file\n"
44 "d - do not stage this hunk or any of the later hunks in "
45 "the file\n")
0ecd9d27
JS
46};
47
36bae1dc
JS
48static struct patch_mode patch_mode_stash = {
49 .diff_cmd = { "diff-index", "HEAD", NULL },
50 .apply_args = { "--cached", NULL },
51 .apply_check_args = { "--cached", NULL },
52 .prompt_mode = {
53 N_("Stash mode change [y,n,q,a,d%s,?]? "),
54 N_("Stash deletion [y,n,q,a,d%s,?]? "),
55 N_("Stash this hunk [y,n,q,a,d%s,?]? "),
56 },
57 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
58 "will immediately be marked for stashing."),
59 .help_patch_text =
60 N_("y - stash this hunk\n"
61 "n - do not stash this hunk\n"
62 "q - quit; do not stash this hunk or any of the remaining "
63 "ones\n"
64 "a - stash this hunk and all later hunks in the file\n"
65 "d - do not stash this hunk or any of the later hunks in "
66 "the file\n"),
67};
68
69static struct patch_mode patch_mode_reset_head = {
70 .diff_cmd = { "diff-index", "--cached", NULL },
71 .apply_args = { "-R", "--cached", NULL },
72 .apply_check_args = { "-R", "--cached", NULL },
73 .is_reverse = 1,
74 .index_only = 1,
75 .prompt_mode = {
76 N_("Unstage mode change [y,n,q,a,d%s,?]? "),
77 N_("Unstage deletion [y,n,q,a,d%s,?]? "),
78 N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
79 },
80 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
81 "will immediately be marked for unstaging."),
82 .help_patch_text =
83 N_("y - unstage this hunk\n"
84 "n - do not unstage this hunk\n"
85 "q - quit; do not unstage this hunk or any of the remaining "
86 "ones\n"
87 "a - unstage this hunk and all later hunks in the file\n"
88 "d - do not unstage this hunk or any of the later hunks in "
89 "the file\n"),
90};
91
92static struct patch_mode patch_mode_reset_nothead = {
93 .diff_cmd = { "diff-index", "-R", "--cached", NULL },
94 .apply_args = { "--cached", NULL },
95 .apply_check_args = { "--cached", NULL },
96 .index_only = 1,
97 .prompt_mode = {
98 N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
99 N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
100 N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
101 },
102 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
103 "will immediately be marked for applying."),
104 .help_patch_text =
105 N_("y - apply this hunk to index\n"
106 "n - do not apply this hunk to index\n"
107 "q - quit; do not apply this hunk or any of the remaining "
108 "ones\n"
109 "a - apply this hunk and all later hunks in the file\n"
110 "d - do not apply this hunk or any of the later hunks in "
111 "the file\n"),
112};
113
25ea47af
JS
114struct hunk_header {
115 unsigned long old_offset, old_count, new_offset, new_count;
116 /*
117 * Start/end offsets to the extra text after the second `@@` in the
118 * hunk header, e.g. the function signature. This is expected to
119 * include the newline.
120 */
121 size_t extra_start, extra_end, colored_extra_start, colored_extra_end;
122};
f6aa7ecc
JS
123
124struct hunk {
510aeca1 125 size_t start, end, colored_start, colored_end, splittable_into;
bcdd297b 126 ssize_t delta;
f6aa7ecc 127 enum { UNDECIDED_HUNK = 0, SKIP_HUNK, USE_HUNK } use;
25ea47af 128 struct hunk_header header;
f6aa7ecc
JS
129};
130
131struct add_p_state {
25ea47af 132 struct add_i_state s;
f6aa7ecc
JS
133 struct strbuf answer, buf;
134
135 /* parsed diff */
e3bd11b4 136 struct strbuf plain, colored;
80399aec
JS
137 struct file_diff {
138 struct hunk head;
139 struct hunk *hunk;
140 size_t hunk_nr, hunk_alloc;
2e408319 141 unsigned deleted:1, mode_change:1,binary:1;
80399aec
JS
142 } *file_diff;
143 size_t file_diff_nr;
d2a233cb
JS
144
145 /* patch mode */
146 struct patch_mode *mode;
147 const char *revision;
f6aa7ecc
JS
148};
149
7584dd3c
JS
150static void err(struct add_p_state *s, const char *fmt, ...)
151{
152 va_list args;
153
154 va_start(args, fmt);
155 fputs(s->s.error_color, stderr);
156 vfprintf(stderr, fmt, args);
157 fputs(s->s.reset_color, stderr);
158 fputc('\n', stderr);
159 va_end(args);
160}
161
f6aa7ecc
JS
162static void setup_child_process(struct add_p_state *s,
163 struct child_process *cp, ...)
164{
165 va_list ap;
166 const char *arg;
167
168 va_start(ap, cp);
169 while ((arg = va_arg(ap, const char *)))
170 argv_array_push(&cp->args, arg);
171 va_end(ap);
172
173 cp->git_cmd = 1;
174 argv_array_pushf(&cp->env_array,
25ea47af
JS
175 INDEX_ENVIRONMENT "=%s", s->s.r->index_file);
176}
177
178static int parse_range(const char **p,
179 unsigned long *offset, unsigned long *count)
180{
181 char *pend;
182
183 *offset = strtoul(*p, &pend, 10);
184 if (pend == *p)
185 return -1;
186 if (*pend != ',') {
187 *count = 1;
188 *p = pend;
189 return 0;
190 }
191 *count = strtoul(pend + 1, (char **)p, 10);
192 return *p == pend + 1 ? -1 : 0;
193}
194
195static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk)
196{
197 struct hunk_header *header = &hunk->header;
198 const char *line = s->plain.buf + hunk->start, *p = line;
199 char *eol = memchr(p, '\n', s->plain.len - hunk->start);
200
201 if (!eol)
202 eol = s->plain.buf + s->plain.len;
203
204 if (!skip_prefix(p, "@@ -", &p) ||
205 parse_range(&p, &header->old_offset, &header->old_count) < 0 ||
206 !skip_prefix(p, " +", &p) ||
207 parse_range(&p, &header->new_offset, &header->new_count) < 0 ||
208 !skip_prefix(p, " @@", &p))
209 return error(_("could not parse hunk header '%.*s'"),
210 (int)(eol - line), line);
211
212 hunk->start = eol - s->plain.buf + (*eol == '\n');
213 header->extra_start = p - s->plain.buf;
214 header->extra_end = hunk->start;
215
216 if (!s->colored.len) {
217 header->colored_extra_start = header->colored_extra_end = 0;
218 return 0;
219 }
220
221 /* Now find the extra text in the colored diff */
222 line = s->colored.buf + hunk->colored_start;
223 eol = memchr(line, '\n', s->colored.len - hunk->colored_start);
224 if (!eol)
225 eol = s->colored.buf + s->colored.len;
226 p = memmem(line, eol - line, "@@ -", 4);
227 if (!p)
228 return error(_("could not parse colored hunk header '%.*s'"),
229 (int)(eol - line), line);
230 p = memmem(p + 4, eol - p - 4, " @@", 3);
231 if (!p)
232 return error(_("could not parse colored hunk header '%.*s'"),
233 (int)(eol - line), line);
234 hunk->colored_start = eol - s->colored.buf + (*eol == '\n');
235 header->colored_extra_start = p + 3 - s->colored.buf;
236 header->colored_extra_end = hunk->colored_start;
237
238 return 0;
f6aa7ecc
JS
239}
240
5906d5de
JS
241static int is_octal(const char *p, size_t len)
242{
243 if (!len)
244 return 0;
245
246 while (len--)
247 if (*p < '0' || *(p++) > '7')
248 return 0;
249 return 1;
250}
251
f6aa7ecc
JS
252static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
253{
e3bd11b4
JS
254 struct argv_array args = ARGV_ARRAY_INIT;
255 struct strbuf *plain = &s->plain, *colored = NULL;
f6aa7ecc 256 struct child_process cp = CHILD_PROCESS_INIT;
510aeca1 257 char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
80399aec
JS
258 size_t file_diff_alloc = 0, i, color_arg_index;
259 struct file_diff *file_diff = NULL;
f6aa7ecc
JS
260 struct hunk *hunk = NULL;
261 int res;
262
d2a233cb
JS
263 argv_array_pushv(&args, s->mode->diff_cmd);
264 if (s->revision) {
265 struct object_id oid;
266 argv_array_push(&args,
267 /* could be on an unborn branch */
268 !strcmp("HEAD", s->revision) &&
269 get_oid("HEAD", &oid) ?
270 empty_tree_oid_hex() : s->revision);
271 }
272 color_arg_index = args.argc;
f6aa7ecc 273 /* Use `--no-color` explicitly, just in case `diff.color = always`. */
d2a233cb 274 argv_array_pushl(&args, "--no-color", "-p", "--", NULL);
f6aa7ecc 275 for (i = 0; i < ps->nr; i++)
e3bd11b4 276 argv_array_push(&args, ps->items[i].original);
f6aa7ecc 277
e3bd11b4
JS
278 setup_child_process(s, &cp, NULL);
279 cp.argv = args.argv;
f6aa7ecc 280 res = capture_command(&cp, plain, 0);
e3bd11b4
JS
281 if (res) {
282 argv_array_clear(&args);
f6aa7ecc 283 return error(_("could not parse diff"));
e3bd11b4
JS
284 }
285 if (!plain->len) {
286 argv_array_clear(&args);
f6aa7ecc 287 return 0;
e3bd11b4 288 }
f6aa7ecc
JS
289 strbuf_complete_line(plain);
290
e3bd11b4
JS
291 if (want_color_fd(1, -1)) {
292 struct child_process colored_cp = CHILD_PROCESS_INIT;
293
294 setup_child_process(s, &colored_cp, NULL);
295 xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
296 colored_cp.argv = args.argv;
297 colored = &s->colored;
298 res = capture_command(&colored_cp, colored, 0);
299 argv_array_clear(&args);
300 if (res)
301 return error(_("could not parse colored diff"));
302 strbuf_complete_line(colored);
303 colored_p = colored->buf;
304 colored_pend = colored_p + colored->len;
305 }
306 argv_array_clear(&args);
307
80399aec 308 /* parse files and hunks */
f6aa7ecc
JS
309 p = plain->buf;
310 pend = p + plain->len;
311 while (p != pend) {
312 char *eol = memchr(p, '\n', pend - p);
5906d5de 313 const char *deleted = NULL, *mode_change = NULL;
47dc4fd5 314
f6aa7ecc
JS
315 if (!eol)
316 eol = pend;
317
318 if (starts_with(p, "diff ")) {
80399aec
JS
319 s->file_diff_nr++;
320 ALLOC_GROW(s->file_diff, s->file_diff_nr,
321 file_diff_alloc);
322 file_diff = s->file_diff + s->file_diff_nr - 1;
323 memset(file_diff, 0, sizeof(*file_diff));
324 hunk = &file_diff->head;
325 hunk->start = p - plain->buf;
326 if (colored_p)
327 hunk->colored_start = colored_p - colored->buf;
510aeca1 328 marker = '\0';
f6aa7ecc
JS
329 } else if (p == plain->buf)
330 BUG("diff starts with unexpected line:\n"
331 "%.*s\n", (int)(eol - p), p);
47dc4fd5
JS
332 else if (file_diff->deleted)
333 ; /* keep the rest of the file in a single "hunk" */
334 else if (starts_with(p, "@@ ") ||
335 (hunk == &file_diff->head &&
336 skip_prefix(p, "deleted file", &deleted))) {
510aeca1
JS
337 if (marker == '-' || marker == '+')
338 /*
339 * Should not happen; previous hunk did not end
340 * in a context line? Handle it anyway.
341 */
342 hunk->splittable_into++;
343
80399aec
JS
344 file_diff->hunk_nr++;
345 ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
346 file_diff->hunk_alloc);
347 hunk = file_diff->hunk + file_diff->hunk_nr - 1;
f6aa7ecc
JS
348 memset(hunk, 0, sizeof(*hunk));
349
350 hunk->start = p - plain->buf;
e3bd11b4
JS
351 if (colored)
352 hunk->colored_start = colored_p - colored->buf;
25ea47af 353
47dc4fd5
JS
354 if (deleted)
355 file_diff->deleted = 1;
356 else if (parse_hunk_header(s, hunk) < 0)
25ea47af 357 return -1;
510aeca1
JS
358
359 /*
360 * Start counting into how many hunks this one can be
361 * split
362 */
363 marker = *p;
5906d5de
JS
364 } else if (hunk == &file_diff->head &&
365 skip_prefix(p, "old mode ", &mode_change) &&
366 is_octal(mode_change, eol - mode_change)) {
367 if (file_diff->mode_change)
368 BUG("double mode change?\n\n%.*s",
369 (int)(eol - plain->buf), plain->buf);
370 if (file_diff->hunk_nr++)
371 BUG("mode change in the middle?\n\n%.*s",
372 (int)(eol - plain->buf), plain->buf);
373
374 /*
375 * Do *not* change `hunk`: the mode change pseudo-hunk
376 * is _part of_ the header "hunk".
377 */
378 file_diff->mode_change = 1;
379 ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,
380 file_diff->hunk_alloc);
381 memset(file_diff->hunk, 0, sizeof(struct hunk));
382 file_diff->hunk->start = p - plain->buf;
383 if (colored_p)
384 file_diff->hunk->colored_start =
385 colored_p - colored->buf;
386 } else if (hunk == &file_diff->head &&
387 skip_prefix(p, "new mode ", &mode_change) &&
388 is_octal(mode_change, eol - mode_change)) {
389
390 /*
391 * Extend the "mode change" pseudo-hunk to include also
392 * the "new mode" line.
393 */
394 if (!file_diff->mode_change)
395 BUG("'new mode' without 'old mode'?\n\n%.*s",
396 (int)(eol - plain->buf), plain->buf);
397 if (file_diff->hunk_nr != 1)
398 BUG("mode change in the middle?\n\n%.*s",
399 (int)(eol - plain->buf), plain->buf);
400 if (p - plain->buf != file_diff->hunk->end)
401 BUG("'new mode' does not immediately follow "
402 "'old mode'?\n\n%.*s",
403 (int)(eol - plain->buf), plain->buf);
2e408319
JS
404 } else if (hunk == &file_diff->head &&
405 starts_with(p, "Binary files "))
406 file_diff->binary = 1;
f6aa7ecc 407
5906d5de
JS
408 if (file_diff->deleted && file_diff->mode_change)
409 BUG("diff contains delete *and* a mode change?!?\n%.*s",
410 (int)(eol - (plain->buf + file_diff->head.start)),
411 plain->buf + file_diff->head.start);
412
510aeca1
JS
413 if ((marker == '-' || marker == '+') && *p == ' ')
414 hunk->splittable_into++;
415 if (marker && *p != '\\')
416 marker = *p;
417
f6aa7ecc
JS
418 p = eol == pend ? pend : eol + 1;
419 hunk->end = p - plain->buf;
e3bd11b4
JS
420
421 if (colored) {
422 char *colored_eol = memchr(colored_p, '\n',
423 colored_pend - colored_p);
424 if (colored_eol)
425 colored_p = colored_eol + 1;
426 else
427 colored_p = colored_pend;
428
429 hunk->colored_end = colored_p - colored->buf;
430 }
5906d5de
JS
431
432 if (mode_change) {
433 if (file_diff->hunk_nr != 1)
434 BUG("mode change in hunk #%d???",
435 (int)file_diff->hunk_nr);
436 /* Adjust the end of the "mode change" pseudo-hunk */
437 file_diff->hunk->end = hunk->end;
438 if (colored)
439 file_diff->hunk->colored_end = hunk->colored_end;
440 }
f6aa7ecc
JS
441 }
442
510aeca1
JS
443 if (marker == '-' || marker == '+')
444 /*
445 * Last hunk ended in non-context line (i.e. it appended lines
446 * to the file, so there are no trailing context lines).
447 */
448 hunk->splittable_into++;
449
f6aa7ecc
JS
450 return 0;
451}
452
510aeca1
JS
453static size_t find_next_line(struct strbuf *sb, size_t offset)
454{
455 char *eol;
456
457 if (offset >= sb->len)
458 BUG("looking for next line beyond buffer (%d >= %d)\n%s",
459 (int)offset, (int)sb->len, sb->buf);
460
461 eol = memchr(sb->buf + offset, '\n', sb->len - offset);
462 if (!eol)
463 return sb->len;
464 return eol - sb->buf + 1;
465}
466
f6aa7ecc 467static void render_hunk(struct add_p_state *s, struct hunk *hunk,
25ea47af 468 ssize_t delta, int colored, struct strbuf *out)
f6aa7ecc 469{
25ea47af
JS
470 struct hunk_header *header = &hunk->header;
471
472 if (hunk->header.old_offset != 0 || hunk->header.new_offset != 0) {
473 /*
474 * Generate the hunk header dynamically, except for special
475 * hunks (such as the diff header).
476 */
477 const char *p;
478 size_t len;
479 unsigned long old_offset = header->old_offset;
480 unsigned long new_offset = header->new_offset;
481
482 if (!colored) {
483 p = s->plain.buf + header->extra_start;
484 len = header->extra_end - header->extra_start;
485 } else {
486 strbuf_addstr(out, s->s.fraginfo_color);
487 p = s->colored.buf + header->colored_extra_start;
488 len = header->colored_extra_end
489 - header->colored_extra_start;
490 }
491
d2a233cb
JS
492 if (s->mode->is_reverse)
493 old_offset -= delta;
494 else
495 new_offset += delta;
25ea47af
JS
496
497 strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@",
498 old_offset, header->old_count,
499 new_offset, header->new_count);
500 if (len)
501 strbuf_add(out, p, len);
502 else if (colored)
503 strbuf_addf(out, "%s\n", GIT_COLOR_RESET);
504 else
505 strbuf_addch(out, '\n');
506 }
507
e3bd11b4
JS
508 if (colored)
509 strbuf_add(out, s->colored.buf + hunk->colored_start,
510 hunk->colored_end - hunk->colored_start);
511 else
512 strbuf_add(out, s->plain.buf + hunk->start,
513 hunk->end - hunk->start);
f6aa7ecc
JS
514}
515
5906d5de
JS
516static void render_diff_header(struct add_p_state *s,
517 struct file_diff *file_diff, int colored,
518 struct strbuf *out)
519{
520 /*
521 * If there was a mode change, the first hunk is a pseudo hunk that
522 * corresponds to the mode line in the header. If the user did not want
523 * to stage that "hunk", we actually have to cut it out from the header.
524 */
525 int skip_mode_change =
526 file_diff->mode_change && file_diff->hunk->use != USE_HUNK;
527 struct hunk *head = &file_diff->head, *first = file_diff->hunk;
528
529 if (!skip_mode_change) {
530 render_hunk(s, head, 0, colored, out);
531 return;
532 }
533
534 if (colored) {
535 const char *p = s->colored.buf;
536
537 strbuf_add(out, p + head->colored_start,
538 first->colored_start - head->colored_start);
539 strbuf_add(out, p + first->colored_end,
540 head->colored_end - first->colored_end);
541 } else {
542 const char *p = s->plain.buf;
543
544 strbuf_add(out, p + head->start, first->start - head->start);
545 strbuf_add(out, p + first->end, head->end - first->end);
546 }
547}
548
11f2c0da
JS
549/* Coalesce hunks again that were split */
550static int merge_hunks(struct add_p_state *s, struct file_diff *file_diff,
bcdd297b 551 size_t *hunk_index, int use_all, struct hunk *merged)
11f2c0da 552{
bcdd297b 553 size_t i = *hunk_index, delta;
11f2c0da
JS
554 struct hunk *hunk = file_diff->hunk + i;
555 /* `header` corresponds to the merged hunk */
556 struct hunk_header *header = &merged->header, *next;
557
bcdd297b 558 if (!use_all && hunk->use != USE_HUNK)
11f2c0da
JS
559 return 0;
560
561 *merged = *hunk;
562 /* We simply skip the colored part (if any) when merging hunks */
563 merged->colored_start = merged->colored_end = 0;
564
565 for (; i + 1 < file_diff->hunk_nr; i++) {
566 hunk++;
567 next = &hunk->header;
568
569 /*
570 * Stop merging hunks when:
571 *
572 * - the hunk is not selected for use, or
573 * - the hunk does not overlap with the already-merged hunk(s)
574 */
bcdd297b
JS
575 if ((!use_all && hunk->use != USE_HUNK) ||
576 header->new_offset >= next->new_offset + merged->delta ||
577 header->new_offset + header->new_count
578 < next->new_offset + merged->delta)
11f2c0da
JS
579 break;
580
bcdd297b
JS
581 /*
582 * If the hunks were not edited, and overlap, we can simply
583 * extend the line range.
584 */
585 if (merged->start < hunk->start && merged->end > hunk->start) {
586 merged->end = hunk->end;
587 merged->colored_end = hunk->colored_end;
588 delta = 0;
589 } else {
590 const char *plain = s->plain.buf;
591 size_t overlapping_line_count = header->new_offset
592 + header->new_count - merged->delta
593 - next->new_offset;
594 size_t overlap_end = hunk->start;
595 size_t overlap_start = overlap_end;
596 size_t overlap_next, len, j;
597
598 /*
599 * One of the hunks was edited: the modified hunk was
600 * appended to the strbuf `s->plain`.
601 *
602 * Let's ensure that at least the last context line of
603 * the first hunk overlaps with the corresponding line
604 * of the second hunk, and then merge.
605 */
606 for (j = 0; j < overlapping_line_count; j++) {
607 overlap_next = find_next_line(&s->plain,
608 overlap_end);
609
610 if (overlap_next > hunk->end)
611 BUG("failed to find %d context lines "
612 "in:\n%.*s",
613 (int)overlapping_line_count,
614 (int)(hunk->end - hunk->start),
615 plain + hunk->start);
616
617 if (plain[overlap_end] != ' ')
618 return error(_("expected context line "
619 "#%d in\n%.*s"),
620 (int)(j + 1),
621 (int)(hunk->end
622 - hunk->start),
623 plain + hunk->start);
624
625 overlap_start = overlap_end;
626 overlap_end = overlap_next;
627 }
628 len = overlap_end - overlap_start;
629
630 if (len > merged->end - merged->start ||
631 memcmp(plain + merged->end - len,
632 plain + overlap_start, len))
633 return error(_("hunks do not overlap:\n%.*s\n"
634 "\tdoes not end with:\n%.*s"),
635 (int)(merged->end - merged->start),
636 plain + merged->start,
637 (int)len, plain + overlap_start);
638
639 /*
640 * Since the start-end ranges are not adjacent, we
641 * cannot simply take the union of the ranges. To
642 * address that, we temporarily append the union of the
643 * lines to the `plain` strbuf.
644 */
645 if (merged->end != s->plain.len) {
646 size_t start = s->plain.len;
647
648 strbuf_add(&s->plain, plain + merged->start,
649 merged->end - merged->start);
650 plain = s->plain.buf;
651 merged->start = start;
652 merged->end = s->plain.len;
653 }
654
655 strbuf_add(&s->plain,
656 plain + overlap_end,
657 hunk->end - overlap_end);
658 merged->end = s->plain.len;
659 merged->splittable_into += hunk->splittable_into;
660 delta = merged->delta;
661 merged->delta += hunk->delta;
662 }
11f2c0da
JS
663
664 header->old_count = next->old_offset + next->old_count
665 - header->old_offset;
bcdd297b
JS
666 header->new_count = next->new_offset + delta
667 + next->new_count - header->new_offset;
11f2c0da
JS
668 }
669
670 if (i == *hunk_index)
671 return 0;
672
673 *hunk_index = i;
674 return 1;
675}
676
80399aec 677static void reassemble_patch(struct add_p_state *s,
bcdd297b
JS
678 struct file_diff *file_diff, int use_all,
679 struct strbuf *out)
f6aa7ecc
JS
680{
681 struct hunk *hunk;
bcdd297b 682 size_t save_len = s->plain.len, i;
25ea47af 683 ssize_t delta = 0;
f6aa7ecc 684
5906d5de 685 render_diff_header(s, file_diff, 0, out);
f6aa7ecc 686
5906d5de 687 for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) {
11f2c0da
JS
688 struct hunk merged = { 0 };
689
80399aec 690 hunk = file_diff->hunk + i;
bcdd297b 691 if (!use_all && hunk->use != USE_HUNK)
25ea47af
JS
692 delta += hunk->header.old_count
693 - hunk->header.new_count;
11f2c0da
JS
694 else {
695 /* merge overlapping hunks into a temporary hunk */
bcdd297b 696 if (merge_hunks(s, file_diff, &i, use_all, &merged))
11f2c0da
JS
697 hunk = &merged;
698
25ea47af 699 render_hunk(s, hunk, delta, 0, out);
bcdd297b
JS
700
701 /*
702 * In case `merge_hunks()` used `plain` as a scratch
703 * pad (this happens when an edited hunk had to be
704 * coalesced with another hunk).
705 */
706 strbuf_setlen(&s->plain, save_len);
707
708 delta += hunk->delta;
11f2c0da 709 }
f6aa7ecc
JS
710 }
711}
712
510aeca1
JS
713static int split_hunk(struct add_p_state *s, struct file_diff *file_diff,
714 size_t hunk_index)
715{
716 int colored = !!s->colored.len, first = 1;
717 struct hunk *hunk = file_diff->hunk + hunk_index;
718 size_t splittable_into;
719 size_t end, colored_end, current, colored_current = 0, context_line_count;
720 struct hunk_header remaining, *header;
721 char marker, ch;
722
723 if (hunk_index >= file_diff->hunk_nr)
724 BUG("invalid hunk index: %d (must be >= 0 and < %d)",
725 (int)hunk_index, (int)file_diff->hunk_nr);
726
727 if (hunk->splittable_into < 2)
728 return 0;
729 splittable_into = hunk->splittable_into;
730
731 end = hunk->end;
732 colored_end = hunk->colored_end;
733
734 remaining = hunk->header;
735
736 file_diff->hunk_nr += splittable_into - 1;
737 ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr, file_diff->hunk_alloc);
738 if (hunk_index + splittable_into < file_diff->hunk_nr)
739 memmove(file_diff->hunk + hunk_index + splittable_into,
740 file_diff->hunk + hunk_index + 1,
741 (file_diff->hunk_nr - hunk_index - splittable_into)
742 * sizeof(*hunk));
743 hunk = file_diff->hunk + hunk_index;
744 hunk->splittable_into = 1;
745 memset(hunk + 1, 0, (splittable_into - 1) * sizeof(*hunk));
746
747 header = &hunk->header;
748 header->old_count = header->new_count = 0;
749
750 current = hunk->start;
751 if (colored)
752 colored_current = hunk->colored_start;
753 marker = '\0';
754 context_line_count = 0;
755
756 while (splittable_into > 1) {
757 ch = s->plain.buf[current];
758
759 if (!ch)
760 BUG("buffer overrun while splitting hunks");
761
762 /*
763 * Is this the first context line after a chain of +/- lines?
764 * Then record the start of the next split hunk.
765 */
766 if ((marker == '-' || marker == '+') && ch == ' ') {
767 first = 0;
768 hunk[1].start = current;
769 if (colored)
770 hunk[1].colored_start = colored_current;
771 context_line_count = 0;
772 }
773
774 /*
775 * Was the previous line a +/- one? Alternatively, is this the
776 * first line (and not a +/- one)?
777 *
778 * Then just increment the appropriate counter and continue
779 * with the next line.
780 */
781 if (marker != ' ' || (ch != '-' && ch != '+')) {
782next_hunk_line:
783 /* Comment lines are attached to the previous line */
784 if (ch == '\\')
785 ch = marker ? marker : ' ';
786
787 /* current hunk not done yet */
788 if (ch == ' ')
789 context_line_count++;
790 else if (ch == '-')
791 header->old_count++;
792 else if (ch == '+')
793 header->new_count++;
794 else
795 BUG("unhandled diff marker: '%c'", ch);
796 marker = ch;
797 current = find_next_line(&s->plain, current);
798 if (colored)
799 colored_current =
800 find_next_line(&s->colored,
801 colored_current);
802 continue;
803 }
804
805 /*
806 * We got us the start of a new hunk!
807 *
808 * This is a context line, so it is shared with the previous
809 * hunk, if any.
810 */
811
812 if (first) {
813 if (header->old_count || header->new_count)
814 BUG("counts are off: %d/%d",
815 (int)header->old_count,
816 (int)header->new_count);
817
818 header->old_count = context_line_count;
819 header->new_count = context_line_count;
820 context_line_count = 0;
821 first = 0;
822 goto next_hunk_line;
823 }
824
825 remaining.old_offset += header->old_count;
826 remaining.old_count -= header->old_count;
827 remaining.new_offset += header->new_count;
828 remaining.new_count -= header->new_count;
829
830 /* initialize next hunk header's offsets */
831 hunk[1].header.old_offset =
832 header->old_offset + header->old_count;
833 hunk[1].header.new_offset =
834 header->new_offset + header->new_count;
835
836 /* add one split hunk */
837 header->old_count += context_line_count;
838 header->new_count += context_line_count;
839
840 hunk->end = current;
841 if (colored)
842 hunk->colored_end = colored_current;
843
844 hunk++;
845 hunk->splittable_into = 1;
846 hunk->use = hunk[-1].use;
847 header = &hunk->header;
848
849 header->old_count = header->new_count = context_line_count;
850 context_line_count = 0;
851
852 splittable_into--;
853 marker = ch;
854 }
855
856 /* last hunk simply gets the rest */
857 if (header->old_offset != remaining.old_offset)
858 BUG("miscounted old_offset: %lu != %lu",
859 header->old_offset, remaining.old_offset);
860 if (header->new_offset != remaining.new_offset)
861 BUG("miscounted new_offset: %lu != %lu",
862 header->new_offset, remaining.new_offset);
863 header->old_count = remaining.old_count;
864 header->new_count = remaining.new_count;
865 hunk->end = end;
866 if (colored)
867 hunk->colored_end = colored_end;
868
869 return 0;
870}
871
bcdd297b
JS
872static void recolor_hunk(struct add_p_state *s, struct hunk *hunk)
873{
874 const char *plain = s->plain.buf;
875 size_t current, eol, next;
876
877 if (!s->colored.len)
878 return;
879
880 hunk->colored_start = s->colored.len;
881 for (current = hunk->start; current < hunk->end; ) {
882 for (eol = current; eol < hunk->end; eol++)
883 if (plain[eol] == '\n')
884 break;
885 next = eol + (eol < hunk->end);
886 if (eol > current && plain[eol - 1] == '\r')
887 eol--;
888
889 strbuf_addstr(&s->colored,
890 plain[current] == '-' ?
891 s->s.file_old_color :
892 plain[current] == '+' ?
893 s->s.file_new_color :
894 s->s.context_color);
895 strbuf_add(&s->colored, plain + current, eol - current);
896 strbuf_addstr(&s->colored, GIT_COLOR_RESET);
897 if (next > eol)
898 strbuf_add(&s->colored, plain + eol, next - eol);
899 current = next;
900 }
901 hunk->colored_end = s->colored.len;
902}
903
904static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
905{
906 size_t i;
907
908 strbuf_reset(&s->buf);
909 strbuf_commented_addf(&s->buf, _("Manual hunk edit mode -- see bottom for "
910 "a quick guide.\n"));
911 render_hunk(s, hunk, 0, 0, &s->buf);
912 strbuf_commented_addf(&s->buf,
913 _("---\n"
914 "To remove '%c' lines, make them ' ' lines "
915 "(context).\n"
916 "To remove '%c' lines, delete them.\n"
917 "Lines starting with %c will be removed.\n"),
d2a233cb
JS
918 s->mode->is_reverse ? '+' : '-',
919 s->mode->is_reverse ? '-' : '+',
920 comment_line_char);
921 strbuf_commented_addf(&s->buf, "%s", _(s->mode->edit_hunk_hint));
bcdd297b
JS
922 /*
923 * TRANSLATORS: 'it' refers to the patch mentioned in the previous
924 * messages.
925 */
926 strbuf_commented_addf(&s->buf,
927 _("If it does not apply cleanly, you will be "
928 "given an opportunity to\n"
929 "edit again. If all lines of the hunk are "
930 "removed, then the edit is\n"
931 "aborted and the hunk is left unchanged.\n"));
932
933 if (strbuf_edit_interactively(&s->buf, "addp-hunk-edit.diff", NULL) < 0)
934 return -1;
935
936 /* strip out commented lines */
937 hunk->start = s->plain.len;
938 for (i = 0; i < s->buf.len; ) {
939 size_t next = find_next_line(&s->buf, i);
940
941 if (s->buf.buf[i] != comment_line_char)
942 strbuf_add(&s->plain, s->buf.buf + i, next - i);
943 i = next;
944 }
945
946 hunk->end = s->plain.len;
947 if (hunk->end == hunk->start)
948 /* The user aborted editing by deleting everything */
949 return 0;
950
951 recolor_hunk(s, hunk);
952
953 /*
954 * If the hunk header is intact, parse it, otherwise simply use the
955 * hunk header prior to editing (which will adjust `hunk->start` to
956 * skip the hunk header).
957 */
958 if (s->plain.buf[hunk->start] == '@' &&
959 parse_hunk_header(s, hunk) < 0)
960 return error(_("could not parse hunk header"));
961
962 return 1;
963}
964
965static ssize_t recount_edited_hunk(struct add_p_state *s, struct hunk *hunk,
966 size_t orig_old_count, size_t orig_new_count)
967{
968 struct hunk_header *header = &hunk->header;
969 size_t i;
970
971 header->old_count = header->new_count = 0;
972 for (i = hunk->start; i < hunk->end; ) {
973 switch (s->plain.buf[i]) {
974 case '-':
975 header->old_count++;
976 break;
977 case '+':
978 header->new_count++;
979 break;
980 case ' ': case '\r': case '\n':
981 header->old_count++;
982 header->new_count++;
983 break;
984 }
985
986 i = find_next_line(&s->plain, i);
987 }
988
989 return orig_old_count - orig_new_count
990 - header->old_count + header->new_count;
991}
992
993static int run_apply_check(struct add_p_state *s,
994 struct file_diff *file_diff)
995{
996 struct child_process cp = CHILD_PROCESS_INIT;
997
998 strbuf_reset(&s->buf);
999 reassemble_patch(s, file_diff, 1, &s->buf);
1000
1001 setup_child_process(s, &cp,
d2a233cb
JS
1002 "apply", "--check", NULL);
1003 argv_array_pushv(&cp.args, s->mode->apply_check_args);
bcdd297b
JS
1004 if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
1005 return error(_("'git apply --cached' failed"));
1006
1007 return 0;
1008}
1009
1010static int prompt_yesno(struct add_p_state *s, const char *prompt)
1011{
1012 for (;;) {
1013 color_fprintf(stdout, s->s.prompt_color, "%s", _(prompt));
1014 fflush(stdout);
1015 if (strbuf_getline(&s->answer, stdin) == EOF)
1016 return -1;
1017 strbuf_trim_trailing_newline(&s->answer);
1018 switch (tolower(s->answer.buf[0])) {
1019 case 'n': return 0;
1020 case 'y': return 1;
1021 }
1022 }
1023}
1024
1025static int edit_hunk_loop(struct add_p_state *s,
1026 struct file_diff *file_diff, struct hunk *hunk)
1027{
1028 size_t plain_len = s->plain.len, colored_len = s->colored.len;
1029 struct hunk backup;
1030
1031 backup = *hunk;
1032
1033 for (;;) {
1034 int res = edit_hunk_manually(s, hunk);
1035 if (res == 0) {
1036 /* abandonded */
1037 *hunk = backup;
1038 return -1;
1039 }
1040
1041 if (res > 0) {
1042 hunk->delta +=
1043 recount_edited_hunk(s, hunk,
1044 backup.header.old_count,
1045 backup.header.new_count);
1046 if (!run_apply_check(s, file_diff))
1047 return 0;
1048 }
1049
1050 /* Drop edits (they were appended to s->plain) */
1051 strbuf_setlen(&s->plain, plain_len);
1052 strbuf_setlen(&s->colored, colored_len);
1053 *hunk = backup;
1054
1055 /*
1056 * TRANSLATORS: do not translate [y/n]
1057 * The program will only accept that input at this point.
1058 * Consider translating (saying "no" discards!) as
1059 * (saying "n" for "no" discards!) if the translation
1060 * of the word "no" does not start with n.
1061 */
1062 res = prompt_yesno(s, _("Your edited hunk does not apply. "
1063 "Edit again (saying \"no\" discards!) "
1064 "[y/n]? "));
1065 if (res < 1)
1066 return -1;
1067 }
1068}
1069
9254bdfb
JS
1070#define SUMMARY_HEADER_WIDTH 20
1071#define SUMMARY_LINE_WIDTH 80
1072static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
1073 struct strbuf *out)
1074{
1075 struct hunk_header *header = &hunk->header;
1076 struct strbuf *plain = &s->plain;
1077 size_t len = out->len, i;
1078
1079 strbuf_addf(out, " -%lu,%lu +%lu,%lu ",
1080 header->old_offset, header->old_count,
1081 header->new_offset, header->new_count);
1082 if (out->len - len < SUMMARY_HEADER_WIDTH)
1083 strbuf_addchars(out, ' ',
1084 SUMMARY_HEADER_WIDTH + len - out->len);
1085 for (i = hunk->start; i < hunk->end; i = find_next_line(plain, i))
1086 if (plain->buf[i] != ' ')
1087 break;
1088 if (i < hunk->end)
1089 strbuf_add(out, plain->buf + i, find_next_line(plain, i) - i);
1090 if (out->len - len > SUMMARY_LINE_WIDTH)
1091 strbuf_setlen(out, len + SUMMARY_LINE_WIDTH);
1092 strbuf_complete_line(out);
1093}
1094
1095#define DISPLAY_HUNKS_LINES 20
1096static size_t display_hunks(struct add_p_state *s,
1097 struct file_diff *file_diff, size_t start_index)
1098{
1099 size_t end_index = start_index + DISPLAY_HUNKS_LINES;
1100
1101 if (end_index > file_diff->hunk_nr)
1102 end_index = file_diff->hunk_nr;
1103
1104 while (start_index < end_index) {
1105 struct hunk *hunk = file_diff->hunk + start_index++;
1106
1107 strbuf_reset(&s->buf);
1108 strbuf_addf(&s->buf, "%c%2d: ", hunk->use == USE_HUNK ? '+'
1109 : hunk->use == SKIP_HUNK ? '-' : ' ',
1110 (int)start_index);
1111 summarize_hunk(s, hunk, &s->buf);
1112 fputs(s->buf.buf, stdout);
1113 }
1114
1115 return end_index;
1116}
1117
54d9d9b2
JS
1118static const char help_patch_remainder[] =
1119N_("j - leave this hunk undecided, see next undecided hunk\n"
f6aa7ecc
JS
1120 "J - leave this hunk undecided, see next hunk\n"
1121 "k - leave this hunk undecided, see previous undecided hunk\n"
1122 "K - leave this hunk undecided, see previous hunk\n"
9254bdfb 1123 "g - select a hunk to go to\n"
d6cf8733 1124 "/ - search for a hunk matching the given regex\n"
510aeca1 1125 "s - split the current hunk into smaller hunks\n"
bcdd297b 1126 "e - manually edit the current hunk\n"
f6aa7ecc
JS
1127 "? - print help\n");
1128
80399aec
JS
1129static int patch_update_file(struct add_p_state *s,
1130 struct file_diff *file_diff)
f6aa7ecc
JS
1131{
1132 size_t hunk_index = 0;
1133 ssize_t i, undecided_previous, undecided_next;
1134 struct hunk *hunk;
1135 char ch;
1136 struct child_process cp = CHILD_PROCESS_INIT;
ade246ef 1137 int colored = !!s->colored.len, quit = 0;
0ecd9d27 1138 enum prompt_mode_type prompt_mode_type;
f6aa7ecc 1139
80399aec 1140 if (!file_diff->hunk_nr)
f6aa7ecc
JS
1141 return 0;
1142
1143 strbuf_reset(&s->buf);
5906d5de 1144 render_diff_header(s, file_diff, colored, &s->buf);
f6aa7ecc
JS
1145 fputs(s->buf.buf, stdout);
1146 for (;;) {
80399aec 1147 if (hunk_index >= file_diff->hunk_nr)
f6aa7ecc 1148 hunk_index = 0;
80399aec 1149 hunk = file_diff->hunk + hunk_index;
f6aa7ecc
JS
1150
1151 undecided_previous = -1;
1152 for (i = hunk_index - 1; i >= 0; i--)
80399aec 1153 if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
f6aa7ecc
JS
1154 undecided_previous = i;
1155 break;
1156 }
1157
1158 undecided_next = -1;
80399aec
JS
1159 for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
1160 if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
f6aa7ecc
JS
1161 undecided_next = i;
1162 break;
1163 }
1164
1165 /* Everything decided? */
1166 if (undecided_previous < 0 && undecided_next < 0 &&
1167 hunk->use != UNDECIDED_HUNK)
1168 break;
1169
1170 strbuf_reset(&s->buf);
25ea47af 1171 render_hunk(s, hunk, 0, colored, &s->buf);
f6aa7ecc
JS
1172 fputs(s->buf.buf, stdout);
1173
1174 strbuf_reset(&s->buf);
1175 if (undecided_previous >= 0)
1176 strbuf_addstr(&s->buf, ",k");
1177 if (hunk_index)
1178 strbuf_addstr(&s->buf, ",K");
1179 if (undecided_next >= 0)
1180 strbuf_addstr(&s->buf, ",j");
80399aec 1181 if (hunk_index + 1 < file_diff->hunk_nr)
f6aa7ecc 1182 strbuf_addstr(&s->buf, ",J");
9254bdfb 1183 if (file_diff->hunk_nr > 1)
d6cf8733 1184 strbuf_addstr(&s->buf, ",g,/");
510aeca1
JS
1185 if (hunk->splittable_into > 1)
1186 strbuf_addstr(&s->buf, ",s");
bcdd297b
JS
1187 if (hunk_index + 1 > file_diff->mode_change &&
1188 !file_diff->deleted)
1189 strbuf_addstr(&s->buf, ",e");
0ecd9d27
JS
1190
1191 if (file_diff->deleted)
1192 prompt_mode_type = PROMPT_DELETION;
1193 else if (file_diff->mode_change && !hunk_index)
1194 prompt_mode_type = PROMPT_MODE_CHANGE;
1195 else
1196 prompt_mode_type = PROMPT_HUNK;
1197
12c24cf8
JS
1198 color_fprintf(stdout, s->s.prompt_color,
1199 "(%"PRIuMAX"/%"PRIuMAX") ",
80399aec
JS
1200 (uintmax_t)hunk_index + 1,
1201 (uintmax_t)file_diff->hunk_nr);
12c24cf8 1202 color_fprintf(stdout, s->s.prompt_color,
d2a233cb
JS
1203 _(s->mode->prompt_mode[prompt_mode_type]),
1204 s->buf.buf);
f6aa7ecc
JS
1205 fflush(stdout);
1206 if (strbuf_getline(&s->answer, stdin) == EOF)
1207 break;
1208 strbuf_trim_trailing_newline(&s->answer);
1209
1210 if (!s->answer.len)
1211 continue;
1212 ch = tolower(s->answer.buf[0]);
1213 if (ch == 'y') {
1214 hunk->use = USE_HUNK;
1215soft_increment:
1216 hunk_index = undecided_next < 0 ?
80399aec 1217 file_diff->hunk_nr : undecided_next;
f6aa7ecc
JS
1218 } else if (ch == 'n') {
1219 hunk->use = SKIP_HUNK;
1220 goto soft_increment;
1221 } else if (ch == 'a') {
80399aec
JS
1222 for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
1223 hunk = file_diff->hunk + hunk_index;
f6aa7ecc
JS
1224 if (hunk->use == UNDECIDED_HUNK)
1225 hunk->use = USE_HUNK;
1226 }
ade246ef 1227 } else if (ch == 'd' || ch == 'q') {
80399aec
JS
1228 for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
1229 hunk = file_diff->hunk + hunk_index;
f6aa7ecc
JS
1230 if (hunk->use == UNDECIDED_HUNK)
1231 hunk->use = SKIP_HUNK;
1232 }
ade246ef
JS
1233 if (ch == 'q') {
1234 quit = 1;
1235 break;
1236 }
7584dd3c
JS
1237 } else if (s->answer.buf[0] == 'K') {
1238 if (hunk_index)
1239 hunk_index--;
1240 else
1241 err(s, _("No previous hunk"));
1242 } else if (s->answer.buf[0] == 'J') {
80399aec 1243 if (hunk_index + 1 < file_diff->hunk_nr)
7584dd3c
JS
1244 hunk_index++;
1245 else
1246 err(s, _("No next hunk"));
1247 } else if (s->answer.buf[0] == 'k') {
1248 if (undecided_previous >= 0)
1249 hunk_index = undecided_previous;
1250 else
1251 err(s, _("No previous hunk"));
1252 } else if (s->answer.buf[0] == 'j') {
1253 if (undecided_next >= 0)
1254 hunk_index = undecided_next;
1255 else
1256 err(s, _("No next hunk"));
9254bdfb
JS
1257 } else if (s->answer.buf[0] == 'g') {
1258 char *pend;
1259 unsigned long response;
1260
1261 if (file_diff->hunk_nr < 2) {
1262 err(s, _("No other hunks to goto"));
1263 continue;
1264 }
1265 strbuf_remove(&s->answer, 0, 1);
1266 strbuf_trim(&s->answer);
1267 i = hunk_index - DISPLAY_HUNKS_LINES / 2;
1268 if (i < file_diff->mode_change)
1269 i = file_diff->mode_change;
1270 while (s->answer.len == 0) {
1271 i = display_hunks(s, file_diff, i);
1272 printf("%s", i < file_diff->hunk_nr ?
1273 _("go to which hunk (<ret> to see "
1274 "more)? ") : _("go to which hunk? "));
1275 fflush(stdout);
1276 if (strbuf_getline(&s->answer,
1277 stdin) == EOF)
1278 break;
1279 strbuf_trim_trailing_newline(&s->answer);
1280 }
1281
1282 strbuf_trim(&s->answer);
1283 response = strtoul(s->answer.buf, &pend, 10);
1284 if (*pend || pend == s->answer.buf)
1285 err(s, _("Invalid number: '%s'"),
1286 s->answer.buf);
1287 else if (0 < response && response <= file_diff->hunk_nr)
1288 hunk_index = response - 1;
1289 else
1290 err(s, Q_("Sorry, only %d hunk available.",
1291 "Sorry, only %d hunks available.",
1292 file_diff->hunk_nr),
1293 (int)file_diff->hunk_nr);
d6cf8733
JS
1294 } else if (s->answer.buf[0] == '/') {
1295 regex_t regex;
1296 int ret;
1297
1298 if (file_diff->hunk_nr < 2) {
1299 err(s, _("No other hunks to search"));
1300 continue;
1301 }
1302 strbuf_remove(&s->answer, 0, 1);
1303 strbuf_trim_trailing_newline(&s->answer);
1304 if (s->answer.len == 0) {
1305 printf("%s", _("search for regex? "));
1306 fflush(stdout);
1307 if (strbuf_getline(&s->answer,
1308 stdin) == EOF)
1309 break;
1310 strbuf_trim_trailing_newline(&s->answer);
1311 if (s->answer.len == 0)
1312 continue;
1313 }
1314 ret = regcomp(&regex, s->answer.buf,
1315 REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
1316 if (ret) {
1317 char errbuf[1024];
1318
1319 regerror(ret, &regex, errbuf, sizeof(errbuf));
1320 err(s, _("Malformed search regexp %s: %s"),
1321 s->answer.buf, errbuf);
1322 continue;
1323 }
1324 i = hunk_index;
1325 for (;;) {
1326 /* render the hunk into a scratch buffer */
1327 render_hunk(s, file_diff->hunk + i, 0, 0,
1328 &s->buf);
1329 if (regexec(&regex, s->buf.buf, 0, NULL, 0)
1330 != REG_NOMATCH)
1331 break;
1332 i++;
1333 if (i == file_diff->hunk_nr)
1334 i = 0;
1335 if (i != hunk_index)
1336 continue;
1337 err(s, _("No hunk matches the given pattern"));
1338 break;
1339 }
1340 hunk_index = i;
510aeca1
JS
1341 } else if (s->answer.buf[0] == 's') {
1342 size_t splittable_into = hunk->splittable_into;
1343 if (splittable_into < 2)
1344 err(s, _("Sorry, cannot split this hunk"));
1345 else if (!split_hunk(s, file_diff,
1346 hunk - file_diff->hunk))
1347 color_fprintf_ln(stdout, s->s.header_color,
1348 _("Split into %d hunks."),
1349 (int)splittable_into);
bcdd297b
JS
1350 } else if (s->answer.buf[0] == 'e') {
1351 if (hunk_index + 1 == file_diff->mode_change)
1352 err(s, _("Sorry, cannot edit this hunk"));
1353 else if (edit_hunk_loop(s, file_diff, hunk) >= 0) {
1354 hunk->use = USE_HUNK;
1355 goto soft_increment;
1356 }
54d9d9b2
JS
1357 } else {
1358 const char *p = _(help_patch_remainder), *eol = p;
1359
1360 color_fprintf(stdout, s->s.help_color, "%s",
d2a233cb 1361 _(s->mode->help_patch_text));
54d9d9b2
JS
1362
1363 /*
1364 * Show only those lines of the remainder that are
1365 * actually applicable with the current hunk.
1366 */
1367 for (; *p; p = eol + (*eol == '\n')) {
1368 eol = strchrnul(p, '\n');
1369
1370 /*
1371 * `s->buf` still contains the part of the
1372 * commands shown in the prompt that are not
1373 * always available.
1374 */
1375 if (*p != '?' && !strchr(s->buf.buf, *p))
1376 continue;
1377
1378 color_fprintf_ln(stdout, s->s.help_color,
1379 "%.*s", (int)(eol - p), p);
1380 }
1381 }
f6aa7ecc
JS
1382 }
1383
1384 /* Any hunk to be used? */
80399aec
JS
1385 for (i = 0; i < file_diff->hunk_nr; i++)
1386 if (file_diff->hunk[i].use == USE_HUNK)
f6aa7ecc
JS
1387 break;
1388
80399aec 1389 if (i < file_diff->hunk_nr) {
f6aa7ecc
JS
1390 /* At least one hunk selected: apply */
1391 strbuf_reset(&s->buf);
bcdd297b 1392 reassemble_patch(s, file_diff, 0, &s->buf);
f6aa7ecc 1393
25ea47af 1394 discard_index(s->s.r->index);
d2a233cb
JS
1395 setup_child_process(s, &cp, "apply", NULL);
1396 argv_array_pushv(&cp.args, s->mode->apply_args);
f6aa7ecc
JS
1397 if (pipe_command(&cp, s->buf.buf, s->buf.len,
1398 NULL, 0, NULL, 0))
d2a233cb 1399 error(_("'git apply' failed"));
25ea47af
JS
1400 if (!repo_read_index(s->s.r))
1401 repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
f6aa7ecc
JS
1402 1, NULL, NULL, NULL);
1403 }
1404
1405 putchar('\n');
ade246ef 1406 return quit;
f6aa7ecc
JS
1407}
1408
d2a233cb
JS
1409int run_add_p(struct repository *r, enum add_p_mode mode,
1410 const char *revision, const struct pathspec *ps)
f6aa7ecc 1411{
25ea47af
JS
1412 struct add_p_state s = {
1413 { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
1414 };
2e408319 1415 size_t i, binary_count = 0;
25ea47af
JS
1416
1417 init_add_i_state(&s.s, r);
f6aa7ecc 1418
36bae1dc
JS
1419 if (mode == ADD_P_STASH)
1420 s.mode = &patch_mode_stash;
1421 else if (mode == ADD_P_RESET) {
1422 if (!revision || !strcmp(revision, "HEAD"))
1423 s.mode = &patch_mode_reset_head;
1424 else
1425 s.mode = &patch_mode_reset_nothead;
1426 } else
1427 s.mode = &patch_mode_add;
d2a233cb
JS
1428 s.revision = revision;
1429
f6aa7ecc 1430 if (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||
36bae1dc
JS
1431 (!s.mode->index_only &&
1432 repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
1433 NULL, NULL, NULL) < 0) ||
f6aa7ecc
JS
1434 parse_diff(&s, ps) < 0) {
1435 strbuf_release(&s.plain);
e3bd11b4 1436 strbuf_release(&s.colored);
f6aa7ecc
JS
1437 return -1;
1438 }
1439
80399aec 1440 for (i = 0; i < s.file_diff_nr; i++)
2e408319
JS
1441 if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)
1442 binary_count++;
1443 else if (patch_update_file(&s, s.file_diff + i))
80399aec 1444 break;
f6aa7ecc 1445
2e408319
JS
1446 if (s.file_diff_nr == 0)
1447 fprintf(stderr, _("No changes.\n"));
1448 else if (binary_count == s.file_diff_nr)
1449 fprintf(stderr, _("Only binary files changed.\n"));
1450
f6aa7ecc
JS
1451 strbuf_release(&s.answer);
1452 strbuf_release(&s.buf);
1453 strbuf_release(&s.plain);
e3bd11b4 1454 strbuf_release(&s.colored);
f6aa7ecc
JS
1455 return 0;
1456}