Raw
1 #define USE_THE_REPOSITORY_VARIABLE
2 #define DISABLE_SIGN_COMPARE_WARNINGS
3
4 #include "git-compat-util.h"
5 #include "add-patch.h"
6 #include "advice.h"
7 #include "commit.h"
8 #include "config.h"
9 #include "diff.h"
10 #include "editor.h"
11 #include "environment.h"
12 #include "gettext.h"
13 #include "hex.h"
14 #include "object-name.h"
15 #include "pager.h"
16 #include "read-cache-ll.h"
17 #include "repository.h"
18 #include "strbuf.h"
19 #include "sigchain.h"
20 #include "run-command.h"
21 #include "strvec.h"
22 #include "pathspec.h"
23 #include "color.h"
24 #include "compat/terminal.h"
25 #include "prompt.h"
26
27 enum prompt_mode_type {
28 PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_ADDITION, PROMPT_HUNK,
29 PROMPT_MODE_MAX, /* must be last */
30 };
31
32 struct patch_mode {
33 /*
34 * The magic constant 4 is chosen such that all patch modes
35 * provide enough space for three command-line arguments followed by a
36 * trailing `NULL`.
37 */
38 const char *diff_cmd[4], *apply_args[4], *apply_check_args[4];
39 unsigned is_reverse:1, index_only:1, apply_for_checkout:1;
40 const char *prompt_mode[PROMPT_MODE_MAX];
41 const char *edit_hunk_hint, *help_patch_text;
42 };
43
44 static struct patch_mode patch_mode_add = {
45 .diff_cmd = { "diff-files", NULL },
46 .apply_args = { "--cached", NULL },
47 .apply_check_args = { "--cached", NULL },
48 .prompt_mode = {
49 N_("Stage mode change%s [y,n,q,a,d%s,?]? "),
50 N_("Stage deletion%s [y,n,q,a,d%s,?]? "),
51 N_("Stage addition%s [y,n,q,a,d%s,?]? "),
52 N_("Stage this hunk%s [y,n,q,a,d%s,?]? ")
53 },
54 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
55 "will immediately be marked for staging."),
56 .help_patch_text =
57 N_("y - stage this hunk\n"
58 "n - do not stage this hunk\n"
59 "q - quit; do not stage this hunk or any of the remaining "
60 "ones\n"
61 "a - stage this hunk and all later hunks in the file\n"
62 "d - do not stage this hunk or any of the later hunks in "
63 "the file\n")
64 };
65
66 static struct patch_mode patch_mode_stash = {
67 .diff_cmd = { "diff-index", "HEAD", NULL },
68 .apply_args = { "--cached", NULL },
69 .apply_check_args = { "--cached", NULL },
70 .prompt_mode = {
71 N_("Stash mode change%s [y,n,q,a,d%s,?]? "),
72 N_("Stash deletion%s [y,n,q,a,d%s,?]? "),
73 N_("Stash addition%s [y,n,q,a,d%s,?]? "),
74 N_("Stash this hunk%s [y,n,q,a,d%s,?]? "),
75 },
76 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
77 "will immediately be marked for stashing."),
78 .help_patch_text =
79 N_("y - stash this hunk\n"
80 "n - do not stash this hunk\n"
81 "q - quit; do not stash this hunk or any of the remaining "
82 "ones\n"
83 "a - stash this hunk and all later hunks in the file\n"
84 "d - do not stash this hunk or any of the later hunks in "
85 "the file\n"),
86 };
87
88 static struct patch_mode patch_mode_reset_head = {
89 .diff_cmd = { "diff-index", "--cached", NULL },
90 .apply_args = { "-R", "--cached", NULL },
91 .apply_check_args = { "-R", "--cached", NULL },
92 .is_reverse = 1,
93 .index_only = 1,
94 .prompt_mode = {
95 N_("Unstage mode change%s [y,n,q,a,d%s,?]? "),
96 N_("Unstage deletion%s [y,n,q,a,d%s,?]? "),
97 N_("Unstage addition%s [y,n,q,a,d%s,?]? "),
98 N_("Unstage this hunk%s [y,n,q,a,d%s,?]? "),
99 },
100 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
101 "will immediately be marked for unstaging."),
102 .help_patch_text =
103 N_("y - unstage this hunk\n"
104 "n - do not unstage this hunk\n"
105 "q - quit; do not unstage this hunk or any of the remaining "
106 "ones\n"
107 "a - unstage this hunk and all later hunks in the file\n"
108 "d - do not unstage this hunk or any of the later hunks in "
109 "the file\n"),
110 };
111
112 static struct patch_mode patch_mode_reset_nothead = {
113 .diff_cmd = { "diff-index", "-R", "--cached", NULL },
114 .apply_args = { "--cached", NULL },
115 .apply_check_args = { "--cached", NULL },
116 .index_only = 1,
117 .prompt_mode = {
118 N_("Apply mode change to index%s [y,n,q,a,d%s,?]? "),
119 N_("Apply deletion to index%s [y,n,q,a,d%s,?]? "),
120 N_("Apply addition to index%s [y,n,q,a,d%s,?]? "),
121 N_("Apply this hunk to index%s [y,n,q,a,d%s,?]? "),
122 },
123 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
124 "will immediately be marked for applying."),
125 .help_patch_text =
126 N_("y - apply this hunk to index\n"
127 "n - do not apply this hunk to index\n"
128 "q - quit; do not apply this hunk or any of the remaining "
129 "ones\n"
130 "a - apply this hunk and all later hunks in the file\n"
131 "d - do not apply this hunk or any of the later hunks in "
132 "the file\n"),
133 };
134
135 static struct patch_mode patch_mode_checkout_index = {
136 .diff_cmd = { "diff-files", NULL },
137 .apply_args = { "-R", NULL },
138 .apply_check_args = { "-R", NULL },
139 .is_reverse = 1,
140 .prompt_mode = {
141 N_("Discard mode change from worktree%s [y,n,q,a,d%s,?]? "),
142 N_("Discard deletion from worktree%s [y,n,q,a,d%s,?]? "),
143 N_("Discard addition from worktree%s [y,n,q,a,d%s,?]? "),
144 N_("Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "),
145 },
146 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
147 "will immediately be marked for discarding."),
148 .help_patch_text =
149 N_("y - discard this hunk from worktree\n"
150 "n - do not discard this hunk from worktree\n"
151 "q - quit; do not discard this hunk or any of the remaining "
152 "ones\n"
153 "a - discard this hunk and all later hunks in the file\n"
154 "d - do not discard this hunk or any of the later hunks in "
155 "the file\n"),
156 };
157
158 static struct patch_mode patch_mode_checkout_head = {
159 .diff_cmd = { "diff-index", NULL },
160 .apply_for_checkout = 1,
161 .apply_check_args = { "-R", NULL },
162 .is_reverse = 1,
163 .prompt_mode = {
164 N_("Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "),
165 N_("Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "),
166 N_("Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "),
167 N_("Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "),
168 },
169 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
170 "will immediately be marked for discarding."),
171 .help_patch_text =
172 N_("y - discard this hunk from index and worktree\n"
173 "n - do not discard this hunk from index and worktree\n"
174 "q - quit; do not discard this hunk or any of the remaining "
175 "ones\n"
176 "a - discard this hunk and all later hunks in the file\n"
177 "d - do not discard this hunk or any of the later hunks in "
178 "the file\n"),
179 };
180
181 static struct patch_mode patch_mode_checkout_nothead = {
182 .diff_cmd = { "diff-index", "-R", NULL },
183 .apply_for_checkout = 1,
184 .apply_check_args = { NULL },
185 .prompt_mode = {
186 N_("Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "),
187 N_("Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "),
188 N_("Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "),
189 N_("Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "),
190 },
191 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
192 "will immediately be marked for applying."),
193 .help_patch_text =
194 N_("y - apply this hunk to index and worktree\n"
195 "n - do not apply this hunk to index and worktree\n"
196 "q - quit; do not apply this hunk or any of the remaining "
197 "ones\n"
198 "a - apply this hunk and all later hunks in the file\n"
199 "d - do not apply this hunk or any of the later hunks in "
200 "the file\n"),
201 };
202
203 static struct patch_mode patch_mode_worktree_head = {
204 .diff_cmd = { "diff-index", NULL },
205 .apply_args = { "-R", NULL },
206 .apply_check_args = { "-R", NULL },
207 .is_reverse = 1,
208 .prompt_mode = {
209 N_("Discard mode change from worktree%s [y,n,q,a,d%s,?]? "),
210 N_("Discard deletion from worktree%s [y,n,q,a,d%s,?]? "),
211 N_("Discard addition from worktree%s [y,n,q,a,d%s,?]? "),
212 N_("Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "),
213 },
214 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
215 "will immediately be marked for discarding."),
216 .help_patch_text =
217 N_("y - discard this hunk from worktree\n"
218 "n - do not discard this hunk from worktree\n"
219 "q - quit; do not discard this hunk or any of the remaining "
220 "ones\n"
221 "a - discard this hunk and all later hunks in the file\n"
222 "d - do not discard this hunk or any of the later hunks in "
223 "the file\n"),
224 };
225
226 static struct patch_mode patch_mode_worktree_nothead = {
227 .diff_cmd = { "diff-index", "-R", NULL },
228 .apply_args = { NULL },
229 .apply_check_args = { NULL },
230 .prompt_mode = {
231 N_("Apply mode change to worktree%s [y,n,q,a,d%s,?]? "),
232 N_("Apply deletion to worktree%s [y,n,q,a,d%s,?]? "),
233 N_("Apply addition to worktree%s [y,n,q,a,d%s,?]? "),
234 N_("Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "),
235 },
236 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
237 "will immediately be marked for applying."),
238 .help_patch_text =
239 N_("y - apply this hunk to worktree\n"
240 "n - do not apply this hunk to worktree\n"
241 "q - quit; do not apply this hunk or any of the remaining "
242 "ones\n"
243 "a - apply this hunk and all later hunks in the file\n"
244 "d - do not apply this hunk or any of the later hunks in "
245 "the file\n"),
246 };
247
248 struct hunk_header {
249 unsigned long old_offset, old_count, new_offset, new_count;
250 /*
251 * Start/end offsets to the extra text after the second `@@` in the
252 * hunk header, e.g. the function signature. This is expected to
253 * include the newline.
254 */
255 size_t extra_start, extra_end, colored_extra_start, colored_extra_end;
256 unsigned suppress_colored_line_range:1;
257 };
258
259 struct hunk {
260 size_t start, end, colored_start, colored_end, splittable_into;
261 ssize_t delta;
262 enum { UNDECIDED_HUNK = 0, SKIP_HUNK, USE_HUNK } use;
263 struct hunk_header header;
264 };
265
266 struct add_p_state {
267 struct repository *r;
268 struct index_state *index;
269 const char *index_file;
270 struct interactive_config cfg;
271 struct strbuf answer, buf;
272
273 /* parsed diff */
274 struct strbuf plain, colored;
275 struct file_diff {
276 struct hunk head;
277 struct hunk *hunk;
278 size_t hunk_nr, hunk_alloc;
279 unsigned deleted:1, added:1, mode_change:1,binary:1;
280 } *file_diff;
281 size_t file_diff_nr;
282
283 /* patch mode */
284 struct patch_mode *mode;
285 const char *revision;
286 };
287
288 static void init_color(struct repository *r,
289 enum git_colorbool use_color,
290 const char *section_and_slot, char *dst,
291 const char *default_color)
292 {
293 char *key = xstrfmt("color.%s", section_and_slot);
294 const char *value;
295
296 if (!want_color(use_color))
297 dst[0] = '\0';
298 else if (repo_config_get_value(r, key, &value) ||
299 color_parse(value, dst))
300 strlcpy(dst, default_color, COLOR_MAXLEN);
301
302 free(key);
303 }
304
305 static enum git_colorbool check_color_config(struct repository *r, const char *var)
306 {
307 const char *value;
308 enum git_colorbool ret;
309
310 if (repo_config_get_value(r, var, &value))
311 ret = GIT_COLOR_UNKNOWN;
312 else
313 ret = git_config_colorbool(var, value);
314
315 /*
316 * Do not rely on want_color() to fall back to color.ui for us. It uses
317 * the value parsed by git_color_config(), which may not have been
318 * called by the main command.
319 */
320 if (ret == GIT_COLOR_UNKNOWN &&
321 !repo_config_get_value(r, "color.ui", &value))
322 ret = git_config_colorbool("color.ui", value);
323
324 return ret;
325 }
326
327 void interactive_config_init(struct interactive_config *cfg,
328 struct repository *r,
329 struct interactive_options *opts)
330 {
331 cfg->context = -1;
332 cfg->interhunkcontext = -1;
333 cfg->auto_advance = opts->auto_advance;
334
335 cfg->use_color_interactive = check_color_config(r, "color.interactive");
336
337 init_color(r, cfg->use_color_interactive, "interactive.header",
338 cfg->header_color, GIT_COLOR_BOLD);
339 init_color(r, cfg->use_color_interactive, "interactive.help",
340 cfg->help_color, GIT_COLOR_BOLD_RED);
341 init_color(r, cfg->use_color_interactive, "interactive.prompt",
342 cfg->prompt_color, GIT_COLOR_BOLD_BLUE);
343 init_color(r, cfg->use_color_interactive, "interactive.error",
344 cfg->error_color, GIT_COLOR_BOLD_RED);
345 strlcpy(cfg->reset_color_interactive,
346 want_color(cfg->use_color_interactive) ? GIT_COLOR_RESET : "", COLOR_MAXLEN);
347
348 cfg->use_color_diff = check_color_config(r, "color.diff");
349
350 init_color(r, cfg->use_color_diff, "diff.frag", cfg->fraginfo_color,
351 diff_get_color(cfg->use_color_diff, DIFF_FRAGINFO));
352 init_color(r, cfg->use_color_diff, "diff.context", cfg->context_color,
353 "fall back");
354 if (!strcmp(cfg->context_color, "fall back"))
355 init_color(r, cfg->use_color_diff, "diff.plain",
356 cfg->context_color,
357 diff_get_color(cfg->use_color_diff, DIFF_CONTEXT));
358 init_color(r, cfg->use_color_diff, "diff.old", cfg->file_old_color,
359 diff_get_color(cfg->use_color_diff, DIFF_FILE_OLD));
360 init_color(r, cfg->use_color_diff, "diff.new", cfg->file_new_color,
361 diff_get_color(cfg->use_color_diff, DIFF_FILE_NEW));
362 strlcpy(cfg->reset_color_diff,
363 want_color(cfg->use_color_diff) ? GIT_COLOR_RESET : "", COLOR_MAXLEN);
364
365 FREE_AND_NULL(cfg->interactive_diff_filter);
366 repo_config_get_string(r, "interactive.difffilter",
367 &cfg->interactive_diff_filter);
368
369 FREE_AND_NULL(cfg->interactive_diff_algorithm);
370 repo_config_get_string(r, "diff.algorithm",
371 &cfg->interactive_diff_algorithm);
372
373 if (!repo_config_get_int(r, "diff.context", &cfg->context))
374 if (cfg->context < 0)
375 die(_("%s cannot be negative"), "diff.context");
376 if (!repo_config_get_int(r, "diff.interHunkContext", &cfg->interhunkcontext))
377 if (cfg->interhunkcontext < 0)
378 die(_("%s cannot be negative"), "diff.interHunkContext");
379
380 repo_config_get_bool(r, "interactive.singlekey", &cfg->use_single_key);
381 if (cfg->use_single_key)
382 setbuf(stdin, NULL);
383
384 if (opts->context != -1) {
385 if (opts->context < 0)
386 die(_("%s cannot be negative"), "--unified");
387 cfg->context = opts->context;
388 }
389 if (opts->interhunkcontext != -1) {
390 if (opts->interhunkcontext < 0)
391 die(_("%s cannot be negative"), "--inter-hunk-context");
392 cfg->interhunkcontext = opts->interhunkcontext;
393 }
394 }
395
396 void interactive_config_clear(struct interactive_config *cfg)
397 {
398 FREE_AND_NULL(cfg->interactive_diff_filter);
399 FREE_AND_NULL(cfg->interactive_diff_algorithm);
400 memset(cfg, 0, sizeof(*cfg));
401 cfg->use_color_interactive = GIT_COLOR_UNKNOWN;
402 cfg->use_color_diff = GIT_COLOR_UNKNOWN;
403 }
404
405 static void add_p_state_clear(struct add_p_state *s)
406 {
407 size_t i;
408
409 strbuf_release(&s->answer);
410 strbuf_release(&s->buf);
411 strbuf_release(&s->plain);
412 strbuf_release(&s->colored);
413 for (i = 0; i < s->file_diff_nr; i++)
414 free(s->file_diff[i].hunk);
415 free(s->file_diff);
416 interactive_config_clear(&s->cfg);
417 }
418
419 __attribute__((format (printf, 2, 3)))
420 static void err(struct add_p_state *s, const char *fmt, ...)
421 {
422 va_list args;
423
424 va_start(args, fmt);
425 fputs(s->cfg.error_color, stdout);
426 vprintf(fmt, args);
427 puts(s->cfg.reset_color_interactive);
428 va_end(args);
429 }
430
431 LAST_ARG_MUST_BE_NULL
432 static void setup_child_process(struct add_p_state *s,
433 struct child_process *cp, ...)
434 {
435 va_list ap;
436 const char *arg;
437
438 va_start(ap, cp);
439 while ((arg = va_arg(ap, const char *)))
440 strvec_push(&cp->args, arg);
441 va_end(ap);
442
443 cp->git_cmd = 1;
444 strvec_pushf(&cp->env,
445 INDEX_ENVIRONMENT "=%s", s->index_file);
446 }
447
448 static int parse_range(const char **p,
449 unsigned long *offset, unsigned long *count)
450 {
451 char *pend;
452
453 *offset = strtoul(*p, &pend, 10);
454 if (pend == *p)
455 return -1;
456 if (*pend != ',') {
457 *count = 1;
458 *p = pend;
459 return 0;
460 }
461 *count = strtoul(pend + 1, (char **)p, 10);
462 return *p == pend + 1 ? -1 : 0;
463 }
464
465 static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk)
466 {
467 struct hunk_header *header = &hunk->header;
468 const char *line = s->plain.buf + hunk->start, *p = line;
469 const char *eol = memchr(p, '\n', s->plain.len - hunk->start);
470
471 if (!eol)
472 eol = s->plain.buf + s->plain.len;
473
474 if (!skip_prefix(p, "@@ -", &p) ||
475 parse_range(&p, &header->old_offset, &header->old_count) < 0 ||
476 !skip_prefix(p, " +", &p) ||
477 parse_range(&p, &header->new_offset, &header->new_count) < 0 ||
478 !skip_prefix(p, " @@", &p))
479 return error(_("could not parse hunk header '%.*s'"),
480 (int)(eol - line), line);
481
482 hunk->start = eol - s->plain.buf + (*eol == '\n');
483 header->extra_start = p - s->plain.buf;
484 header->extra_end = hunk->start;
485
486 if (!s->colored.len) {
487 header->colored_extra_start = header->colored_extra_end = 0;
488 return 0;
489 }
490
491 /* Now find the extra text in the colored diff */
492 line = s->colored.buf + hunk->colored_start;
493 eol = memchr(line, '\n', s->colored.len - hunk->colored_start);
494 if (!eol)
495 eol = s->colored.buf + s->colored.len;
496 p = memmem(line, eol - line, "@@ -", 4);
497 if (p && (p = memmem(p + 4, eol - p - 4, " @@", 3))) {
498 header->colored_extra_start = p + 3 - s->colored.buf;
499 } else {
500 /* could not parse colored hunk header, leave as-is */
501 header->colored_extra_start = hunk->colored_start;
502 header->suppress_colored_line_range = 1;
503 }
504 hunk->colored_start = eol - s->colored.buf + (*eol == '\n');
505 header->colored_extra_end = hunk->colored_start;
506
507 return 0;
508 }
509
510 static int is_octal(const char *p, size_t len)
511 {
512 if (!len)
513 return 0;
514
515 while (len--)
516 if (*p < '0' || *(p++) > '7')
517 return 0;
518 return 1;
519 }
520
521 static void complete_file(char marker, struct hunk *hunk)
522 {
523 if (marker == '-' || marker == '+')
524 /*
525 * Last hunk ended in non-context line (i.e. it
526 * appended lines to the file, so there are no
527 * trailing context lines).
528 */
529 hunk->splittable_into++;
530 }
531
532 /* Empty context lines may omit the leading ' ' */
533 static int normalize_marker(const char *p)
534 {
535 return p[0] == '\n' || (p[0] == '\r' && p[1] == '\n') ? ' ' : p[0];
536 }
537
538 static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
539 {
540 struct strvec args = STRVEC_INIT;
541 struct strbuf *plain = &s->plain, *colored = NULL;
542 struct child_process cp = CHILD_PROCESS_INIT;
543 char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
544 size_t file_diff_alloc = 0, i, color_arg_index;
545 struct file_diff *file_diff = NULL;
546 struct hunk *hunk = NULL;
547 int res;
548
549 strvec_pushv(&args, s->mode->diff_cmd);
550 if (s->cfg.context != -1)
551 strvec_pushf(&args, "--unified=%i", s->cfg.context);
552 if (s->cfg.interhunkcontext != -1)
553 strvec_pushf(&args, "--inter-hunk-context=%i", s->cfg.interhunkcontext);
554 if (s->cfg.interactive_diff_algorithm)
555 strvec_pushf(&args, "--diff-algorithm=%s", s->cfg.interactive_diff_algorithm);
556 if (s->revision) {
557 struct object_id oid;
558 strvec_push(&args,
559 /* could be on an unborn branch */
560 !strcmp("HEAD", s->revision) &&
561 repo_get_oid(s->r, "HEAD", &oid) ?
562 empty_tree_oid_hex(s->r->hash_algo) : s->revision);
563 }
564 color_arg_index = args.nr;
565 /* Use `--no-color` explicitly, just in case `diff.color = always`. */
566 strvec_pushl(&args, "--no-color", "--ignore-submodules=dirty", "-p",
567 "--", NULL);
568 for (i = 0; i < ps->nr; i++)
569 strvec_push(&args, ps->items[i].original);
570
571 setup_child_process(s, &cp, NULL);
572 strvec_pushv(&cp.args, args.v);
573 res = capture_command(&cp, plain, 0);
574 if (res) {
575 strvec_clear(&args);
576 return error(_("could not parse diff"));
577 }
578 if (!plain->len) {
579 strvec_clear(&args);
580 return 0;
581 }
582 strbuf_complete_line(plain);
583
584 if (want_color_fd(1, s->cfg.use_color_diff)) {
585 struct child_process colored_cp = CHILD_PROCESS_INIT;
586 const char *diff_filter = s->cfg.interactive_diff_filter;
587
588 setup_child_process(s, &colored_cp, NULL);
589 xsnprintf((char *)args.v[color_arg_index], 8, "--color");
590 strvec_pushv(&colored_cp.args, args.v);
591 colored = &s->colored;
592 res = capture_command(&colored_cp, colored, 0);
593 strvec_clear(&args);
594 if (res)
595 return error(_("could not parse colored diff"));
596
597 if (diff_filter) {
598 struct child_process filter_cp = CHILD_PROCESS_INIT;
599
600 setup_child_process(s, &filter_cp,
601 diff_filter, NULL);
602 filter_cp.git_cmd = 0;
603 filter_cp.use_shell = 1;
604 strbuf_reset(&s->buf);
605 if (pipe_command(&filter_cp,
606 colored->buf, colored->len,
607 &s->buf, colored->len,
608 NULL, 0) < 0)
609 return error(_("failed to run '%s'"),
610 diff_filter);
611 strbuf_swap(colored, &s->buf);
612 }
613
614 strbuf_complete_line(colored);
615 colored_p = colored->buf;
616 colored_pend = colored_p + colored->len;
617 }
618 strvec_clear(&args);
619
620 /* parse files and hunks */
621 p = plain->buf;
622 pend = p + plain->len;
623 while (p != pend) {
624 char *eol = memchr(p, '\n', pend - p);
625 const char *deleted = NULL, *mode_change = NULL;
626 char ch = normalize_marker(p);
627
628 if (!eol)
629 eol = pend;
630
631 if (starts_with(p, "diff ") ||
632 starts_with(p, "* Unmerged path ")) {
633 complete_file(marker, hunk);
634 ALLOC_GROW_BY(s->file_diff, s->file_diff_nr, 1,
635 file_diff_alloc);
636 file_diff = s->file_diff + s->file_diff_nr - 1;
637 hunk = &file_diff->head;
638 hunk->start = p - plain->buf;
639 if (colored_p)
640 hunk->colored_start = colored_p - colored->buf;
641 marker = '\0';
642 } else if (p == plain->buf)
643 BUG("diff starts with unexpected line:\n"
644 "%.*s\n", (int)(eol - p), p);
645 else if (file_diff->deleted)
646 ; /* keep the rest of the file in a single "hunk" */
647 else if (starts_with(p, "@@ ") ||
648 (hunk == &file_diff->head &&
649 (skip_prefix(p, "deleted file", &deleted)))) {
650 if (marker == '-' || marker == '+')
651 /*
652 * Should not happen; previous hunk did not end
653 * in a context line? Handle it anyway.
654 */
655 hunk->splittable_into++;
656
657 ALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,
658 file_diff->hunk_alloc);
659 hunk = file_diff->hunk + file_diff->hunk_nr - 1;
660
661 hunk->start = p - plain->buf;
662 if (colored)
663 hunk->colored_start = colored_p - colored->buf;
664
665 if (deleted)
666 file_diff->deleted = 1;
667 else if (parse_hunk_header(s, hunk) < 0)
668 return -1;
669
670 /*
671 * Start counting into how many hunks this one can be
672 * split
673 */
674 marker = ch;
675 } else if (hunk == &file_diff->head &&
676 starts_with(p, "new file")) {
677 file_diff->added = 1;
678 } else if (hunk == &file_diff->head &&
679 skip_prefix(p, "old mode ", &mode_change) &&
680 is_octal(mode_change, eol - mode_change)) {
681 if (file_diff->mode_change)
682 BUG("double mode change?\n\n%.*s",
683 (int)(eol - plain->buf), plain->buf);
684 if (file_diff->hunk_nr)
685 BUG("mode change in the middle?\n\n%.*s",
686 (int)(eol - plain->buf), plain->buf);
687
688 /*
689 * Do *not* change `hunk`: the mode change pseudo-hunk
690 * is _part of_ the header "hunk".
691 */
692 file_diff->mode_change = 1;
693 ALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,
694 file_diff->hunk_alloc);
695 file_diff->hunk->start = p - plain->buf;
696 if (colored_p)
697 file_diff->hunk->colored_start =
698 colored_p - colored->buf;
699 } else if (hunk == &file_diff->head &&
700 skip_prefix(p, "new mode ", &mode_change) &&
701 is_octal(mode_change, eol - mode_change)) {
702
703 /*
704 * Extend the "mode change" pseudo-hunk to include also
705 * the "new mode" line.
706 */
707 if (!file_diff->mode_change)
708 BUG("'new mode' without 'old mode'?\n\n%.*s",
709 (int)(eol - plain->buf), plain->buf);
710 if (file_diff->hunk_nr != 1)
711 BUG("mode change in the middle?\n\n%.*s",
712 (int)(eol - plain->buf), plain->buf);
713 if (p - plain->buf != file_diff->hunk->end)
714 BUG("'new mode' does not immediately follow "
715 "'old mode'?\n\n%.*s",
716 (int)(eol - plain->buf), plain->buf);
717 } else if (hunk == &file_diff->head &&
718 starts_with(p, "Binary files "))
719 file_diff->binary = 1;
720
721 if (!!file_diff->deleted + !!file_diff->added +
722 !!file_diff->mode_change > 1)
723 BUG("diff can only contain delete *or* add *or* a "
724 "mode change?!?\n%.*s",
725 (int)(eol - (plain->buf + file_diff->head.start)),
726 plain->buf + file_diff->head.start);
727
728 if ((marker == '-' || marker == '+') && ch == ' ')
729 hunk->splittable_into++;
730 if (marker && ch != '\\')
731 marker = ch;
732
733 p = eol == pend ? pend : eol + 1;
734 hunk->end = p - plain->buf;
735
736 if (colored) {
737 char *colored_eol = memchr(colored_p, '\n',
738 colored_pend - colored_p);
739 if (colored_eol)
740 colored_p = colored_eol + 1;
741 else if (p != pend)
742 /* non-colored has more lines? */
743 goto mismatched_output;
744 else if (colored_p == colored_pend)
745 /* last line has no matching colored one? */
746 goto mismatched_output;
747 else
748 colored_p = colored_pend;
749
750 hunk->colored_end = colored_p - colored->buf;
751 }
752
753 if (mode_change) {
754 if (file_diff->hunk_nr != 1)
755 BUG("mode change in hunk #%d???",
756 (int)file_diff->hunk_nr);
757 /* Adjust the end of the "mode change" pseudo-hunk */
758 file_diff->hunk->end = hunk->end;
759 if (colored)
760 file_diff->hunk->colored_end = hunk->colored_end;
761 }
762 }
763 complete_file(marker, hunk);
764
765 /* non-colored shorter than colored? */
766 if (colored_p != colored_pend) {
767 mismatched_output:
768 error(_("mismatched output from interactive.diffFilter"));
769 advise(_("Your filter must maintain a one-to-one correspondence\n"
770 "between its input and output lines."));
771 return -1;
772 }
773
774 return 0;
775 }
776
777 static size_t find_next_line(struct strbuf *sb, size_t offset)
778 {
779 char *eol;
780
781 if (offset >= sb->len)
782 BUG("looking for next line beyond buffer (%d >= %d)\n%s",
783 (int)offset, (int)sb->len, sb->buf);
784
785 eol = memchr(sb->buf + offset, '\n', sb->len - offset);
786 if (!eol)
787 return sb->len;
788 return eol - sb->buf + 1;
789 }
790
791 static void render_hunk(struct add_p_state *s, struct hunk *hunk,
792 ssize_t delta, int colored, struct strbuf *out)
793 {
794 struct hunk_header *header = &hunk->header;
795
796 if (hunk->header.old_offset != 0 || hunk->header.new_offset != 0) {
797 /*
798 * Generate the hunk header dynamically, except for special
799 * hunks (such as the diff header).
800 */
801 const char *p;
802 size_t len;
803 unsigned long old_offset = header->old_offset;
804 unsigned long new_offset = header->new_offset;
805
806 if (!colored) {
807 p = s->plain.buf + header->extra_start;
808 len = header->extra_end - header->extra_start;
809 } else if (header->suppress_colored_line_range) {
810 strbuf_add(out,
811 s->colored.buf + header->colored_extra_start,
812 header->colored_extra_end -
813 header->colored_extra_start);
814
815 strbuf_add(out, s->colored.buf + hunk->colored_start,
816 hunk->colored_end - hunk->colored_start);
817 return;
818 } else {
819 strbuf_addstr(out, s->cfg.fraginfo_color);
820 p = s->colored.buf + header->colored_extra_start;
821 len = header->colored_extra_end
822 - header->colored_extra_start;
823 }
824
825 if (s->mode->is_reverse)
826 old_offset -= delta;
827 else
828 new_offset += delta;
829
830 strbuf_addf(out, "@@ -%lu", old_offset);
831 if (header->old_count != 1)
832 strbuf_addf(out, ",%lu", header->old_count);
833 strbuf_addf(out, " +%lu", new_offset);
834 if (header->new_count != 1)
835 strbuf_addf(out, ",%lu", header->new_count);
836 strbuf_addstr(out, " @@");
837
838 if (len)
839 strbuf_add(out, p, len);
840 else if (colored)
841 strbuf_addf(out, "%s\n", s->cfg.reset_color_diff);
842 else
843 strbuf_addch(out, '\n');
844 }
845
846 if (colored)
847 strbuf_add(out, s->colored.buf + hunk->colored_start,
848 hunk->colored_end - hunk->colored_start);
849 else
850 strbuf_add(out, s->plain.buf + hunk->start,
851 hunk->end - hunk->start);
852 }
853
854 static void render_diff_header(struct add_p_state *s,
855 struct file_diff *file_diff, int colored,
856 struct strbuf *out)
857 {
858 /*
859 * If there was a mode change, the first hunk is a pseudo hunk that
860 * corresponds to the mode line in the header. If the user did not want
861 * to stage that "hunk", we actually have to cut it out from the header.
862 */
863 int skip_mode_change =
864 file_diff->mode_change && file_diff->hunk->use != USE_HUNK;
865 struct hunk *head = &file_diff->head, *first = file_diff->hunk;
866
867 if (!skip_mode_change) {
868 render_hunk(s, head, 0, colored, out);
869 return;
870 }
871
872 if (colored) {
873 const char *p = s->colored.buf;
874
875 strbuf_add(out, p + head->colored_start,
876 first->colored_start - head->colored_start);
877 strbuf_add(out, p + first->colored_end,
878 head->colored_end - first->colored_end);
879 } else {
880 const char *p = s->plain.buf;
881
882 strbuf_add(out, p + head->start, first->start - head->start);
883 strbuf_add(out, p + first->end, head->end - first->end);
884 }
885 }
886
887 /* Coalesce hunks again that were split */
888 static int merge_hunks(struct add_p_state *s, struct file_diff *file_diff,
889 size_t *hunk_index, int use_all, struct hunk *merged)
890 {
891 size_t i = *hunk_index, delta;
892 struct hunk *hunk = file_diff->hunk + i;
893 /* `header` corresponds to the merged hunk */
894 struct hunk_header *header = &merged->header, *next;
895
896 if (!use_all && hunk->use != USE_HUNK)
897 return 0;
898
899 *merged = *hunk;
900 /* We simply skip the colored part (if any) when merging hunks */
901 merged->colored_start = merged->colored_end = 0;
902
903 for (; i + 1 < file_diff->hunk_nr; i++) {
904 hunk++;
905 next = &hunk->header;
906
907 /*
908 * Stop merging hunks when:
909 *
910 * - the hunk is not selected for use, or
911 * - the hunk does not overlap with the already-merged hunk(s)
912 */
913 if ((!use_all && hunk->use != USE_HUNK) ||
914 header->new_offset >= next->new_offset + merged->delta ||
915 header->new_offset + header->new_count
916 < next->new_offset + merged->delta)
917 break;
918
919 /*
920 * If the hunks were not edited, and overlap, we can simply
921 * extend the line range.
922 */
923 if (merged->start < hunk->start && merged->end > hunk->start) {
924 merged->end = hunk->end;
925 merged->colored_end = hunk->colored_end;
926 delta = 0;
927 } else {
928 const char *plain = s->plain.buf;
929 size_t overlapping_line_count = header->new_offset
930 + header->new_count - merged->delta
931 - next->new_offset;
932 size_t overlap_end = hunk->start;
933 size_t overlap_start = overlap_end;
934 size_t overlap_next, len, j;
935
936 /*
937 * One of the hunks was edited: the modified hunk was
938 * appended to the strbuf `s->plain`.
939 *
940 * Let's ensure that at least the last context line of
941 * the first hunk overlaps with the corresponding line
942 * of the second hunk, and then merge.
943 */
944 for (j = 0; j < overlapping_line_count; j++) {
945 overlap_next = find_next_line(&s->plain,
946 overlap_end);
947
948 if (overlap_next > hunk->end)
949 BUG("failed to find %d context lines "
950 "in:\n%.*s",
951 (int)overlapping_line_count,
952 (int)(hunk->end - hunk->start),
953 plain + hunk->start);
954
955 if (normalize_marker(&plain[overlap_end]) != ' ')
956 return error(_("expected context line "
957 "#%d in\n%.*s"),
958 (int)(j + 1),
959 (int)(hunk->end
960 - hunk->start),
961 plain + hunk->start);
962
963 overlap_start = overlap_end;
964 overlap_end = overlap_next;
965 }
966 len = overlap_end - overlap_start;
967
968 if (len > merged->end - merged->start ||
969 memcmp(plain + merged->end - len,
970 plain + overlap_start, len))
971 return error(_("hunks do not overlap:\n%.*s\n"
972 "\tdoes not end with:\n%.*s"),
973 (int)(merged->end - merged->start),
974 plain + merged->start,
975 (int)len, plain + overlap_start);
976
977 /*
978 * Since the start-end ranges are not adjacent, we
979 * cannot simply take the union of the ranges. To
980 * address that, we temporarily append the union of the
981 * lines to the `plain` strbuf.
982 */
983 if (merged->end != s->plain.len) {
984 size_t start = s->plain.len;
985
986 strbuf_add(&s->plain, plain + merged->start,
987 merged->end - merged->start);
988 plain = s->plain.buf;
989 merged->start = start;
990 merged->end = s->plain.len;
991 }
992
993 strbuf_add(&s->plain,
994 plain + overlap_end,
995 hunk->end - overlap_end);
996 merged->end = s->plain.len;
997 merged->splittable_into += hunk->splittable_into;
998 delta = merged->delta;
999 merged->delta += hunk->delta;
1000 }
1001
1002 header->old_count = next->old_offset + next->old_count
1003 - header->old_offset;
1004 header->new_count = next->new_offset + delta
1005 + next->new_count - header->new_offset;
1006 }
1007
1008 if (i == *hunk_index)
1009 return 0;
1010
1011 *hunk_index = i;
1012 return 1;
1013 }
1014
1015 static void reassemble_patch(struct add_p_state *s,
1016 struct file_diff *file_diff, int use_all,
1017 struct strbuf *out)
1018 {
1019 struct hunk *hunk;
1020 size_t save_len = s->plain.len, i;
1021 ssize_t delta = 0;
1022
1023 render_diff_header(s, file_diff, 0, out);
1024
1025 for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) {
1026 struct hunk merged = { 0 };
1027
1028 hunk = file_diff->hunk + i;
1029 if (!use_all && hunk->use != USE_HUNK)
1030 delta += hunk->header.old_count
1031 - hunk->header.new_count;
1032 else {
1033 /* merge overlapping hunks into a temporary hunk */
1034 if (merge_hunks(s, file_diff, &i, use_all, &merged))
1035 hunk = &merged;
1036
1037 render_hunk(s, hunk, delta, 0, out);
1038
1039 /*
1040 * In case `merge_hunks()` used `plain` as a scratch
1041 * pad (this happens when an edited hunk had to be
1042 * coalesced with another hunk).
1043 */
1044 strbuf_setlen(&s->plain, save_len);
1045
1046 delta += hunk->delta;
1047 }
1048 }
1049 }
1050
1051 static int split_hunk(struct add_p_state *s, struct file_diff *file_diff,
1052 size_t hunk_index)
1053 {
1054 int colored = !!s->colored.len, first = 1;
1055 struct hunk *hunk = file_diff->hunk + hunk_index;
1056 size_t splittable_into;
1057 size_t end, colored_end, current, colored_current = 0, context_line_count;
1058 struct hunk_header remaining, *header;
1059 char marker, ch;
1060
1061 if (hunk_index >= file_diff->hunk_nr)
1062 BUG("invalid hunk index: %d (must be >= 0 and < %d)",
1063 (int)hunk_index, (int)file_diff->hunk_nr);
1064
1065 if (hunk->splittable_into < 2)
1066 return 0;
1067 splittable_into = hunk->splittable_into;
1068
1069 end = hunk->end;
1070 colored_end = hunk->colored_end;
1071
1072 remaining = hunk->header;
1073
1074 file_diff->hunk_nr += splittable_into - 1;
1075 ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr, file_diff->hunk_alloc);
1076 if (hunk_index + splittable_into < file_diff->hunk_nr)
1077 memmove(file_diff->hunk + hunk_index + splittable_into,
1078 file_diff->hunk + hunk_index + 1,
1079 (file_diff->hunk_nr - hunk_index - splittable_into)
1080 * sizeof(*hunk));
1081 hunk = file_diff->hunk + hunk_index;
1082 hunk->splittable_into = 1;
1083 hunk->use = UNDECIDED_HUNK;
1084 memset(hunk + 1, 0, (splittable_into - 1) * sizeof(*hunk));
1085
1086 header = &hunk->header;
1087 header->old_count = header->new_count = 0;
1088
1089 current = hunk->start;
1090 if (colored)
1091 colored_current = hunk->colored_start;
1092 marker = '\0';
1093 context_line_count = 0;
1094
1095 while (splittable_into > 1) {
1096 ch = normalize_marker(&s->plain.buf[current]);
1097
1098 if (!ch)
1099 BUG("buffer overrun while splitting hunks");
1100
1101 /*
1102 * Is this the first context line after a chain of +/- lines?
1103 * Then record the start of the next split hunk.
1104 */
1105 if ((marker == '-' || marker == '+') && ch == ' ') {
1106 first = 0;
1107 hunk[1].start = current;
1108 if (colored)
1109 hunk[1].colored_start = colored_current;
1110 context_line_count = 0;
1111 }
1112
1113 /*
1114 * Was the previous line a +/- one? Alternatively, is this the
1115 * first line (and not a +/- one)?
1116 *
1117 * Then just increment the appropriate counter and continue
1118 * with the next line.
1119 */
1120 if (marker != ' ' || (ch != '-' && ch != '+')) {
1121 next_hunk_line:
1122 /* Comment lines are attached to the previous line */
1123 if (ch == '\\')
1124 ch = marker ? marker : ' ';
1125
1126 /* current hunk not done yet */
1127 if (ch == ' ')
1128 context_line_count++;
1129 else if (ch == '-')
1130 header->old_count++;
1131 else if (ch == '+')
1132 header->new_count++;
1133 else
1134 BUG("unhandled diff marker: '%c'", ch);
1135 marker = ch;
1136 current = find_next_line(&s->plain, current);
1137 if (colored)
1138 colored_current =
1139 find_next_line(&s->colored,
1140 colored_current);
1141 continue;
1142 }
1143
1144 /*
1145 * We got us the start of a new hunk!
1146 *
1147 * This is a context line, so it is shared with the previous
1148 * hunk, if any.
1149 */
1150
1151 if (first) {
1152 if (header->old_count || header->new_count)
1153 BUG("counts are off: %d/%d",
1154 (int)header->old_count,
1155 (int)header->new_count);
1156
1157 header->old_count = context_line_count;
1158 header->new_count = context_line_count;
1159 context_line_count = 0;
1160 first = 0;
1161 goto next_hunk_line;
1162 }
1163
1164 remaining.old_offset += header->old_count;
1165 remaining.old_count -= header->old_count;
1166 remaining.new_offset += header->new_count;
1167 remaining.new_count -= header->new_count;
1168
1169 /* initialize next hunk header's offsets */
1170 hunk[1].header.old_offset =
1171 header->old_offset + header->old_count;
1172 hunk[1].header.new_offset =
1173 header->new_offset + header->new_count;
1174
1175 /* add one split hunk */
1176 header->old_count += context_line_count;
1177 header->new_count += context_line_count;
1178
1179 hunk->end = current;
1180 if (colored)
1181 hunk->colored_end = colored_current;
1182
1183 hunk++;
1184 hunk->splittable_into = 1;
1185 hunk->use = UNDECIDED_HUNK;
1186 header = &hunk->header;
1187
1188 header->old_count = header->new_count = context_line_count;
1189 context_line_count = 0;
1190
1191 splittable_into--;
1192 marker = ch;
1193 }
1194
1195 /* last hunk simply gets the rest */
1196 if (header->old_offset != remaining.old_offset)
1197 BUG("miscounted old_offset: %lu != %lu",
1198 header->old_offset, remaining.old_offset);
1199 if (header->new_offset != remaining.new_offset)
1200 BUG("miscounted new_offset: %lu != %lu",
1201 header->new_offset, remaining.new_offset);
1202 header->old_count = remaining.old_count;
1203 header->new_count = remaining.new_count;
1204 hunk->end = end;
1205 if (colored)
1206 hunk->colored_end = colored_end;
1207
1208 return 0;
1209 }
1210
1211 static void recolor_hunk(struct add_p_state *s, struct hunk *hunk)
1212 {
1213 const char *plain = s->plain.buf;
1214 size_t current, eol, next;
1215
1216 if (!s->colored.len)
1217 return;
1218
1219 hunk->colored_start = s->colored.len;
1220 for (current = hunk->start; current < hunk->end; ) {
1221 for (eol = current; eol < hunk->end; eol++)
1222 if (plain[eol] == '\n')
1223 break;
1224 next = eol + (eol < hunk->end);
1225 if (eol > current && plain[eol - 1] == '\r')
1226 eol--;
1227
1228 strbuf_addstr(&s->colored,
1229 plain[current] == '-' ?
1230 s->cfg.file_old_color :
1231 plain[current] == '+' ?
1232 s->cfg.file_new_color :
1233 s->cfg.context_color);
1234 strbuf_add(&s->colored, plain + current, eol - current);
1235 strbuf_addstr(&s->colored, s->cfg.reset_color_diff);
1236 if (next > eol)
1237 strbuf_add(&s->colored, plain + eol, next - eol);
1238 current = next;
1239 }
1240 hunk->colored_end = s->colored.len;
1241 }
1242
1243 static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
1244 {
1245 size_t i;
1246
1247 strbuf_reset(&s->buf);
1248 strbuf_commented_addf(&s->buf, comment_line_str,
1249 _("Manual hunk edit mode -- see bottom for "
1250 "a quick guide.\n"));
1251 render_hunk(s, hunk, 0, 0, &s->buf);
1252 strbuf_commented_addf(&s->buf, comment_line_str,
1253 _("---\n"
1254 "To remove '%c' lines, make them ' ' lines "
1255 "(context).\n"
1256 "To remove '%c' lines, delete them.\n"
1257 "Lines starting with %s will be removed.\n"),
1258 s->mode->is_reverse ? '+' : '-',
1259 s->mode->is_reverse ? '-' : '+',
1260 comment_line_str);
1261 strbuf_commented_addf(&s->buf, comment_line_str, "%s",
1262 _(s->mode->edit_hunk_hint));
1263 /*
1264 * TRANSLATORS: 'it' refers to the patch mentioned in the previous
1265 * messages.
1266 */
1267 strbuf_commented_addf(&s->buf, comment_line_str,
1268 _("If it does not apply cleanly, you will be "
1269 "given an opportunity to\n"
1270 "edit again. If all lines of the hunk are "
1271 "removed, then the edit is\n"
1272 "aborted and the hunk is left unchanged.\n"));
1273
1274 if (strbuf_edit_interactively(s->r, &s->buf,
1275 "addp-hunk-edit.diff", NULL) < 0)
1276 return -1;
1277
1278 /* strip out commented lines */
1279 hunk->start = s->plain.len;
1280 for (i = 0; i < s->buf.len; ) {
1281 size_t next = find_next_line(&s->buf, i);
1282
1283 if (!starts_with(s->buf.buf + i, comment_line_str))
1284 strbuf_add(&s->plain, s->buf.buf + i, next - i);
1285 i = next;
1286 }
1287
1288 hunk->end = s->plain.len;
1289 if (hunk->end == hunk->start)
1290 /* The user aborted editing by deleting everything */
1291 return 0;
1292
1293 recolor_hunk(s, hunk);
1294
1295 /*
1296 * If the hunk header is intact, parse it, otherwise simply use the
1297 * hunk header prior to editing (which will adjust `hunk->start` to
1298 * skip the hunk header).
1299 */
1300 if (s->plain.buf[hunk->start] == '@' &&
1301 parse_hunk_header(s, hunk) < 0)
1302 return error(_("could not parse hunk header"));
1303
1304 return 1;
1305 }
1306
1307 static ssize_t recount_edited_hunk(struct add_p_state *s, struct hunk *hunk,
1308 size_t orig_old_count, size_t orig_new_count)
1309 {
1310 struct hunk_header *header = &hunk->header;
1311 size_t i;
1312 char ch, marker = ' ';
1313
1314 hunk->splittable_into = 0;
1315 header->old_count = header->new_count = 0;
1316 for (i = hunk->start; i < hunk->end; ) {
1317 ch = normalize_marker(&s->plain.buf[i]);
1318 switch (ch) {
1319 case '-':
1320 header->old_count++;
1321 if (marker == ' ')
1322 hunk->splittable_into++;
1323 marker = ch;
1324 break;
1325 case '+':
1326 header->new_count++;
1327 if (marker == ' ')
1328 hunk->splittable_into++;
1329 marker = ch;
1330 break;
1331 case ' ':
1332 header->old_count++;
1333 header->new_count++;
1334 marker = ch;
1335 break;
1336 }
1337
1338 i = find_next_line(&s->plain, i);
1339 }
1340
1341 return orig_old_count - orig_new_count
1342 - header->old_count + header->new_count;
1343 }
1344
1345 static int run_apply_check(struct add_p_state *s,
1346 struct file_diff *file_diff)
1347 {
1348 struct child_process cp = CHILD_PROCESS_INIT;
1349
1350 strbuf_reset(&s->buf);
1351 reassemble_patch(s, file_diff, 1, &s->buf);
1352
1353 setup_child_process(s, &cp,
1354 "apply", "--check", NULL);
1355 strvec_pushv(&cp.args, s->mode->apply_check_args);
1356 if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
1357 return error(_("'git apply --cached' failed"));
1358
1359 return 0;
1360 }
1361
1362 static int read_single_character(struct add_p_state *s)
1363 {
1364 if (s->cfg.use_single_key) {
1365 int res = read_key_without_echo(&s->answer);
1366 printf("%s\n", res == EOF ? "" : s->answer.buf);
1367 return res;
1368 }
1369
1370 if (git_read_line_interactively(&s->answer) == EOF)
1371 return EOF;
1372 return 0;
1373 }
1374
1375 static int prompt_yesno(struct add_p_state *s, const char *prompt)
1376 {
1377 for (;;) {
1378 color_fprintf(stdout, s->cfg.prompt_color, "%s", _(prompt));
1379 fflush(stdout);
1380 if (read_single_character(s) == EOF)
1381 return -1;
1382 /* do not limit to 1-byte input to allow 'no' etc. */
1383 switch (tolower(s->answer.buf[0])) {
1384 case 'n': return 0;
1385 case 'y': return 1;
1386 }
1387 }
1388 }
1389
1390 static int edit_hunk_loop(struct add_p_state *s,
1391 struct file_diff *file_diff, struct hunk *hunk)
1392 {
1393 size_t plain_len = s->plain.len, colored_len = s->colored.len;
1394 struct hunk backup;
1395
1396 backup = *hunk;
1397
1398 for (;;) {
1399 int res = edit_hunk_manually(s, hunk);
1400 if (res == 0) {
1401 /* abandoned */
1402 *hunk = backup;
1403 return -1;
1404 }
1405
1406 if (res > 0) {
1407 hunk->delta +=
1408 recount_edited_hunk(s, hunk,
1409 backup.header.old_count,
1410 backup.header.new_count);
1411 if (!run_apply_check(s, file_diff))
1412 return 0;
1413 }
1414
1415 /* Drop edits (they were appended to s->plain) */
1416 strbuf_setlen(&s->plain, plain_len);
1417 strbuf_setlen(&s->colored, colored_len);
1418 *hunk = backup;
1419
1420 /*
1421 * TRANSLATORS: do not translate [y/n]
1422 * The program will only accept that input at this point.
1423 * Consider translating (saying "no" discards!) as
1424 * (saying "n" for "no" discards!) if the translation
1425 * of the word "no" does not start with n.
1426 */
1427 res = prompt_yesno(s, _("Your edited hunk does not apply. "
1428 "Edit again (saying \"no\" discards!) "
1429 "[y/n]? "));
1430 if (res < 1)
1431 return -1;
1432 }
1433 }
1434
1435 static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
1436 int is_reverse)
1437 {
1438 const char *reverse = is_reverse ? "-R" : NULL;
1439 struct child_process check_index = CHILD_PROCESS_INIT;
1440 struct child_process check_worktree = CHILD_PROCESS_INIT;
1441 struct child_process apply_index = CHILD_PROCESS_INIT;
1442 struct child_process apply_worktree = CHILD_PROCESS_INIT;
1443 int applies_index, applies_worktree;
1444
1445 setup_child_process(s, &check_index,
1446 "apply", "--cached", "--check", reverse, NULL);
1447 applies_index = !pipe_command(&check_index, diff->buf, diff->len,
1448 NULL, 0, NULL, 0);
1449
1450 setup_child_process(s, &check_worktree,
1451 "apply", "--check", reverse, NULL);
1452 applies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,
1453 NULL, 0, NULL, 0);
1454
1455 if (applies_worktree && applies_index) {
1456 setup_child_process(s, &apply_index,
1457 "apply", "--cached", reverse, NULL);
1458 pipe_command(&apply_index, diff->buf, diff->len,
1459 NULL, 0, NULL, 0);
1460
1461 setup_child_process(s, &apply_worktree,
1462 "apply", reverse, NULL);
1463 pipe_command(&apply_worktree, diff->buf, diff->len,
1464 NULL, 0, NULL, 0);
1465
1466 return 1;
1467 }
1468
1469 if (!applies_index) {
1470 err(s, _("The selected hunks do not apply to the index!"));
1471 if (prompt_yesno(s, _("Apply them to the worktree "
1472 "anyway? ")) > 0) {
1473 setup_child_process(s, &apply_worktree,
1474 "apply", reverse, NULL);
1475 return pipe_command(&apply_worktree, diff->buf,
1476 diff->len, NULL, 0, NULL, 0);
1477 }
1478 err(s, _("Nothing was applied.\n"));
1479 } else
1480 /* As a last resort, show the diff to the user */
1481 fwrite(diff->buf, diff->len, 1, stdout);
1482
1483 return 0;
1484 }
1485
1486 #define SUMMARY_HEADER_WIDTH 20
1487 #define SUMMARY_LINE_WIDTH 80
1488 static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
1489 struct strbuf *out)
1490 {
1491 struct hunk_header *header = &hunk->header;
1492 struct strbuf *plain = &s->plain;
1493 size_t len = out->len, i;
1494
1495 strbuf_addf(out, " -%lu,%lu +%lu,%lu ",
1496 header->old_offset, header->old_count,
1497 header->new_offset, header->new_count);
1498 if (out->len - len < SUMMARY_HEADER_WIDTH)
1499 strbuf_addchars(out, ' ',
1500 SUMMARY_HEADER_WIDTH + len - out->len);
1501 for (i = hunk->start; i < hunk->end; i = find_next_line(plain, i))
1502 if (plain->buf[i] != ' ')
1503 break;
1504 if (i < hunk->end)
1505 strbuf_add(out, plain->buf + i, find_next_line(plain, i) - i);
1506 if (out->len - len > SUMMARY_LINE_WIDTH)
1507 strbuf_setlen(out, len + SUMMARY_LINE_WIDTH);
1508 strbuf_complete_line(out);
1509 }
1510
1511 #define DISPLAY_HUNKS_LINES 20
1512 static size_t display_hunks(struct add_p_state *s,
1513 struct file_diff *file_diff, size_t start_index)
1514 {
1515 size_t end_index = start_index + DISPLAY_HUNKS_LINES;
1516
1517 if (end_index > file_diff->hunk_nr)
1518 end_index = file_diff->hunk_nr;
1519
1520 while (start_index < end_index) {
1521 struct hunk *hunk = file_diff->hunk + start_index++;
1522
1523 strbuf_reset(&s->buf);
1524 strbuf_addf(&s->buf, "%c%2d: ", hunk->use == USE_HUNK ? '+'
1525 : hunk->use == SKIP_HUNK ? '-' : ' ',
1526 (int)start_index);
1527 summarize_hunk(s, hunk, &s->buf);
1528 fputs(s->buf.buf, stdout);
1529 }
1530
1531 return end_index;
1532 }
1533
1534 static const char help_patch_remainder[] =
1535 N_("j - go to the next undecided hunk, roll over at the bottom\n"
1536 "J - go to the next hunk, roll over at the bottom\n"
1537 "k - go to the previous undecided hunk, roll over at the top\n"
1538 "K - go to the previous hunk, roll over at the top\n"
1539 "g - select a hunk to go to\n"
1540 "/ - search for a hunk matching the given regex\n"
1541 "s - split the current hunk into smaller hunks\n"
1542 "e - manually edit the current hunk\n"
1543 "p - print the current hunk\n"
1544 "P - print the current hunk using the pager\n"
1545 "> - go to the next file, roll over at the bottom\n"
1546 "< - go to the previous file, roll over at the top\n"
1547 "? - print help\n"
1548 "HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\n");
1549
1550 static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)
1551 {
1552 struct child_process cp = CHILD_PROCESS_INIT;
1553 size_t j;
1554
1555 /* Any hunk to be used? */
1556 for (j = 0; j < file_diff->hunk_nr; j++)
1557 if (file_diff->hunk[j].use == USE_HUNK)
1558 break;
1559
1560 if (j < file_diff->hunk_nr ||
1561 (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
1562 /* At least one hunk selected: apply */
1563 strbuf_reset(&s->buf);
1564 reassemble_patch(s, file_diff, 0, &s->buf);
1565
1566 discard_index(s->index);
1567 if (s->mode->apply_for_checkout)
1568 apply_for_checkout(s, &s->buf,
1569 s->mode->is_reverse);
1570 else {
1571 setup_child_process(s, &cp, "apply", NULL);
1572 strvec_pushv(&cp.args, s->mode->apply_args);
1573 if (pipe_command(&cp, s->buf.buf, s->buf.len,
1574 NULL, 0, NULL, 0))
1575 error(_("'git apply' failed"));
1576 }
1577 if (read_index_from(s->index, s->index_file, s->r->gitdir) >= 0 &&
1578 s->index == s->r->index) {
1579 repo_refresh_and_write_index(s->r, REFRESH_QUIET, 0,
1580 1, NULL, NULL, NULL);
1581 }
1582 }
1583
1584 }
1585
1586 static size_t dec_mod(size_t a, size_t m)
1587 {
1588 return a > 0 ? a - 1 : m - 1;
1589 }
1590
1591 static size_t inc_mod(size_t a, size_t m)
1592 {
1593 return a < m - 1 ? a + 1 : 0;
1594 }
1595
1596 static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)
1597 {
1598 for (size_t i = 0; i < file_diff->hunk_nr; i++) {
1599 if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
1600 *idx = i;
1601 return true;
1602 }
1603 }
1604 return false;
1605 }
1606
1607 static size_t patch_update_file(struct add_p_state *s,
1608 size_t idx,
1609 unsigned flags)
1610 {
1611 size_t hunk_index = 0;
1612 ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
1613 struct hunk *hunk;
1614 char ch;
1615 int colored = !!s->colored.len, use_pager = 0;
1616 enum prompt_mode_type prompt_mode_type;
1617 int all_decided = 0;
1618 struct file_diff *file_diff = s->file_diff + idx;
1619 size_t patch_update_resp = idx;
1620
1621 /* Empty added files have no hunks */
1622 if (!file_diff->hunk_nr && !file_diff->added)
1623 return patch_update_resp + 1;
1624
1625 strbuf_reset(&s->buf);
1626 render_diff_header(s, file_diff, colored, &s->buf);
1627 fputs(s->buf.buf, stdout);
1628 for (;;) {
1629 const char *hunk_use_decision = "";
1630 enum {
1631 ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
1632 ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
1633 ALLOW_GOTO_NEXT_HUNK = 1 << 2,
1634 ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
1635 ALLOW_SEARCH_AND_GOTO = 1 << 4,
1636 ALLOW_SPLIT = 1 << 5,
1637 ALLOW_EDIT = 1 << 6,
1638 ALLOW_GOTO_PREVIOUS_FILE = 1 << 7,
1639 ALLOW_GOTO_NEXT_FILE = 1 << 8
1640 } permitted = 0;
1641
1642 if (hunk_index >= file_diff->hunk_nr)
1643 hunk_index = 0;
1644 hunk = file_diff->hunk_nr
1645 ? file_diff->hunk + hunk_index
1646 : &file_diff->head;
1647 undecided_previous = -1;
1648 undecided_next = -1;
1649
1650 if (file_diff->hunk_nr) {
1651 for (i = dec_mod(hunk_index, file_diff->hunk_nr);
1652 i != hunk_index;
1653 i = dec_mod(i, file_diff->hunk_nr))
1654 if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
1655 undecided_previous = i;
1656 break;
1657 }
1658
1659 for (i = inc_mod(hunk_index, file_diff->hunk_nr);
1660 i != hunk_index;
1661 i = inc_mod(i, file_diff->hunk_nr))
1662 if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
1663 undecided_next = i;
1664 break;
1665 }
1666 }
1667
1668 /* Everything decided? */
1669 if (undecided_previous < 0 && undecided_next < 0 &&
1670 hunk->use != UNDECIDED_HUNK) {
1671 if (!s->cfg.auto_advance)
1672 all_decided = 1;
1673 else {
1674 patch_update_resp++;
1675 break;
1676 }
1677 }
1678 strbuf_reset(&s->buf);
1679 if (file_diff->hunk_nr) {
1680 if (rendered_hunk_index != hunk_index) {
1681 if (use_pager) {
1682 setup_pager(s->r);
1683 sigchain_push(SIGPIPE, SIG_IGN);
1684 }
1685 render_hunk(s, hunk, 0, colored, &s->buf);
1686 fputs(s->buf.buf, stdout);
1687 rendered_hunk_index = hunk_index;
1688 if (use_pager) {
1689 sigchain_pop(SIGPIPE);
1690 wait_for_pager();
1691 use_pager = 0;
1692 }
1693 }
1694
1695 strbuf_reset(&s->buf);
1696 if (undecided_previous >= 0) {
1697 permitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;
1698 strbuf_addstr(&s->buf, ",k");
1699 }
1700 if (file_diff->hunk_nr > 1) {
1701 permitted |= ALLOW_GOTO_PREVIOUS_HUNK;
1702 strbuf_addstr(&s->buf, ",K");
1703 }
1704 if (undecided_next >= 0) {
1705 permitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;
1706 strbuf_addstr(&s->buf, ",j");
1707 }
1708 if (file_diff->hunk_nr > 1) {
1709 permitted |= ALLOW_GOTO_NEXT_HUNK;
1710 strbuf_addstr(&s->buf, ",J");
1711 }
1712 if (file_diff->hunk_nr > 1) {
1713 permitted |= ALLOW_SEARCH_AND_GOTO;
1714 strbuf_addstr(&s->buf, ",g,/");
1715 }
1716 if (hunk->splittable_into > 1) {
1717 permitted |= ALLOW_SPLIT;
1718 strbuf_addstr(&s->buf, ",s");
1719 }
1720 if (!(flags & ADD_P_DISALLOW_EDIT) &&
1721 hunk_index + 1 > file_diff->mode_change &&
1722 !file_diff->deleted) {
1723 permitted |= ALLOW_EDIT;
1724 strbuf_addstr(&s->buf, ",e");
1725 }
1726 if (!s->cfg.auto_advance && s->file_diff_nr > 1) {
1727 permitted |= ALLOW_GOTO_NEXT_FILE;
1728 strbuf_addstr(&s->buf, ",>");
1729 }
1730 if (!s->cfg.auto_advance && s->file_diff_nr > 1) {
1731 permitted |= ALLOW_GOTO_PREVIOUS_FILE;
1732 strbuf_addstr(&s->buf, ",<");
1733 }
1734 strbuf_addstr(&s->buf, ",p,P");
1735 }
1736 if (file_diff->deleted)
1737 prompt_mode_type = PROMPT_DELETION;
1738 else if (file_diff->added)
1739 prompt_mode_type = PROMPT_ADDITION;
1740 else if (file_diff->mode_change && !hunk_index)
1741 prompt_mode_type = PROMPT_MODE_CHANGE;
1742 else
1743 prompt_mode_type = PROMPT_HUNK;
1744
1745 printf("%s(%"PRIuMAX"/%"PRIuMAX") ", s->cfg.prompt_color,
1746 (uintmax_t)hunk_index + 1,
1747 (uintmax_t)(file_diff->hunk_nr
1748 ? file_diff->hunk_nr
1749 : 1));
1750 if (hunk->use != UNDECIDED_HUNK) {
1751 if (hunk->use == USE_HUNK)
1752 hunk_use_decision = _(" (was: y)");
1753 else
1754 hunk_use_decision = _(" (was: n)");
1755 }
1756 printf(_(s->mode->prompt_mode[prompt_mode_type]),
1757 hunk_use_decision, s->buf.buf);
1758 if (*s->cfg.reset_color_interactive)
1759 fputs(s->cfg.reset_color_interactive, stdout);
1760 fflush(stdout);
1761 if (read_single_character(s) == EOF) {
1762 patch_update_resp = s->file_diff_nr;
1763 break;
1764 }
1765
1766 if (!s->answer.len)
1767 continue;
1768 ch = tolower(s->answer.buf[0]);
1769
1770 /* 'g' takes a hunk number and '/' takes a regexp */
1771 if (s->answer.len != 1 && (ch != 'g' && ch != '/')) {
1772 err(s, _("Only one letter is expected, got '%s'"), s->answer.buf);
1773 continue;
1774 }
1775 if (ch == 'y') {
1776 hunk->use = USE_HUNK;
1777 soft_increment:
1778 hunk_index = undecided_next < 0 ?
1779 file_diff->hunk_nr : undecided_next;
1780 } else if (ch == 'n') {
1781 hunk->use = SKIP_HUNK;
1782 goto soft_increment;
1783 } else if (ch == 'a') {
1784 if (file_diff->hunk_nr) {
1785 for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
1786 hunk = file_diff->hunk + hunk_index;
1787 if (hunk->use == UNDECIDED_HUNK)
1788 hunk->use = USE_HUNK;
1789 }
1790 if (!get_first_undecided(file_diff, &hunk_index))
1791 hunk_index = 0;
1792 } else if (hunk->use == UNDECIDED_HUNK) {
1793 hunk->use = USE_HUNK;
1794 }
1795 } else if (ch == 'd') {
1796 if (file_diff->hunk_nr) {
1797 for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
1798 hunk = file_diff->hunk + hunk_index;
1799 if (hunk->use == UNDECIDED_HUNK)
1800 hunk->use = SKIP_HUNK;
1801 }
1802 if (!get_first_undecided(file_diff, &hunk_index))
1803 hunk_index = 0;
1804 } else if (hunk->use == UNDECIDED_HUNK) {
1805 hunk->use = SKIP_HUNK;
1806 }
1807 } else if (ch == 'q') {
1808 patch_update_resp = s->file_diff_nr;
1809 break;
1810 } else if (!s->cfg.auto_advance && s->answer.buf[0] == '>') {
1811 if (permitted & ALLOW_GOTO_NEXT_FILE) {
1812 if (patch_update_resp == s->file_diff_nr - 1)
1813 patch_update_resp = 0;
1814 else
1815 patch_update_resp++;
1816 break;
1817 } else {
1818 err(s, _("No next file"));
1819 continue;
1820 }
1821 } else if (!s->cfg.auto_advance && s->answer.buf[0] == '<') {
1822 if (permitted & ALLOW_GOTO_PREVIOUS_FILE) {
1823 if (patch_update_resp == 0)
1824 patch_update_resp = s->file_diff_nr - 1;
1825 else
1826 patch_update_resp--;
1827 break;
1828 } else {
1829 err(s, _("No previous file"));
1830 continue;
1831 }
1832 } else if (s->answer.buf[0] == 'K') {
1833 if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
1834 hunk_index = dec_mod(hunk_index,
1835 file_diff->hunk_nr);
1836 else
1837 err(s, _("No other hunk"));
1838 } else if (s->answer.buf[0] == 'J') {
1839 if (permitted & ALLOW_GOTO_NEXT_HUNK)
1840 hunk_index++;
1841 else
1842 err(s, _("No other hunk"));
1843 } else if (s->answer.buf[0] == 'k') {
1844 if (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)
1845 hunk_index = undecided_previous;
1846 else
1847 err(s, _("No other undecided hunk"));
1848 } else if (s->answer.buf[0] == 'j') {
1849 if (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)
1850 hunk_index = undecided_next;
1851 else
1852 err(s, _("No other undecided hunk"));
1853 } else if (s->answer.buf[0] == 'g') {
1854 char *pend;
1855 unsigned long response;
1856
1857 if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
1858 err(s, _("No other hunks to goto"));
1859 continue;
1860 }
1861 strbuf_remove(&s->answer, 0, 1);
1862 strbuf_trim(&s->answer);
1863 i = hunk_index - DISPLAY_HUNKS_LINES / 2;
1864 if (i < (int)file_diff->mode_change)
1865 i = file_diff->mode_change;
1866 while (s->answer.len == 0) {
1867 i = display_hunks(s, file_diff, i);
1868 printf("%s", i < file_diff->hunk_nr ?
1869 _("go to which hunk (<ret> to see "
1870 "more)? ") : _("go to which hunk? "));
1871 fflush(stdout);
1872 if (strbuf_getline(&s->answer,
1873 stdin) == EOF)
1874 break;
1875 strbuf_trim_trailing_newline(&s->answer);
1876 }
1877
1878 strbuf_trim(&s->answer);
1879 response = strtoul(s->answer.buf, &pend, 10);
1880 if (*pend || pend == s->answer.buf)
1881 err(s, _("Invalid number: '%s'"),
1882 s->answer.buf);
1883 else if (0 < response && response <= file_diff->hunk_nr)
1884 hunk_index = response - 1;
1885 else
1886 err(s, Q_("Sorry, only %d hunk available.",
1887 "Sorry, only %d hunks available.",
1888 file_diff->hunk_nr),
1889 (int)file_diff->hunk_nr);
1890 } else if (s->answer.buf[0] == '/') {
1891 regex_t regex;
1892 int ret;
1893
1894 if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
1895 err(s, _("No other hunks to search"));
1896 continue;
1897 }
1898 strbuf_remove(&s->answer, 0, 1);
1899 strbuf_trim_trailing_newline(&s->answer);
1900 if (s->answer.len == 0) {
1901 printf("%s", _("search for regex? "));
1902 fflush(stdout);
1903 if (strbuf_getline(&s->answer,
1904 stdin) == EOF)
1905 break;
1906 strbuf_trim_trailing_newline(&s->answer);
1907 if (s->answer.len == 0)
1908 continue;
1909 }
1910 ret = regcomp(&regex, s->answer.buf,
1911 REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
1912 if (ret) {
1913 char errbuf[1024];
1914
1915 regerror(ret, &regex, errbuf, sizeof(errbuf));
1916 err(s, _("Malformed search regexp %s: %s"),
1917 s->answer.buf, errbuf);
1918 continue;
1919 }
1920 i = hunk_index;
1921 for (;;) {
1922 /* render the hunk into a scratch buffer */
1923 render_hunk(s, file_diff->hunk + i, 0, 0,
1924 &s->buf);
1925 if (regexec(&regex, s->buf.buf, 0, NULL, 0)
1926 != REG_NOMATCH)
1927 break;
1928 i++;
1929 if (i == file_diff->hunk_nr)
1930 i = 0;
1931 if (i != hunk_index)
1932 continue;
1933 err(s, _("No hunk matches the given pattern"));
1934 break;
1935 }
1936 regfree(&regex);
1937 hunk_index = i;
1938 } else if (s->answer.buf[0] == 's') {
1939 size_t splittable_into = hunk->splittable_into;
1940 if (!(permitted & ALLOW_SPLIT)) {
1941 err(s, _("Sorry, cannot split this hunk"));
1942 } else if (!split_hunk(s, file_diff,
1943 hunk - file_diff->hunk)) {
1944 color_fprintf_ln(stdout, s->cfg.header_color,
1945 _("Split into %d hunks."),
1946 (int)splittable_into);
1947 rendered_hunk_index = -1;
1948 }
1949 } else if (s->answer.buf[0] == 'e') {
1950 if (!(permitted & ALLOW_EDIT))
1951 err(s, _("Sorry, cannot edit this hunk"));
1952 else if (edit_hunk_loop(s, file_diff, hunk) >= 0) {
1953 hunk->use = USE_HUNK;
1954 goto soft_increment;
1955 }
1956 } else if (ch == 'p') {
1957 rendered_hunk_index = -1;
1958 use_pager = (s->answer.buf[0] == 'P') ? 1 : 0;
1959 } else if (s->answer.buf[0] == '?') {
1960 const char *p = _(help_patch_remainder), *eol = p;
1961
1962 color_fprintf(stdout, s->cfg.help_color, "%s",
1963 _(s->mode->help_patch_text));
1964
1965 /*
1966 * Show only those lines of the remainder that are
1967 * actually applicable with the current hunk.
1968 */
1969 for (; *p; p = eol + (*eol == '\n')) {
1970 eol = strchrnul(p, '\n');
1971
1972 /*
1973 * `s->buf` still contains the part of the
1974 * commands shown in the prompt that are not
1975 * always available.
1976 */
1977 if (all_decided && !strncmp(p, "HUNKS SUMMARY", 13)) {
1978 int total = file_diff->hunk_nr, used = 0, skipped = 0;
1979
1980 for (i = 0; i < file_diff->hunk_nr; i++) {
1981 if (file_diff->hunk[i].use == USE_HUNK)
1982 used += 1;
1983 if (file_diff->hunk[i].use == SKIP_HUNK)
1984 skipped += 1;
1985 }
1986 color_fprintf_ln(stdout, s->cfg.help_color, _(p),
1987 total, used, skipped);
1988 }
1989 if (*p != '?' && !strchr(s->buf.buf, *p))
1990 continue;
1991
1992 color_fprintf_ln(stdout, s->cfg.help_color,
1993 "%.*s", (int)(eol - p), p);
1994 }
1995 } else {
1996 err(s, _("Unknown command '%s' (use '?' for help)"),
1997 s->answer.buf);
1998 }
1999 }
2000
2001 if (s->cfg.auto_advance)
2002 apply_patch(s, file_diff);
2003
2004 putchar('\n');
2005 return patch_update_resp;
2006 }
2007
2008 static int run_add_p_common(struct add_p_state *state,
2009 const struct pathspec *ps,
2010 unsigned flags)
2011 {
2012 size_t binary_count = 0;
2013 size_t i;
2014
2015 if (parse_diff(state, ps) < 0)
2016 return -1;
2017
2018 for (i = 0; i < state->file_diff_nr;) {
2019 if (state->file_diff[i].binary && !state->file_diff[i].hunk_nr) {
2020 binary_count++;
2021 i++;
2022 continue;
2023 }
2024 if ((i = patch_update_file(state, i, flags)) == state->file_diff_nr)
2025 break;
2026 }
2027
2028 if (!state->cfg.auto_advance)
2029 for (i = 0; i < state->file_diff_nr; i++)
2030 apply_patch(state, state->file_diff + i);
2031
2032 if (state->file_diff_nr == 0)
2033 err(state, _("No changes."));
2034 else if (binary_count == state->file_diff_nr)
2035 err(state, _("Only binary files changed."));
2036
2037 return 0;
2038 }
2039
2040 int run_add_p(struct repository *r, enum add_p_mode mode,
2041 struct interactive_options *opts, const char *revision,
2042 const struct pathspec *ps,
2043 unsigned flags)
2044 {
2045 struct add_p_state s = {
2046 .r = r,
2047 .index = r->index,
2048 .index_file = r->index_file,
2049 .answer = STRBUF_INIT,
2050 .buf = STRBUF_INIT,
2051 .plain = STRBUF_INIT,
2052 .colored = STRBUF_INIT,
2053 };
2054 int ret;
2055
2056 interactive_config_init(&s.cfg, r, opts);
2057
2058 if (mode == ADD_P_STASH)
2059 s.mode = &patch_mode_stash;
2060 else if (mode == ADD_P_RESET) {
2061 if (!revision || !strcmp(revision, "HEAD"))
2062 s.mode = &patch_mode_reset_head;
2063 else
2064 s.mode = &patch_mode_reset_nothead;
2065 } else if (mode == ADD_P_CHECKOUT) {
2066 if (!revision)
2067 s.mode = &patch_mode_checkout_index;
2068 else if (!strcmp(revision, "HEAD"))
2069 s.mode = &patch_mode_checkout_head;
2070 else
2071 s.mode = &patch_mode_checkout_nothead;
2072 } else if (mode == ADD_P_WORKTREE) {
2073 if (!revision)
2074 s.mode = &patch_mode_checkout_index;
2075 else if (!strcmp(revision, "HEAD"))
2076 s.mode = &patch_mode_worktree_head;
2077 else
2078 s.mode = &patch_mode_worktree_nothead;
2079 } else
2080 s.mode = &patch_mode_add;
2081 s.revision = revision;
2082
2083 discard_index(r->index);
2084 if (repo_read_index(r) < 0 ||
2085 (!s.mode->index_only &&
2086 repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
2087 NULL, NULL, NULL) < 0)) {
2088 ret = -1;
2089 goto out;
2090 }
2091
2092 ret = run_add_p_common(&s, ps, flags);
2093 if (ret < 0)
2094 goto out;
2095
2096 ret = 0;
2097
2098 out:
2099 add_p_state_clear(&s);
2100 return ret;
2101 }
2102
2103 int run_add_p_index(struct repository *r,
2104 struct index_state *index,
2105 const char *index_file,
2106 struct interactive_options *opts,
2107 const char *revision,
2108 const struct pathspec *ps,
2109 unsigned flags)
2110 {
2111 struct patch_mode mode = {
2112 .apply_args = { "--cached", NULL },
2113 .apply_check_args = { "--cached", NULL },
2114 .prompt_mode = {
2115 N_("Stage mode change [y,n,q,a,d%s,?]? "),
2116 N_("Stage deletion [y,n,q,a,d%s,?]? "),
2117 N_("Stage addition [y,n,q,a,d%s,?]? "),
2118 N_("Stage this hunk [y,n,q,a,d%s,?]? ")
2119 },
2120 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
2121 "will immediately be marked for staging."),
2122 .help_patch_text =
2123 N_("y - stage this hunk\n"
2124 "n - do not stage this hunk\n"
2125 "q - quit; do not stage this hunk or any of the remaining "
2126 "ones\n"
2127 "a - stage this hunk and all later hunks in the file\n"
2128 "d - do not stage this hunk or any of the later hunks in "
2129 "the file\n"),
2130 .index_only = 1,
2131 };
2132 struct add_p_state s = {
2133 .r = r,
2134 .index = index,
2135 .index_file = index_file,
2136 .answer = STRBUF_INIT,
2137 .buf = STRBUF_INIT,
2138 .plain = STRBUF_INIT,
2139 .colored = STRBUF_INIT,
2140 .mode = &mode,
2141 .revision = revision,
2142 };
2143 char parent_tree_oid[GIT_MAX_HEXSZ + 1];
2144 struct commit *commit;
2145 int ret;
2146
2147 interactive_config_init(&s.cfg, r, opts);
2148
2149 commit = lookup_commit_reference_by_name(revision);
2150 if (!commit) {
2151 err(&s, _("Revision does not refer to a commit"));
2152 ret = -1;
2153 goto out;
2154 }
2155
2156 if (commit->parents)
2157 oid_to_hex_r(parent_tree_oid, get_commit_tree_oid(commit->parents->item));
2158 else
2159 oid_to_hex_r(parent_tree_oid, r->hash_algo->empty_tree);
2160
2161 mode.diff_cmd[0] = "diff-tree";
2162 mode.diff_cmd[1] = "-r";
2163 mode.diff_cmd[2] = parent_tree_oid;
2164
2165 ret = run_add_p_common(&s, ps, flags);
2166 if (ret < 0)
2167 goto out;
2168
2169 ret = 0;
2170
2171 out:
2172 add_p_state_clear(&s);
2173 return ret;
2174 }