The sixth batch
[git.git] / add-patch.c
blob95c67d8c80c4f33bfa6180f69fdf2065d0b5b85c
1 #define USE_THE_REPOSITORY_VARIABLE
2 #define DISABLE_SIGN_COMPARE_WARNINGS
4 #include "git-compat-util.h"
5 #include "add-interactive.h"
6 #include "advice.h"
7 #include "editor.h"
8 #include "environment.h"
9 #include "gettext.h"
10 #include "object-name.h"
11 #include "pager.h"
12 #include "read-cache-ll.h"
13 #include "repository.h"
14 #include "strbuf.h"
15 #include "sigchain.h"
16 #include "run-command.h"
17 #include "strvec.h"
18 #include "pathspec.h"
19 #include "color.h"
20 #include "compat/terminal.h"
21 #include "prompt.h"
23 enum prompt_mode_type {
24 PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_ADDITION, PROMPT_HUNK,
25 PROMPT_MODE_MAX, /* must be last */
28 struct patch_mode {
30 * The magic constant 4 is chosen such that all patch modes
31 * provide enough space for three command-line arguments followed by a
32 * trailing `NULL`.
34 const char *diff_cmd[4], *apply_args[4], *apply_check_args[4];
35 unsigned is_reverse:1, index_only:1, apply_for_checkout:1;
36 const char *prompt_mode[PROMPT_MODE_MAX];
37 const char *edit_hunk_hint, *help_patch_text;
40 static struct patch_mode patch_mode_add = {
41 .diff_cmd = { "diff-files", NULL },
42 .apply_args = { "--cached", NULL },
43 .apply_check_args = { "--cached", NULL },
44 .prompt_mode = {
45 N_("Stage mode change [y,n,q,a,d%s,?]? "),
46 N_("Stage deletion [y,n,q,a,d%s,?]? "),
47 N_("Stage addition [y,n,q,a,d%s,?]? "),
48 N_("Stage this hunk [y,n,q,a,d%s,?]? ")
50 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
51 "will immediately be marked for staging."),
52 .help_patch_text =
53 N_("y - stage this hunk\n"
54 "n - do not stage this hunk\n"
55 "q - quit; do not stage this hunk or any of the remaining "
56 "ones\n"
57 "a - stage this hunk and all later hunks in the file\n"
58 "d - do not stage this hunk or any of the later hunks in "
59 "the file\n")
62 static struct patch_mode patch_mode_stash = {
63 .diff_cmd = { "diff-index", "HEAD", NULL },
64 .apply_args = { "--cached", NULL },
65 .apply_check_args = { "--cached", NULL },
66 .prompt_mode = {
67 N_("Stash mode change [y,n,q,a,d%s,?]? "),
68 N_("Stash deletion [y,n,q,a,d%s,?]? "),
69 N_("Stash addition [y,n,q,a,d%s,?]? "),
70 N_("Stash this hunk [y,n,q,a,d%s,?]? "),
72 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
73 "will immediately be marked for stashing."),
74 .help_patch_text =
75 N_("y - stash this hunk\n"
76 "n - do not stash this hunk\n"
77 "q - quit; do not stash this hunk or any of the remaining "
78 "ones\n"
79 "a - stash this hunk and all later hunks in the file\n"
80 "d - do not stash this hunk or any of the later hunks in "
81 "the file\n"),
84 static struct patch_mode patch_mode_reset_head = {
85 .diff_cmd = { "diff-index", "--cached", NULL },
86 .apply_args = { "-R", "--cached", NULL },
87 .apply_check_args = { "-R", "--cached", NULL },
88 .is_reverse = 1,
89 .index_only = 1,
90 .prompt_mode = {
91 N_("Unstage mode change [y,n,q,a,d%s,?]? "),
92 N_("Unstage deletion [y,n,q,a,d%s,?]? "),
93 N_("Unstage addition [y,n,q,a,d%s,?]? "),
94 N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
96 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
97 "will immediately be marked for unstaging."),
98 .help_patch_text =
99 N_("y - unstage this hunk\n"
100 "n - do not unstage this hunk\n"
101 "q - quit; do not unstage this hunk or any of the remaining "
102 "ones\n"
103 "a - unstage this hunk and all later hunks in the file\n"
104 "d - do not unstage this hunk or any of the later hunks in "
105 "the file\n"),
108 static struct patch_mode patch_mode_reset_nothead = {
109 .diff_cmd = { "diff-index", "-R", "--cached", NULL },
110 .apply_args = { "--cached", NULL },
111 .apply_check_args = { "--cached", NULL },
112 .index_only = 1,
113 .prompt_mode = {
114 N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
115 N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
116 N_("Apply addition to index [y,n,q,a,d%s,?]? "),
117 N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
119 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
120 "will immediately be marked for applying."),
121 .help_patch_text =
122 N_("y - apply this hunk to index\n"
123 "n - do not apply this hunk to index\n"
124 "q - quit; do not apply this hunk or any of the remaining "
125 "ones\n"
126 "a - apply this hunk and all later hunks in the file\n"
127 "d - do not apply this hunk or any of the later hunks in "
128 "the file\n"),
131 static struct patch_mode patch_mode_checkout_index = {
132 .diff_cmd = { "diff-files", NULL },
133 .apply_args = { "-R", NULL },
134 .apply_check_args = { "-R", NULL },
135 .is_reverse = 1,
136 .prompt_mode = {
137 N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
138 N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
139 N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
140 N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
142 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
143 "will immediately be marked for discarding."),
144 .help_patch_text =
145 N_("y - discard this hunk from worktree\n"
146 "n - do not discard this hunk from worktree\n"
147 "q - quit; do not discard this hunk or any of the remaining "
148 "ones\n"
149 "a - discard this hunk and all later hunks in the file\n"
150 "d - do not discard this hunk or any of the later hunks in "
151 "the file\n"),
154 static struct patch_mode patch_mode_checkout_head = {
155 .diff_cmd = { "diff-index", NULL },
156 .apply_for_checkout = 1,
157 .apply_check_args = { "-R", NULL },
158 .is_reverse = 1,
159 .prompt_mode = {
160 N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
161 N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
162 N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
163 N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
165 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
166 "will immediately be marked for discarding."),
167 .help_patch_text =
168 N_("y - discard this hunk from index and worktree\n"
169 "n - do not discard this hunk from index and worktree\n"
170 "q - quit; do not discard this hunk or any of the remaining "
171 "ones\n"
172 "a - discard this hunk and all later hunks in the file\n"
173 "d - do not discard this hunk or any of the later hunks in "
174 "the file\n"),
177 static struct patch_mode patch_mode_checkout_nothead = {
178 .diff_cmd = { "diff-index", "-R", NULL },
179 .apply_for_checkout = 1,
180 .apply_check_args = { NULL },
181 .prompt_mode = {
182 N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
183 N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
184 N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
185 N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
187 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
188 "will immediately be marked for applying."),
189 .help_patch_text =
190 N_("y - apply this hunk to index and worktree\n"
191 "n - do not apply this hunk to index and worktree\n"
192 "q - quit; do not apply this hunk or any of the remaining "
193 "ones\n"
194 "a - apply this hunk and all later hunks in the file\n"
195 "d - do not apply this hunk or any of the later hunks in "
196 "the file\n"),
199 static struct patch_mode patch_mode_worktree_head = {
200 .diff_cmd = { "diff-index", NULL },
201 .apply_args = { "-R", NULL },
202 .apply_check_args = { "-R", NULL },
203 .is_reverse = 1,
204 .prompt_mode = {
205 N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
206 N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
207 N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
208 N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
210 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
211 "will immediately be marked for discarding."),
212 .help_patch_text =
213 N_("y - discard this hunk from worktree\n"
214 "n - do not discard this hunk from worktree\n"
215 "q - quit; do not discard this hunk or any of the remaining "
216 "ones\n"
217 "a - discard this hunk and all later hunks in the file\n"
218 "d - do not discard this hunk or any of the later hunks in "
219 "the file\n"),
222 static struct patch_mode patch_mode_worktree_nothead = {
223 .diff_cmd = { "diff-index", "-R", NULL },
224 .apply_args = { NULL },
225 .apply_check_args = { NULL },
226 .prompt_mode = {
227 N_("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
228 N_("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
229 N_("Apply addition to worktree [y,n,q,a,d%s,?]? "),
230 N_("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
232 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
233 "will immediately be marked for applying."),
234 .help_patch_text =
235 N_("y - apply this hunk to worktree\n"
236 "n - do not apply this hunk to worktree\n"
237 "q - quit; do not apply this hunk or any of the remaining "
238 "ones\n"
239 "a - apply this hunk and all later hunks in the file\n"
240 "d - do not apply this hunk or any of the later hunks in "
241 "the file\n"),
244 struct hunk_header {
245 unsigned long old_offset, old_count, new_offset, new_count;
247 * Start/end offsets to the extra text after the second `@@` in the
248 * hunk header, e.g. the function signature. This is expected to
249 * include the newline.
251 size_t extra_start, extra_end, colored_extra_start, colored_extra_end;
252 unsigned suppress_colored_line_range:1;
255 struct hunk {
256 size_t start, end, colored_start, colored_end, splittable_into;
257 ssize_t delta;
258 enum { UNDECIDED_HUNK = 0, SKIP_HUNK, USE_HUNK } use;
259 struct hunk_header header;
262 struct add_p_state {
263 struct add_i_state s;
264 struct strbuf answer, buf;
266 /* parsed diff */
267 struct strbuf plain, colored;
268 struct file_diff {
269 struct hunk head;
270 struct hunk *hunk;
271 size_t hunk_nr, hunk_alloc;
272 unsigned deleted:1, added:1, mode_change:1,binary:1;
273 } *file_diff;
274 size_t file_diff_nr;
276 /* patch mode */
277 struct patch_mode *mode;
278 const char *revision;
281 static void add_p_state_clear(struct add_p_state *s)
283 size_t i;
285 strbuf_release(&s->answer);
286 strbuf_release(&s->buf);
287 strbuf_release(&s->plain);
288 strbuf_release(&s->colored);
289 for (i = 0; i < s->file_diff_nr; i++)
290 free(s->file_diff[i].hunk);
291 free(s->file_diff);
292 clear_add_i_state(&s->s);
295 __attribute__((format (printf, 2, 3)))
296 static void err(struct add_p_state *s, const char *fmt, ...)
298 va_list args;
300 va_start(args, fmt);
301 fputs(s->s.error_color, stdout);
302 vprintf(fmt, args);
303 puts(s->s.reset_color);
304 va_end(args);
307 LAST_ARG_MUST_BE_NULL
308 static void setup_child_process(struct add_p_state *s,
309 struct child_process *cp, ...)
311 va_list ap;
312 const char *arg;
314 va_start(ap, cp);
315 while ((arg = va_arg(ap, const char *)))
316 strvec_push(&cp->args, arg);
317 va_end(ap);
319 cp->git_cmd = 1;
320 strvec_pushf(&cp->env,
321 INDEX_ENVIRONMENT "=%s", s->s.r->index_file);
324 static int parse_range(const char **p,
325 unsigned long *offset, unsigned long *count)
327 char *pend;
329 *offset = strtoul(*p, &pend, 10);
330 if (pend == *p)
331 return -1;
332 if (*pend != ',') {
333 *count = 1;
334 *p = pend;
335 return 0;
337 *count = strtoul(pend + 1, (char **)p, 10);
338 return *p == pend + 1 ? -1 : 0;
341 static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk)
343 struct hunk_header *header = &hunk->header;
344 const char *line = s->plain.buf + hunk->start, *p = line;
345 char *eol = memchr(p, '\n', s->plain.len - hunk->start);
347 if (!eol)
348 eol = s->plain.buf + s->plain.len;
350 if (!skip_prefix(p, "@@ -", &p) ||
351 parse_range(&p, &header->old_offset, &header->old_count) < 0 ||
352 !skip_prefix(p, " +", &p) ||
353 parse_range(&p, &header->new_offset, &header->new_count) < 0 ||
354 !skip_prefix(p, " @@", &p))
355 return error(_("could not parse hunk header '%.*s'"),
356 (int)(eol - line), line);
358 hunk->start = eol - s->plain.buf + (*eol == '\n');
359 header->extra_start = p - s->plain.buf;
360 header->extra_end = hunk->start;
362 if (!s->colored.len) {
363 header->colored_extra_start = header->colored_extra_end = 0;
364 return 0;
367 /* Now find the extra text in the colored diff */
368 line = s->colored.buf + hunk->colored_start;
369 eol = memchr(line, '\n', s->colored.len - hunk->colored_start);
370 if (!eol)
371 eol = s->colored.buf + s->colored.len;
372 p = memmem(line, eol - line, "@@ -", 4);
373 if (p && (p = memmem(p + 4, eol - p - 4, " @@", 3))) {
374 header->colored_extra_start = p + 3 - s->colored.buf;
375 } else {
376 /* could not parse colored hunk header, leave as-is */
377 header->colored_extra_start = hunk->colored_start;
378 header->suppress_colored_line_range = 1;
380 hunk->colored_start = eol - s->colored.buf + (*eol == '\n');
381 header->colored_extra_end = hunk->colored_start;
383 return 0;
386 static int is_octal(const char *p, size_t len)
388 if (!len)
389 return 0;
391 while (len--)
392 if (*p < '0' || *(p++) > '7')
393 return 0;
394 return 1;
397 static void complete_file(char marker, struct hunk *hunk)
399 if (marker == '-' || marker == '+')
401 * Last hunk ended in non-context line (i.e. it
402 * appended lines to the file, so there are no
403 * trailing context lines).
405 hunk->splittable_into++;
408 /* Empty context lines may omit the leading ' ' */
409 static int normalize_marker(const char *p)
411 return p[0] == '\n' || (p[0] == '\r' && p[1] == '\n') ? ' ' : p[0];
414 static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
416 struct strvec args = STRVEC_INIT;
417 const char *diff_algorithm = s->s.interactive_diff_algorithm;
418 struct strbuf *plain = &s->plain, *colored = NULL;
419 struct child_process cp = CHILD_PROCESS_INIT;
420 char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
421 size_t file_diff_alloc = 0, i, color_arg_index;
422 struct file_diff *file_diff = NULL;
423 struct hunk *hunk = NULL;
424 int res;
426 strvec_pushv(&args, s->mode->diff_cmd);
427 if (diff_algorithm)
428 strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm);
429 if (s->revision) {
430 struct object_id oid;
431 strvec_push(&args,
432 /* could be on an unborn branch */
433 !strcmp("HEAD", s->revision) &&
434 repo_get_oid(the_repository, "HEAD", &oid) ?
435 empty_tree_oid_hex(the_repository->hash_algo) : s->revision);
437 color_arg_index = args.nr;
438 /* Use `--no-color` explicitly, just in case `diff.color = always`. */
439 strvec_pushl(&args, "--no-color", "--ignore-submodules=dirty", "-p",
440 "--", NULL);
441 for (i = 0; i < ps->nr; i++)
442 strvec_push(&args, ps->items[i].original);
444 setup_child_process(s, &cp, NULL);
445 strvec_pushv(&cp.args, args.v);
446 res = capture_command(&cp, plain, 0);
447 if (res) {
448 strvec_clear(&args);
449 return error(_("could not parse diff"));
451 if (!plain->len) {
452 strvec_clear(&args);
453 return 0;
455 strbuf_complete_line(plain);
457 if (want_color_fd(1, -1)) {
458 struct child_process colored_cp = CHILD_PROCESS_INIT;
459 const char *diff_filter = s->s.interactive_diff_filter;
461 setup_child_process(s, &colored_cp, NULL);
462 xsnprintf((char *)args.v[color_arg_index], 8, "--color");
463 strvec_pushv(&colored_cp.args, args.v);
464 colored = &s->colored;
465 res = capture_command(&colored_cp, colored, 0);
466 strvec_clear(&args);
467 if (res)
468 return error(_("could not parse colored diff"));
470 if (diff_filter) {
471 struct child_process filter_cp = CHILD_PROCESS_INIT;
473 setup_child_process(s, &filter_cp,
474 diff_filter, NULL);
475 filter_cp.git_cmd = 0;
476 filter_cp.use_shell = 1;
477 strbuf_reset(&s->buf);
478 if (pipe_command(&filter_cp,
479 colored->buf, colored->len,
480 &s->buf, colored->len,
481 NULL, 0) < 0)
482 return error(_("failed to run '%s'"),
483 diff_filter);
484 strbuf_swap(colored, &s->buf);
487 strbuf_complete_line(colored);
488 colored_p = colored->buf;
489 colored_pend = colored_p + colored->len;
491 strvec_clear(&args);
493 /* parse files and hunks */
494 p = plain->buf;
495 pend = p + plain->len;
496 while (p != pend) {
497 char *eol = memchr(p, '\n', pend - p);
498 const char *deleted = NULL, *mode_change = NULL;
499 char ch = normalize_marker(p);
501 if (!eol)
502 eol = pend;
504 if (starts_with(p, "diff ") ||
505 starts_with(p, "* Unmerged path ")) {
506 complete_file(marker, hunk);
507 ALLOC_GROW_BY(s->file_diff, s->file_diff_nr, 1,
508 file_diff_alloc);
509 file_diff = s->file_diff + s->file_diff_nr - 1;
510 hunk = &file_diff->head;
511 hunk->start = p - plain->buf;
512 if (colored_p)
513 hunk->colored_start = colored_p - colored->buf;
514 marker = '\0';
515 } else if (p == plain->buf)
516 BUG("diff starts with unexpected line:\n"
517 "%.*s\n", (int)(eol - p), p);
518 else if (file_diff->deleted)
519 ; /* keep the rest of the file in a single "hunk" */
520 else if (starts_with(p, "@@ ") ||
521 (hunk == &file_diff->head &&
522 (skip_prefix(p, "deleted file", &deleted)))) {
523 if (marker == '-' || marker == '+')
525 * Should not happen; previous hunk did not end
526 * in a context line? Handle it anyway.
528 hunk->splittable_into++;
530 ALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,
531 file_diff->hunk_alloc);
532 hunk = file_diff->hunk + file_diff->hunk_nr - 1;
534 hunk->start = p - plain->buf;
535 if (colored)
536 hunk->colored_start = colored_p - colored->buf;
538 if (deleted)
539 file_diff->deleted = 1;
540 else if (parse_hunk_header(s, hunk) < 0)
541 return -1;
544 * Start counting into how many hunks this one can be
545 * split
547 marker = ch;
548 } else if (hunk == &file_diff->head &&
549 starts_with(p, "new file")) {
550 file_diff->added = 1;
551 } else if (hunk == &file_diff->head &&
552 skip_prefix(p, "old mode ", &mode_change) &&
553 is_octal(mode_change, eol - mode_change)) {
554 if (file_diff->mode_change)
555 BUG("double mode change?\n\n%.*s",
556 (int)(eol - plain->buf), plain->buf);
557 if (file_diff->hunk_nr)
558 BUG("mode change in the middle?\n\n%.*s",
559 (int)(eol - plain->buf), plain->buf);
562 * Do *not* change `hunk`: the mode change pseudo-hunk
563 * is _part of_ the header "hunk".
565 file_diff->mode_change = 1;
566 ALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,
567 file_diff->hunk_alloc);
568 file_diff->hunk->start = p - plain->buf;
569 if (colored_p)
570 file_diff->hunk->colored_start =
571 colored_p - colored->buf;
572 } else if (hunk == &file_diff->head &&
573 skip_prefix(p, "new mode ", &mode_change) &&
574 is_octal(mode_change, eol - mode_change)) {
577 * Extend the "mode change" pseudo-hunk to include also
578 * the "new mode" line.
580 if (!file_diff->mode_change)
581 BUG("'new mode' without 'old mode'?\n\n%.*s",
582 (int)(eol - plain->buf), plain->buf);
583 if (file_diff->hunk_nr != 1)
584 BUG("mode change in the middle?\n\n%.*s",
585 (int)(eol - plain->buf), plain->buf);
586 if (p - plain->buf != file_diff->hunk->end)
587 BUG("'new mode' does not immediately follow "
588 "'old mode'?\n\n%.*s",
589 (int)(eol - plain->buf), plain->buf);
590 } else if (hunk == &file_diff->head &&
591 starts_with(p, "Binary files "))
592 file_diff->binary = 1;
594 if (!!file_diff->deleted + !!file_diff->added +
595 !!file_diff->mode_change > 1)
596 BUG("diff can only contain delete *or* add *or* a "
597 "mode change?!?\n%.*s",
598 (int)(eol - (plain->buf + file_diff->head.start)),
599 plain->buf + file_diff->head.start);
601 if ((marker == '-' || marker == '+') && ch == ' ')
602 hunk->splittable_into++;
603 if (marker && ch != '\\')
604 marker = ch;
606 p = eol == pend ? pend : eol + 1;
607 hunk->end = p - plain->buf;
609 if (colored) {
610 char *colored_eol = memchr(colored_p, '\n',
611 colored_pend - colored_p);
612 if (colored_eol)
613 colored_p = colored_eol + 1;
614 else if (p != pend)
615 /* non-colored has more lines? */
616 goto mismatched_output;
617 else if (colored_p == colored_pend)
618 /* last line has no matching colored one? */
619 goto mismatched_output;
620 else
621 colored_p = colored_pend;
623 hunk->colored_end = colored_p - colored->buf;
626 if (mode_change) {
627 if (file_diff->hunk_nr != 1)
628 BUG("mode change in hunk #%d???",
629 (int)file_diff->hunk_nr);
630 /* Adjust the end of the "mode change" pseudo-hunk */
631 file_diff->hunk->end = hunk->end;
632 if (colored)
633 file_diff->hunk->colored_end = hunk->colored_end;
636 complete_file(marker, hunk);
638 /* non-colored shorter than colored? */
639 if (colored_p != colored_pend) {
640 mismatched_output:
641 error(_("mismatched output from interactive.diffFilter"));
642 advise(_("Your filter must maintain a one-to-one correspondence\n"
643 "between its input and output lines."));
644 return -1;
647 return 0;
650 static size_t find_next_line(struct strbuf *sb, size_t offset)
652 char *eol;
654 if (offset >= sb->len)
655 BUG("looking for next line beyond buffer (%d >= %d)\n%s",
656 (int)offset, (int)sb->len, sb->buf);
658 eol = memchr(sb->buf + offset, '\n', sb->len - offset);
659 if (!eol)
660 return sb->len;
661 return eol - sb->buf + 1;
664 static void render_hunk(struct add_p_state *s, struct hunk *hunk,
665 ssize_t delta, int colored, struct strbuf *out)
667 struct hunk_header *header = &hunk->header;
669 if (hunk->header.old_offset != 0 || hunk->header.new_offset != 0) {
671 * Generate the hunk header dynamically, except for special
672 * hunks (such as the diff header).
674 const char *p;
675 size_t len;
676 unsigned long old_offset = header->old_offset;
677 unsigned long new_offset = header->new_offset;
679 if (!colored) {
680 p = s->plain.buf + header->extra_start;
681 len = header->extra_end - header->extra_start;
682 } else if (header->suppress_colored_line_range) {
683 strbuf_add(out,
684 s->colored.buf + header->colored_extra_start,
685 header->colored_extra_end -
686 header->colored_extra_start);
688 strbuf_add(out, s->colored.buf + hunk->colored_start,
689 hunk->colored_end - hunk->colored_start);
690 return;
691 } else {
692 strbuf_addstr(out, s->s.fraginfo_color);
693 p = s->colored.buf + header->colored_extra_start;
694 len = header->colored_extra_end
695 - header->colored_extra_start;
698 if (s->mode->is_reverse)
699 old_offset -= delta;
700 else
701 new_offset += delta;
703 strbuf_addf(out, "@@ -%lu", old_offset);
704 if (header->old_count != 1)
705 strbuf_addf(out, ",%lu", header->old_count);
706 strbuf_addf(out, " +%lu", new_offset);
707 if (header->new_count != 1)
708 strbuf_addf(out, ",%lu", header->new_count);
709 strbuf_addstr(out, " @@");
711 if (len)
712 strbuf_add(out, p, len);
713 else if (colored)
714 strbuf_addf(out, "%s\n", s->s.reset_color);
715 else
716 strbuf_addch(out, '\n');
719 if (colored)
720 strbuf_add(out, s->colored.buf + hunk->colored_start,
721 hunk->colored_end - hunk->colored_start);
722 else
723 strbuf_add(out, s->plain.buf + hunk->start,
724 hunk->end - hunk->start);
727 static void render_diff_header(struct add_p_state *s,
728 struct file_diff *file_diff, int colored,
729 struct strbuf *out)
732 * If there was a mode change, the first hunk is a pseudo hunk that
733 * corresponds to the mode line in the header. If the user did not want
734 * to stage that "hunk", we actually have to cut it out from the header.
736 int skip_mode_change =
737 file_diff->mode_change && file_diff->hunk->use != USE_HUNK;
738 struct hunk *head = &file_diff->head, *first = file_diff->hunk;
740 if (!skip_mode_change) {
741 render_hunk(s, head, 0, colored, out);
742 return;
745 if (colored) {
746 const char *p = s->colored.buf;
748 strbuf_add(out, p + head->colored_start,
749 first->colored_start - head->colored_start);
750 strbuf_add(out, p + first->colored_end,
751 head->colored_end - first->colored_end);
752 } else {
753 const char *p = s->plain.buf;
755 strbuf_add(out, p + head->start, first->start - head->start);
756 strbuf_add(out, p + first->end, head->end - first->end);
760 /* Coalesce hunks again that were split */
761 static int merge_hunks(struct add_p_state *s, struct file_diff *file_diff,
762 size_t *hunk_index, int use_all, struct hunk *merged)
764 size_t i = *hunk_index, delta;
765 struct hunk *hunk = file_diff->hunk + i;
766 /* `header` corresponds to the merged hunk */
767 struct hunk_header *header = &merged->header, *next;
769 if (!use_all && hunk->use != USE_HUNK)
770 return 0;
772 *merged = *hunk;
773 /* We simply skip the colored part (if any) when merging hunks */
774 merged->colored_start = merged->colored_end = 0;
776 for (; i + 1 < file_diff->hunk_nr; i++) {
777 hunk++;
778 next = &hunk->header;
781 * Stop merging hunks when:
783 * - the hunk is not selected for use, or
784 * - the hunk does not overlap with the already-merged hunk(s)
786 if ((!use_all && hunk->use != USE_HUNK) ||
787 header->new_offset >= next->new_offset + merged->delta ||
788 header->new_offset + header->new_count
789 < next->new_offset + merged->delta)
790 break;
793 * If the hunks were not edited, and overlap, we can simply
794 * extend the line range.
796 if (merged->start < hunk->start && merged->end > hunk->start) {
797 merged->end = hunk->end;
798 merged->colored_end = hunk->colored_end;
799 delta = 0;
800 } else {
801 const char *plain = s->plain.buf;
802 size_t overlapping_line_count = header->new_offset
803 + header->new_count - merged->delta
804 - next->new_offset;
805 size_t overlap_end = hunk->start;
806 size_t overlap_start = overlap_end;
807 size_t overlap_next, len, j;
810 * One of the hunks was edited: the modified hunk was
811 * appended to the strbuf `s->plain`.
813 * Let's ensure that at least the last context line of
814 * the first hunk overlaps with the corresponding line
815 * of the second hunk, and then merge.
817 for (j = 0; j < overlapping_line_count; j++) {
818 overlap_next = find_next_line(&s->plain,
819 overlap_end);
821 if (overlap_next > hunk->end)
822 BUG("failed to find %d context lines "
823 "in:\n%.*s",
824 (int)overlapping_line_count,
825 (int)(hunk->end - hunk->start),
826 plain + hunk->start);
828 if (normalize_marker(&plain[overlap_end]) != ' ')
829 return error(_("expected context line "
830 "#%d in\n%.*s"),
831 (int)(j + 1),
832 (int)(hunk->end
833 - hunk->start),
834 plain + hunk->start);
836 overlap_start = overlap_end;
837 overlap_end = overlap_next;
839 len = overlap_end - overlap_start;
841 if (len > merged->end - merged->start ||
842 memcmp(plain + merged->end - len,
843 plain + overlap_start, len))
844 return error(_("hunks do not overlap:\n%.*s\n"
845 "\tdoes not end with:\n%.*s"),
846 (int)(merged->end - merged->start),
847 plain + merged->start,
848 (int)len, plain + overlap_start);
851 * Since the start-end ranges are not adjacent, we
852 * cannot simply take the union of the ranges. To
853 * address that, we temporarily append the union of the
854 * lines to the `plain` strbuf.
856 if (merged->end != s->plain.len) {
857 size_t start = s->plain.len;
859 strbuf_add(&s->plain, plain + merged->start,
860 merged->end - merged->start);
861 plain = s->plain.buf;
862 merged->start = start;
863 merged->end = s->plain.len;
866 strbuf_add(&s->plain,
867 plain + overlap_end,
868 hunk->end - overlap_end);
869 merged->end = s->plain.len;
870 merged->splittable_into += hunk->splittable_into;
871 delta = merged->delta;
872 merged->delta += hunk->delta;
875 header->old_count = next->old_offset + next->old_count
876 - header->old_offset;
877 header->new_count = next->new_offset + delta
878 + next->new_count - header->new_offset;
881 if (i == *hunk_index)
882 return 0;
884 *hunk_index = i;
885 return 1;
888 static void reassemble_patch(struct add_p_state *s,
889 struct file_diff *file_diff, int use_all,
890 struct strbuf *out)
892 struct hunk *hunk;
893 size_t save_len = s->plain.len, i;
894 ssize_t delta = 0;
896 render_diff_header(s, file_diff, 0, out);
898 for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) {
899 struct hunk merged = { 0 };
901 hunk = file_diff->hunk + i;
902 if (!use_all && hunk->use != USE_HUNK)
903 delta += hunk->header.old_count
904 - hunk->header.new_count;
905 else {
906 /* merge overlapping hunks into a temporary hunk */
907 if (merge_hunks(s, file_diff, &i, use_all, &merged))
908 hunk = &merged;
910 render_hunk(s, hunk, delta, 0, out);
913 * In case `merge_hunks()` used `plain` as a scratch
914 * pad (this happens when an edited hunk had to be
915 * coalesced with another hunk).
917 strbuf_setlen(&s->plain, save_len);
919 delta += hunk->delta;
924 static int split_hunk(struct add_p_state *s, struct file_diff *file_diff,
925 size_t hunk_index)
927 int colored = !!s->colored.len, first = 1;
928 struct hunk *hunk = file_diff->hunk + hunk_index;
929 size_t splittable_into;
930 size_t end, colored_end, current, colored_current = 0, context_line_count;
931 struct hunk_header remaining, *header;
932 char marker, ch;
934 if (hunk_index >= file_diff->hunk_nr)
935 BUG("invalid hunk index: %d (must be >= 0 and < %d)",
936 (int)hunk_index, (int)file_diff->hunk_nr);
938 if (hunk->splittable_into < 2)
939 return 0;
940 splittable_into = hunk->splittable_into;
942 end = hunk->end;
943 colored_end = hunk->colored_end;
945 remaining = hunk->header;
947 file_diff->hunk_nr += splittable_into - 1;
948 ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr, file_diff->hunk_alloc);
949 if (hunk_index + splittable_into < file_diff->hunk_nr)
950 memmove(file_diff->hunk + hunk_index + splittable_into,
951 file_diff->hunk + hunk_index + 1,
952 (file_diff->hunk_nr - hunk_index - splittable_into)
953 * sizeof(*hunk));
954 hunk = file_diff->hunk + hunk_index;
955 hunk->splittable_into = 1;
956 memset(hunk + 1, 0, (splittable_into - 1) * sizeof(*hunk));
958 header = &hunk->header;
959 header->old_count = header->new_count = 0;
961 current = hunk->start;
962 if (colored)
963 colored_current = hunk->colored_start;
964 marker = '\0';
965 context_line_count = 0;
967 while (splittable_into > 1) {
968 ch = normalize_marker(&s->plain.buf[current]);
970 if (!ch)
971 BUG("buffer overrun while splitting hunks");
974 * Is this the first context line after a chain of +/- lines?
975 * Then record the start of the next split hunk.
977 if ((marker == '-' || marker == '+') && ch == ' ') {
978 first = 0;
979 hunk[1].start = current;
980 if (colored)
981 hunk[1].colored_start = colored_current;
982 context_line_count = 0;
986 * Was the previous line a +/- one? Alternatively, is this the
987 * first line (and not a +/- one)?
989 * Then just increment the appropriate counter and continue
990 * with the next line.
992 if (marker != ' ' || (ch != '-' && ch != '+')) {
993 next_hunk_line:
994 /* Comment lines are attached to the previous line */
995 if (ch == '\\')
996 ch = marker ? marker : ' ';
998 /* current hunk not done yet */
999 if (ch == ' ')
1000 context_line_count++;
1001 else if (ch == '-')
1002 header->old_count++;
1003 else if (ch == '+')
1004 header->new_count++;
1005 else
1006 BUG("unhandled diff marker: '%c'", ch);
1007 marker = ch;
1008 current = find_next_line(&s->plain, current);
1009 if (colored)
1010 colored_current =
1011 find_next_line(&s->colored,
1012 colored_current);
1013 continue;
1017 * We got us the start of a new hunk!
1019 * This is a context line, so it is shared with the previous
1020 * hunk, if any.
1023 if (first) {
1024 if (header->old_count || header->new_count)
1025 BUG("counts are off: %d/%d",
1026 (int)header->old_count,
1027 (int)header->new_count);
1029 header->old_count = context_line_count;
1030 header->new_count = context_line_count;
1031 context_line_count = 0;
1032 first = 0;
1033 goto next_hunk_line;
1036 remaining.old_offset += header->old_count;
1037 remaining.old_count -= header->old_count;
1038 remaining.new_offset += header->new_count;
1039 remaining.new_count -= header->new_count;
1041 /* initialize next hunk header's offsets */
1042 hunk[1].header.old_offset =
1043 header->old_offset + header->old_count;
1044 hunk[1].header.new_offset =
1045 header->new_offset + header->new_count;
1047 /* add one split hunk */
1048 header->old_count += context_line_count;
1049 header->new_count += context_line_count;
1051 hunk->end = current;
1052 if (colored)
1053 hunk->colored_end = colored_current;
1055 hunk++;
1056 hunk->splittable_into = 1;
1057 hunk->use = hunk[-1].use;
1058 header = &hunk->header;
1060 header->old_count = header->new_count = context_line_count;
1061 context_line_count = 0;
1063 splittable_into--;
1064 marker = ch;
1067 /* last hunk simply gets the rest */
1068 if (header->old_offset != remaining.old_offset)
1069 BUG("miscounted old_offset: %lu != %lu",
1070 header->old_offset, remaining.old_offset);
1071 if (header->new_offset != remaining.new_offset)
1072 BUG("miscounted new_offset: %lu != %lu",
1073 header->new_offset, remaining.new_offset);
1074 header->old_count = remaining.old_count;
1075 header->new_count = remaining.new_count;
1076 hunk->end = end;
1077 if (colored)
1078 hunk->colored_end = colored_end;
1080 return 0;
1083 static void recolor_hunk(struct add_p_state *s, struct hunk *hunk)
1085 const char *plain = s->plain.buf;
1086 size_t current, eol, next;
1088 if (!s->colored.len)
1089 return;
1091 hunk->colored_start = s->colored.len;
1092 for (current = hunk->start; current < hunk->end; ) {
1093 for (eol = current; eol < hunk->end; eol++)
1094 if (plain[eol] == '\n')
1095 break;
1096 next = eol + (eol < hunk->end);
1097 if (eol > current && plain[eol - 1] == '\r')
1098 eol--;
1100 strbuf_addstr(&s->colored,
1101 plain[current] == '-' ?
1102 s->s.file_old_color :
1103 plain[current] == '+' ?
1104 s->s.file_new_color :
1105 s->s.context_color);
1106 strbuf_add(&s->colored, plain + current, eol - current);
1107 strbuf_addstr(&s->colored, s->s.reset_color);
1108 if (next > eol)
1109 strbuf_add(&s->colored, plain + eol, next - eol);
1110 current = next;
1112 hunk->colored_end = s->colored.len;
1115 static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
1117 size_t i;
1119 strbuf_reset(&s->buf);
1120 strbuf_commented_addf(&s->buf, comment_line_str,
1121 _("Manual hunk edit mode -- see bottom for "
1122 "a quick guide.\n"));
1123 render_hunk(s, hunk, 0, 0, &s->buf);
1124 strbuf_commented_addf(&s->buf, comment_line_str,
1125 _("---\n"
1126 "To remove '%c' lines, make them ' ' lines "
1127 "(context).\n"
1128 "To remove '%c' lines, delete them.\n"
1129 "Lines starting with %s will be removed.\n"),
1130 s->mode->is_reverse ? '+' : '-',
1131 s->mode->is_reverse ? '-' : '+',
1132 comment_line_str);
1133 strbuf_commented_addf(&s->buf, comment_line_str, "%s",
1134 _(s->mode->edit_hunk_hint));
1136 * TRANSLATORS: 'it' refers to the patch mentioned in the previous
1137 * messages.
1139 strbuf_commented_addf(&s->buf, comment_line_str,
1140 _("If it does not apply cleanly, you will be "
1141 "given an opportunity to\n"
1142 "edit again. If all lines of the hunk are "
1143 "removed, then the edit is\n"
1144 "aborted and the hunk is left unchanged.\n"));
1146 if (strbuf_edit_interactively(the_repository, &s->buf,
1147 "addp-hunk-edit.diff", NULL) < 0)
1148 return -1;
1150 /* strip out commented lines */
1151 hunk->start = s->plain.len;
1152 for (i = 0; i < s->buf.len; ) {
1153 size_t next = find_next_line(&s->buf, i);
1155 if (!starts_with(s->buf.buf + i, comment_line_str))
1156 strbuf_add(&s->plain, s->buf.buf + i, next - i);
1157 i = next;
1160 hunk->end = s->plain.len;
1161 if (hunk->end == hunk->start)
1162 /* The user aborted editing by deleting everything */
1163 return 0;
1165 recolor_hunk(s, hunk);
1168 * If the hunk header is intact, parse it, otherwise simply use the
1169 * hunk header prior to editing (which will adjust `hunk->start` to
1170 * skip the hunk header).
1172 if (s->plain.buf[hunk->start] == '@' &&
1173 parse_hunk_header(s, hunk) < 0)
1174 return error(_("could not parse hunk header"));
1176 return 1;
1179 static ssize_t recount_edited_hunk(struct add_p_state *s, struct hunk *hunk,
1180 size_t orig_old_count, size_t orig_new_count)
1182 struct hunk_header *header = &hunk->header;
1183 size_t i;
1185 header->old_count = header->new_count = 0;
1186 for (i = hunk->start; i < hunk->end; ) {
1187 switch(normalize_marker(&s->plain.buf[i])) {
1188 case '-':
1189 header->old_count++;
1190 break;
1191 case '+':
1192 header->new_count++;
1193 break;
1194 case ' ':
1195 header->old_count++;
1196 header->new_count++;
1197 break;
1200 i = find_next_line(&s->plain, i);
1203 return orig_old_count - orig_new_count
1204 - header->old_count + header->new_count;
1207 static int run_apply_check(struct add_p_state *s,
1208 struct file_diff *file_diff)
1210 struct child_process cp = CHILD_PROCESS_INIT;
1212 strbuf_reset(&s->buf);
1213 reassemble_patch(s, file_diff, 1, &s->buf);
1215 setup_child_process(s, &cp,
1216 "apply", "--check", NULL);
1217 strvec_pushv(&cp.args, s->mode->apply_check_args);
1218 if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
1219 return error(_("'git apply --cached' failed"));
1221 return 0;
1224 static int read_single_character(struct add_p_state *s)
1226 if (s->s.use_single_key) {
1227 int res = read_key_without_echo(&s->answer);
1228 printf("%s\n", res == EOF ? "" : s->answer.buf);
1229 return res;
1232 if (git_read_line_interactively(&s->answer) == EOF)
1233 return EOF;
1234 return 0;
1237 static int prompt_yesno(struct add_p_state *s, const char *prompt)
1239 for (;;) {
1240 color_fprintf(stdout, s->s.prompt_color, "%s", _(prompt));
1241 fflush(stdout);
1242 if (read_single_character(s) == EOF)
1243 return -1;
1244 /* do not limit to 1-byte input to allow 'no' etc. */
1245 switch (tolower(s->answer.buf[0])) {
1246 case 'n': return 0;
1247 case 'y': return 1;
1252 static int edit_hunk_loop(struct add_p_state *s,
1253 struct file_diff *file_diff, struct hunk *hunk)
1255 size_t plain_len = s->plain.len, colored_len = s->colored.len;
1256 struct hunk backup;
1258 backup = *hunk;
1260 for (;;) {
1261 int res = edit_hunk_manually(s, hunk);
1262 if (res == 0) {
1263 /* abandoned */
1264 *hunk = backup;
1265 return -1;
1268 if (res > 0) {
1269 hunk->delta +=
1270 recount_edited_hunk(s, hunk,
1271 backup.header.old_count,
1272 backup.header.new_count);
1273 if (!run_apply_check(s, file_diff))
1274 return 0;
1277 /* Drop edits (they were appended to s->plain) */
1278 strbuf_setlen(&s->plain, plain_len);
1279 strbuf_setlen(&s->colored, colored_len);
1280 *hunk = backup;
1283 * TRANSLATORS: do not translate [y/n]
1284 * The program will only accept that input at this point.
1285 * Consider translating (saying "no" discards!) as
1286 * (saying "n" for "no" discards!) if the translation
1287 * of the word "no" does not start with n.
1289 res = prompt_yesno(s, _("Your edited hunk does not apply. "
1290 "Edit again (saying \"no\" discards!) "
1291 "[y/n]? "));
1292 if (res < 1)
1293 return -1;
1297 static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
1298 int is_reverse)
1300 const char *reverse = is_reverse ? "-R" : NULL;
1301 struct child_process check_index = CHILD_PROCESS_INIT;
1302 struct child_process check_worktree = CHILD_PROCESS_INIT;
1303 struct child_process apply_index = CHILD_PROCESS_INIT;
1304 struct child_process apply_worktree = CHILD_PROCESS_INIT;
1305 int applies_index, applies_worktree;
1307 setup_child_process(s, &check_index,
1308 "apply", "--cached", "--check", reverse, NULL);
1309 applies_index = !pipe_command(&check_index, diff->buf, diff->len,
1310 NULL, 0, NULL, 0);
1312 setup_child_process(s, &check_worktree,
1313 "apply", "--check", reverse, NULL);
1314 applies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,
1315 NULL, 0, NULL, 0);
1317 if (applies_worktree && applies_index) {
1318 setup_child_process(s, &apply_index,
1319 "apply", "--cached", reverse, NULL);
1320 pipe_command(&apply_index, diff->buf, diff->len,
1321 NULL, 0, NULL, 0);
1323 setup_child_process(s, &apply_worktree,
1324 "apply", reverse, NULL);
1325 pipe_command(&apply_worktree, diff->buf, diff->len,
1326 NULL, 0, NULL, 0);
1328 return 1;
1331 if (!applies_index) {
1332 err(s, _("The selected hunks do not apply to the index!"));
1333 if (prompt_yesno(s, _("Apply them to the worktree "
1334 "anyway? ")) > 0) {
1335 setup_child_process(s, &apply_worktree,
1336 "apply", reverse, NULL);
1337 return pipe_command(&apply_worktree, diff->buf,
1338 diff->len, NULL, 0, NULL, 0);
1340 err(s, _("Nothing was applied.\n"));
1341 } else
1342 /* As a last resort, show the diff to the user */
1343 fwrite(diff->buf, diff->len, 1, stdout);
1345 return 0;
1348 #define SUMMARY_HEADER_WIDTH 20
1349 #define SUMMARY_LINE_WIDTH 80
1350 static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
1351 struct strbuf *out)
1353 struct hunk_header *header = &hunk->header;
1354 struct strbuf *plain = &s->plain;
1355 size_t len = out->len, i;
1357 strbuf_addf(out, " -%lu,%lu +%lu,%lu ",
1358 header->old_offset, header->old_count,
1359 header->new_offset, header->new_count);
1360 if (out->len - len < SUMMARY_HEADER_WIDTH)
1361 strbuf_addchars(out, ' ',
1362 SUMMARY_HEADER_WIDTH + len - out->len);
1363 for (i = hunk->start; i < hunk->end; i = find_next_line(plain, i))
1364 if (plain->buf[i] != ' ')
1365 break;
1366 if (i < hunk->end)
1367 strbuf_add(out, plain->buf + i, find_next_line(plain, i) - i);
1368 if (out->len - len > SUMMARY_LINE_WIDTH)
1369 strbuf_setlen(out, len + SUMMARY_LINE_WIDTH);
1370 strbuf_complete_line(out);
1373 #define DISPLAY_HUNKS_LINES 20
1374 static size_t display_hunks(struct add_p_state *s,
1375 struct file_diff *file_diff, size_t start_index)
1377 size_t end_index = start_index + DISPLAY_HUNKS_LINES;
1379 if (end_index > file_diff->hunk_nr)
1380 end_index = file_diff->hunk_nr;
1382 while (start_index < end_index) {
1383 struct hunk *hunk = file_diff->hunk + start_index++;
1385 strbuf_reset(&s->buf);
1386 strbuf_addf(&s->buf, "%c%2d: ", hunk->use == USE_HUNK ? '+'
1387 : hunk->use == SKIP_HUNK ? '-' : ' ',
1388 (int)start_index);
1389 summarize_hunk(s, hunk, &s->buf);
1390 fputs(s->buf.buf, stdout);
1393 return end_index;
1396 static const char help_patch_remainder[] =
1397 N_("j - leave this hunk undecided, see next undecided hunk\n"
1398 "J - leave this hunk undecided, see next hunk\n"
1399 "k - leave this hunk undecided, see previous undecided hunk\n"
1400 "K - leave this hunk undecided, see previous hunk\n"
1401 "g - select a hunk to go to\n"
1402 "/ - search for a hunk matching the given regex\n"
1403 "s - split the current hunk into smaller hunks\n"
1404 "e - manually edit the current hunk\n"
1405 "p - print the current hunk, 'P' to use the pager\n"
1406 "? - print help\n");
1408 static int patch_update_file(struct add_p_state *s,
1409 struct file_diff *file_diff)
1411 size_t hunk_index = 0;
1412 ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
1413 struct hunk *hunk;
1414 char ch;
1415 struct child_process cp = CHILD_PROCESS_INIT;
1416 int colored = !!s->colored.len, quit = 0, use_pager = 0;
1417 enum prompt_mode_type prompt_mode_type;
1418 enum {
1419 ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
1420 ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
1421 ALLOW_GOTO_NEXT_HUNK = 1 << 2,
1422 ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
1423 ALLOW_SEARCH_AND_GOTO = 1 << 4,
1424 ALLOW_SPLIT = 1 << 5,
1425 ALLOW_EDIT = 1 << 6
1426 } permitted = 0;
1428 /* Empty added files have no hunks */
1429 if (!file_diff->hunk_nr && !file_diff->added)
1430 return 0;
1432 strbuf_reset(&s->buf);
1433 render_diff_header(s, file_diff, colored, &s->buf);
1434 fputs(s->buf.buf, stdout);
1435 for (;;) {
1436 if (hunk_index >= file_diff->hunk_nr)
1437 hunk_index = 0;
1438 hunk = file_diff->hunk_nr
1439 ? file_diff->hunk + hunk_index
1440 : &file_diff->head;
1441 undecided_previous = -1;
1442 undecided_next = -1;
1444 if (file_diff->hunk_nr) {
1445 for (i = hunk_index - 1; i >= 0; i--)
1446 if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
1447 undecided_previous = i;
1448 break;
1451 for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
1452 if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
1453 undecided_next = i;
1454 break;
1458 /* Everything decided? */
1459 if (undecided_previous < 0 && undecided_next < 0 &&
1460 hunk->use != UNDECIDED_HUNK)
1461 break;
1463 strbuf_reset(&s->buf);
1464 if (file_diff->hunk_nr) {
1465 if (rendered_hunk_index != hunk_index) {
1466 if (use_pager) {
1467 setup_pager(the_repository);
1468 sigchain_push(SIGPIPE, SIG_IGN);
1470 render_hunk(s, hunk, 0, colored, &s->buf);
1471 fputs(s->buf.buf, stdout);
1472 rendered_hunk_index = hunk_index;
1473 if (use_pager) {
1474 sigchain_pop(SIGPIPE);
1475 wait_for_pager();
1476 use_pager = 0;
1480 strbuf_reset(&s->buf);
1481 if (undecided_previous >= 0) {
1482 permitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;
1483 strbuf_addstr(&s->buf, ",k");
1485 if (hunk_index) {
1486 permitted |= ALLOW_GOTO_PREVIOUS_HUNK;
1487 strbuf_addstr(&s->buf, ",K");
1489 if (undecided_next >= 0) {
1490 permitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;
1491 strbuf_addstr(&s->buf, ",j");
1493 if (hunk_index + 1 < file_diff->hunk_nr) {
1494 permitted |= ALLOW_GOTO_NEXT_HUNK;
1495 strbuf_addstr(&s->buf, ",J");
1497 if (file_diff->hunk_nr > 1) {
1498 permitted |= ALLOW_SEARCH_AND_GOTO;
1499 strbuf_addstr(&s->buf, ",g,/");
1501 if (hunk->splittable_into > 1) {
1502 permitted |= ALLOW_SPLIT;
1503 strbuf_addstr(&s->buf, ",s");
1505 if (hunk_index + 1 > file_diff->mode_change &&
1506 !file_diff->deleted) {
1507 permitted |= ALLOW_EDIT;
1508 strbuf_addstr(&s->buf, ",e");
1510 strbuf_addstr(&s->buf, ",p");
1512 if (file_diff->deleted)
1513 prompt_mode_type = PROMPT_DELETION;
1514 else if (file_diff->added)
1515 prompt_mode_type = PROMPT_ADDITION;
1516 else if (file_diff->mode_change && !hunk_index)
1517 prompt_mode_type = PROMPT_MODE_CHANGE;
1518 else
1519 prompt_mode_type = PROMPT_HUNK;
1521 printf("%s(%"PRIuMAX"/%"PRIuMAX") ", s->s.prompt_color,
1522 (uintmax_t)hunk_index + 1,
1523 (uintmax_t)(file_diff->hunk_nr
1524 ? file_diff->hunk_nr
1525 : 1));
1526 printf(_(s->mode->prompt_mode[prompt_mode_type]),
1527 s->buf.buf);
1528 if (*s->s.reset_color)
1529 fputs(s->s.reset_color, stdout);
1530 fflush(stdout);
1531 if (read_single_character(s) == EOF)
1532 break;
1534 if (!s->answer.len)
1535 continue;
1536 ch = tolower(s->answer.buf[0]);
1538 /* 'g' takes a hunk number and '/' takes a regexp */
1539 if (s->answer.len != 1 && (ch != 'g' && ch != '/')) {
1540 err(s, _("Only one letter is expected, got '%s'"), s->answer.buf);
1541 continue;
1543 if (ch == 'y') {
1544 hunk->use = USE_HUNK;
1545 soft_increment:
1546 hunk_index = undecided_next < 0 ?
1547 file_diff->hunk_nr : undecided_next;
1548 } else if (ch == 'n') {
1549 hunk->use = SKIP_HUNK;
1550 goto soft_increment;
1551 } else if (ch == 'a') {
1552 if (file_diff->hunk_nr) {
1553 for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
1554 hunk = file_diff->hunk + hunk_index;
1555 if (hunk->use == UNDECIDED_HUNK)
1556 hunk->use = USE_HUNK;
1558 } else if (hunk->use == UNDECIDED_HUNK) {
1559 hunk->use = USE_HUNK;
1561 } else if (ch == 'd' || ch == 'q') {
1562 if (file_diff->hunk_nr) {
1563 for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
1564 hunk = file_diff->hunk + hunk_index;
1565 if (hunk->use == UNDECIDED_HUNK)
1566 hunk->use = SKIP_HUNK;
1568 } else if (hunk->use == UNDECIDED_HUNK) {
1569 hunk->use = SKIP_HUNK;
1571 if (ch == 'q') {
1572 quit = 1;
1573 break;
1575 } else if (s->answer.buf[0] == 'K') {
1576 if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
1577 hunk_index--;
1578 else
1579 err(s, _("No previous hunk"));
1580 } else if (s->answer.buf[0] == 'J') {
1581 if (permitted & ALLOW_GOTO_NEXT_HUNK)
1582 hunk_index++;
1583 else
1584 err(s, _("No next hunk"));
1585 } else if (s->answer.buf[0] == 'k') {
1586 if (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)
1587 hunk_index = undecided_previous;
1588 else
1589 err(s, _("No previous hunk"));
1590 } else if (s->answer.buf[0] == 'j') {
1591 if (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)
1592 hunk_index = undecided_next;
1593 else
1594 err(s, _("No next hunk"));
1595 } else if (s->answer.buf[0] == 'g') {
1596 char *pend;
1597 unsigned long response;
1599 if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
1600 err(s, _("No other hunks to goto"));
1601 continue;
1603 strbuf_remove(&s->answer, 0, 1);
1604 strbuf_trim(&s->answer);
1605 i = hunk_index - DISPLAY_HUNKS_LINES / 2;
1606 if (i < (int)file_diff->mode_change)
1607 i = file_diff->mode_change;
1608 while (s->answer.len == 0) {
1609 i = display_hunks(s, file_diff, i);
1610 printf("%s", i < file_diff->hunk_nr ?
1611 _("go to which hunk (<ret> to see "
1612 "more)? ") : _("go to which hunk? "));
1613 fflush(stdout);
1614 if (strbuf_getline(&s->answer,
1615 stdin) == EOF)
1616 break;
1617 strbuf_trim_trailing_newline(&s->answer);
1620 strbuf_trim(&s->answer);
1621 response = strtoul(s->answer.buf, &pend, 10);
1622 if (*pend || pend == s->answer.buf)
1623 err(s, _("Invalid number: '%s'"),
1624 s->answer.buf);
1625 else if (0 < response && response <= file_diff->hunk_nr)
1626 hunk_index = response - 1;
1627 else
1628 err(s, Q_("Sorry, only %d hunk available.",
1629 "Sorry, only %d hunks available.",
1630 file_diff->hunk_nr),
1631 (int)file_diff->hunk_nr);
1632 } else if (s->answer.buf[0] == '/') {
1633 regex_t regex;
1634 int ret;
1636 if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
1637 err(s, _("No other hunks to search"));
1638 continue;
1640 strbuf_remove(&s->answer, 0, 1);
1641 strbuf_trim_trailing_newline(&s->answer);
1642 if (s->answer.len == 0) {
1643 printf("%s", _("search for regex? "));
1644 fflush(stdout);
1645 if (strbuf_getline(&s->answer,
1646 stdin) == EOF)
1647 break;
1648 strbuf_trim_trailing_newline(&s->answer);
1649 if (s->answer.len == 0)
1650 continue;
1652 ret = regcomp(&regex, s->answer.buf,
1653 REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
1654 if (ret) {
1655 char errbuf[1024];
1657 regerror(ret, &regex, errbuf, sizeof(errbuf));
1658 err(s, _("Malformed search regexp %s: %s"),
1659 s->answer.buf, errbuf);
1660 continue;
1662 i = hunk_index;
1663 for (;;) {
1664 /* render the hunk into a scratch buffer */
1665 render_hunk(s, file_diff->hunk + i, 0, 0,
1666 &s->buf);
1667 if (regexec(&regex, s->buf.buf, 0, NULL, 0)
1668 != REG_NOMATCH)
1669 break;
1670 i++;
1671 if (i == file_diff->hunk_nr)
1672 i = 0;
1673 if (i != hunk_index)
1674 continue;
1675 err(s, _("No hunk matches the given pattern"));
1676 break;
1678 regfree(&regex);
1679 hunk_index = i;
1680 } else if (s->answer.buf[0] == 's') {
1681 size_t splittable_into = hunk->splittable_into;
1682 if (!(permitted & ALLOW_SPLIT)) {
1683 err(s, _("Sorry, cannot split this hunk"));
1684 } else if (!split_hunk(s, file_diff,
1685 hunk - file_diff->hunk)) {
1686 color_fprintf_ln(stdout, s->s.header_color,
1687 _("Split into %d hunks."),
1688 (int)splittable_into);
1689 rendered_hunk_index = -1;
1691 } else if (s->answer.buf[0] == 'e') {
1692 if (!(permitted & ALLOW_EDIT))
1693 err(s, _("Sorry, cannot edit this hunk"));
1694 else if (edit_hunk_loop(s, file_diff, hunk) >= 0) {
1695 hunk->use = USE_HUNK;
1696 goto soft_increment;
1698 } else if (ch == 'p') {
1699 rendered_hunk_index = -1;
1700 use_pager = (s->answer.buf[0] == 'P') ? 1 : 0;
1701 } else if (s->answer.buf[0] == '?') {
1702 const char *p = _(help_patch_remainder), *eol = p;
1704 color_fprintf(stdout, s->s.help_color, "%s",
1705 _(s->mode->help_patch_text));
1708 * Show only those lines of the remainder that are
1709 * actually applicable with the current hunk.
1711 for (; *p; p = eol + (*eol == '\n')) {
1712 eol = strchrnul(p, '\n');
1715 * `s->buf` still contains the part of the
1716 * commands shown in the prompt that are not
1717 * always available.
1719 if (*p != '?' && !strchr(s->buf.buf, *p))
1720 continue;
1722 color_fprintf_ln(stdout, s->s.help_color,
1723 "%.*s", (int)(eol - p), p);
1725 } else {
1726 err(s, _("Unknown command '%s' (use '?' for help)"),
1727 s->answer.buf);
1731 /* Any hunk to be used? */
1732 for (i = 0; i < file_diff->hunk_nr; i++)
1733 if (file_diff->hunk[i].use == USE_HUNK)
1734 break;
1736 if (i < file_diff->hunk_nr ||
1737 (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
1738 /* At least one hunk selected: apply */
1739 strbuf_reset(&s->buf);
1740 reassemble_patch(s, file_diff, 0, &s->buf);
1742 discard_index(s->s.r->index);
1743 if (s->mode->apply_for_checkout)
1744 apply_for_checkout(s, &s->buf,
1745 s->mode->is_reverse);
1746 else {
1747 setup_child_process(s, &cp, "apply", NULL);
1748 strvec_pushv(&cp.args, s->mode->apply_args);
1749 if (pipe_command(&cp, s->buf.buf, s->buf.len,
1750 NULL, 0, NULL, 0))
1751 error(_("'git apply' failed"));
1753 if (repo_read_index(s->s.r) >= 0)
1754 repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
1755 1, NULL, NULL, NULL);
1758 putchar('\n');
1759 return quit;
1762 int run_add_p(struct repository *r, enum add_p_mode mode,
1763 const char *revision, const struct pathspec *ps)
1765 struct add_p_state s = {
1766 { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
1768 size_t i, binary_count = 0;
1770 init_add_i_state(&s.s, r);
1772 if (mode == ADD_P_STASH)
1773 s.mode = &patch_mode_stash;
1774 else if (mode == ADD_P_RESET) {
1775 if (!revision || !strcmp(revision, "HEAD"))
1776 s.mode = &patch_mode_reset_head;
1777 else
1778 s.mode = &patch_mode_reset_nothead;
1779 } else if (mode == ADD_P_CHECKOUT) {
1780 if (!revision)
1781 s.mode = &patch_mode_checkout_index;
1782 else if (!strcmp(revision, "HEAD"))
1783 s.mode = &patch_mode_checkout_head;
1784 else
1785 s.mode = &patch_mode_checkout_nothead;
1786 } else if (mode == ADD_P_WORKTREE) {
1787 if (!revision)
1788 s.mode = &patch_mode_checkout_index;
1789 else if (!strcmp(revision, "HEAD"))
1790 s.mode = &patch_mode_worktree_head;
1791 else
1792 s.mode = &patch_mode_worktree_nothead;
1793 } else
1794 s.mode = &patch_mode_add;
1795 s.revision = revision;
1797 discard_index(r->index);
1798 if (repo_read_index(r) < 0 ||
1799 (!s.mode->index_only &&
1800 repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
1801 NULL, NULL, NULL) < 0) ||
1802 parse_diff(&s, ps) < 0) {
1803 add_p_state_clear(&s);
1804 return -1;
1807 for (i = 0; i < s.file_diff_nr; i++)
1808 if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)
1809 binary_count++;
1810 else if (patch_update_file(&s, s.file_diff + i))
1811 break;
1813 if (s.file_diff_nr == 0)
1814 err(&s, _("No changes."));
1815 else if (binary_count == s.file_diff_nr)
1816 err(&s, _("Only binary files changed."));
1818 add_p_state_clear(&s);
1819 return 0;