]>
Commit | Line | Data |
---|---|---|
f6aa7ecc JS |
1 | #include "cache.h" |
2 | #include "add-interactive.h" | |
3 | #include "strbuf.h" | |
4 | #include "run-command.h" | |
5 | #include "argv-array.h" | |
6 | #include "pathspec.h" | |
e3bd11b4 | 7 | #include "color.h" |
25ea47af JS |
8 | #include "diff.h" |
9 | ||
0ecd9d27 JS |
10 | enum prompt_mode_type { |
11 | PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK | |
12 | }; | |
13 | ||
14 | static const char *prompt_mode[] = { | |
15 | N_("Stage mode change [y,n,a,d%s,?]? "), | |
16 | N_("Stage deletion [y,n,a,d%s,?]? "), | |
17 | N_("Stage this hunk [y,n,a,d%s,?]? ") | |
18 | }; | |
19 | ||
25ea47af JS |
20 | struct hunk_header { |
21 | unsigned long old_offset, old_count, new_offset, new_count; | |
22 | /* | |
23 | * Start/end offsets to the extra text after the second `@@` in the | |
24 | * hunk header, e.g. the function signature. This is expected to | |
25 | * include the newline. | |
26 | */ | |
27 | size_t extra_start, extra_end, colored_extra_start, colored_extra_end; | |
28 | }; | |
f6aa7ecc JS |
29 | |
30 | struct hunk { | |
e3bd11b4 | 31 | size_t start, end, colored_start, colored_end; |
f6aa7ecc | 32 | enum { UNDECIDED_HUNK = 0, SKIP_HUNK, USE_HUNK } use; |
25ea47af | 33 | struct hunk_header header; |
f6aa7ecc JS |
34 | }; |
35 | ||
36 | struct add_p_state { | |
25ea47af | 37 | struct add_i_state s; |
f6aa7ecc JS |
38 | struct strbuf answer, buf; |
39 | ||
40 | /* parsed diff */ | |
e3bd11b4 | 41 | struct strbuf plain, colored; |
80399aec JS |
42 | struct file_diff { |
43 | struct hunk head; | |
44 | struct hunk *hunk; | |
45 | size_t hunk_nr, hunk_alloc; | |
5906d5de | 46 | unsigned deleted:1, mode_change:1; |
80399aec JS |
47 | } *file_diff; |
48 | size_t file_diff_nr; | |
f6aa7ecc JS |
49 | }; |
50 | ||
7584dd3c JS |
51 | static void err(struct add_p_state *s, const char *fmt, ...) |
52 | { | |
53 | va_list args; | |
54 | ||
55 | va_start(args, fmt); | |
56 | fputs(s->s.error_color, stderr); | |
57 | vfprintf(stderr, fmt, args); | |
58 | fputs(s->s.reset_color, stderr); | |
59 | fputc('\n', stderr); | |
60 | va_end(args); | |
61 | } | |
62 | ||
f6aa7ecc JS |
63 | static void setup_child_process(struct add_p_state *s, |
64 | struct child_process *cp, ...) | |
65 | { | |
66 | va_list ap; | |
67 | const char *arg; | |
68 | ||
69 | va_start(ap, cp); | |
70 | while ((arg = va_arg(ap, const char *))) | |
71 | argv_array_push(&cp->args, arg); | |
72 | va_end(ap); | |
73 | ||
74 | cp->git_cmd = 1; | |
75 | argv_array_pushf(&cp->env_array, | |
25ea47af JS |
76 | INDEX_ENVIRONMENT "=%s", s->s.r->index_file); |
77 | } | |
78 | ||
79 | static int parse_range(const char **p, | |
80 | unsigned long *offset, unsigned long *count) | |
81 | { | |
82 | char *pend; | |
83 | ||
84 | *offset = strtoul(*p, &pend, 10); | |
85 | if (pend == *p) | |
86 | return -1; | |
87 | if (*pend != ',') { | |
88 | *count = 1; | |
89 | *p = pend; | |
90 | return 0; | |
91 | } | |
92 | *count = strtoul(pend + 1, (char **)p, 10); | |
93 | return *p == pend + 1 ? -1 : 0; | |
94 | } | |
95 | ||
96 | static int parse_hunk_header(struct add_p_state *s, struct hunk *hunk) | |
97 | { | |
98 | struct hunk_header *header = &hunk->header; | |
99 | const char *line = s->plain.buf + hunk->start, *p = line; | |
100 | char *eol = memchr(p, '\n', s->plain.len - hunk->start); | |
101 | ||
102 | if (!eol) | |
103 | eol = s->plain.buf + s->plain.len; | |
104 | ||
105 | if (!skip_prefix(p, "@@ -", &p) || | |
106 | parse_range(&p, &header->old_offset, &header->old_count) < 0 || | |
107 | !skip_prefix(p, " +", &p) || | |
108 | parse_range(&p, &header->new_offset, &header->new_count) < 0 || | |
109 | !skip_prefix(p, " @@", &p)) | |
110 | return error(_("could not parse hunk header '%.*s'"), | |
111 | (int)(eol - line), line); | |
112 | ||
113 | hunk->start = eol - s->plain.buf + (*eol == '\n'); | |
114 | header->extra_start = p - s->plain.buf; | |
115 | header->extra_end = hunk->start; | |
116 | ||
117 | if (!s->colored.len) { | |
118 | header->colored_extra_start = header->colored_extra_end = 0; | |
119 | return 0; | |
120 | } | |
121 | ||
122 | /* Now find the extra text in the colored diff */ | |
123 | line = s->colored.buf + hunk->colored_start; | |
124 | eol = memchr(line, '\n', s->colored.len - hunk->colored_start); | |
125 | if (!eol) | |
126 | eol = s->colored.buf + s->colored.len; | |
127 | p = memmem(line, eol - line, "@@ -", 4); | |
128 | if (!p) | |
129 | return error(_("could not parse colored hunk header '%.*s'"), | |
130 | (int)(eol - line), line); | |
131 | p = memmem(p + 4, eol - p - 4, " @@", 3); | |
132 | if (!p) | |
133 | return error(_("could not parse colored hunk header '%.*s'"), | |
134 | (int)(eol - line), line); | |
135 | hunk->colored_start = eol - s->colored.buf + (*eol == '\n'); | |
136 | header->colored_extra_start = p + 3 - s->colored.buf; | |
137 | header->colored_extra_end = hunk->colored_start; | |
138 | ||
139 | return 0; | |
f6aa7ecc JS |
140 | } |
141 | ||
5906d5de JS |
142 | static int is_octal(const char *p, size_t len) |
143 | { | |
144 | if (!len) | |
145 | return 0; | |
146 | ||
147 | while (len--) | |
148 | if (*p < '0' || *(p++) > '7') | |
149 | return 0; | |
150 | return 1; | |
151 | } | |
152 | ||
f6aa7ecc JS |
153 | static int parse_diff(struct add_p_state *s, const struct pathspec *ps) |
154 | { | |
e3bd11b4 JS |
155 | struct argv_array args = ARGV_ARRAY_INIT; |
156 | struct strbuf *plain = &s->plain, *colored = NULL; | |
f6aa7ecc | 157 | struct child_process cp = CHILD_PROCESS_INIT; |
e3bd11b4 | 158 | char *p, *pend, *colored_p = NULL, *colored_pend = NULL; |
80399aec JS |
159 | size_t file_diff_alloc = 0, i, color_arg_index; |
160 | struct file_diff *file_diff = NULL; | |
f6aa7ecc JS |
161 | struct hunk *hunk = NULL; |
162 | int res; | |
163 | ||
164 | /* Use `--no-color` explicitly, just in case `diff.color = always`. */ | |
e3bd11b4 JS |
165 | argv_array_pushl(&args, "diff-files", "-p", "--no-color", "--", NULL); |
166 | color_arg_index = args.argc - 2; | |
f6aa7ecc | 167 | for (i = 0; i < ps->nr; i++) |
e3bd11b4 | 168 | argv_array_push(&args, ps->items[i].original); |
f6aa7ecc | 169 | |
e3bd11b4 JS |
170 | setup_child_process(s, &cp, NULL); |
171 | cp.argv = args.argv; | |
f6aa7ecc | 172 | res = capture_command(&cp, plain, 0); |
e3bd11b4 JS |
173 | if (res) { |
174 | argv_array_clear(&args); | |
f6aa7ecc | 175 | return error(_("could not parse diff")); |
e3bd11b4 JS |
176 | } |
177 | if (!plain->len) { | |
178 | argv_array_clear(&args); | |
f6aa7ecc | 179 | return 0; |
e3bd11b4 | 180 | } |
f6aa7ecc JS |
181 | strbuf_complete_line(plain); |
182 | ||
e3bd11b4 JS |
183 | if (want_color_fd(1, -1)) { |
184 | struct child_process colored_cp = CHILD_PROCESS_INIT; | |
185 | ||
186 | setup_child_process(s, &colored_cp, NULL); | |
187 | xsnprintf((char *)args.argv[color_arg_index], 8, "--color"); | |
188 | colored_cp.argv = args.argv; | |
189 | colored = &s->colored; | |
190 | res = capture_command(&colored_cp, colored, 0); | |
191 | argv_array_clear(&args); | |
192 | if (res) | |
193 | return error(_("could not parse colored diff")); | |
194 | strbuf_complete_line(colored); | |
195 | colored_p = colored->buf; | |
196 | colored_pend = colored_p + colored->len; | |
197 | } | |
198 | argv_array_clear(&args); | |
199 | ||
80399aec | 200 | /* parse files and hunks */ |
f6aa7ecc JS |
201 | p = plain->buf; |
202 | pend = p + plain->len; | |
203 | while (p != pend) { | |
204 | char *eol = memchr(p, '\n', pend - p); | |
5906d5de | 205 | const char *deleted = NULL, *mode_change = NULL; |
47dc4fd5 | 206 | |
f6aa7ecc JS |
207 | if (!eol) |
208 | eol = pend; | |
209 | ||
210 | if (starts_with(p, "diff ")) { | |
80399aec JS |
211 | s->file_diff_nr++; |
212 | ALLOC_GROW(s->file_diff, s->file_diff_nr, | |
213 | file_diff_alloc); | |
214 | file_diff = s->file_diff + s->file_diff_nr - 1; | |
215 | memset(file_diff, 0, sizeof(*file_diff)); | |
216 | hunk = &file_diff->head; | |
217 | hunk->start = p - plain->buf; | |
218 | if (colored_p) | |
219 | hunk->colored_start = colored_p - colored->buf; | |
f6aa7ecc JS |
220 | } else if (p == plain->buf) |
221 | BUG("diff starts with unexpected line:\n" | |
222 | "%.*s\n", (int)(eol - p), p); | |
47dc4fd5 JS |
223 | else if (file_diff->deleted) |
224 | ; /* keep the rest of the file in a single "hunk" */ | |
225 | else if (starts_with(p, "@@ ") || | |
226 | (hunk == &file_diff->head && | |
227 | skip_prefix(p, "deleted file", &deleted))) { | |
80399aec JS |
228 | file_diff->hunk_nr++; |
229 | ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr, | |
230 | file_diff->hunk_alloc); | |
231 | hunk = file_diff->hunk + file_diff->hunk_nr - 1; | |
f6aa7ecc JS |
232 | memset(hunk, 0, sizeof(*hunk)); |
233 | ||
234 | hunk->start = p - plain->buf; | |
e3bd11b4 JS |
235 | if (colored) |
236 | hunk->colored_start = colored_p - colored->buf; | |
25ea47af | 237 | |
47dc4fd5 JS |
238 | if (deleted) |
239 | file_diff->deleted = 1; | |
240 | else if (parse_hunk_header(s, hunk) < 0) | |
25ea47af | 241 | return -1; |
5906d5de JS |
242 | } else if (hunk == &file_diff->head && |
243 | skip_prefix(p, "old mode ", &mode_change) && | |
244 | is_octal(mode_change, eol - mode_change)) { | |
245 | if (file_diff->mode_change) | |
246 | BUG("double mode change?\n\n%.*s", | |
247 | (int)(eol - plain->buf), plain->buf); | |
248 | if (file_diff->hunk_nr++) | |
249 | BUG("mode change in the middle?\n\n%.*s", | |
250 | (int)(eol - plain->buf), plain->buf); | |
251 | ||
252 | /* | |
253 | * Do *not* change `hunk`: the mode change pseudo-hunk | |
254 | * is _part of_ the header "hunk". | |
255 | */ | |
256 | file_diff->mode_change = 1; | |
257 | ALLOC_GROW(file_diff->hunk, file_diff->hunk_nr, | |
258 | file_diff->hunk_alloc); | |
259 | memset(file_diff->hunk, 0, sizeof(struct hunk)); | |
260 | file_diff->hunk->start = p - plain->buf; | |
261 | if (colored_p) | |
262 | file_diff->hunk->colored_start = | |
263 | colored_p - colored->buf; | |
264 | } else if (hunk == &file_diff->head && | |
265 | skip_prefix(p, "new mode ", &mode_change) && | |
266 | is_octal(mode_change, eol - mode_change)) { | |
267 | ||
268 | /* | |
269 | * Extend the "mode change" pseudo-hunk to include also | |
270 | * the "new mode" line. | |
271 | */ | |
272 | if (!file_diff->mode_change) | |
273 | BUG("'new mode' without 'old mode'?\n\n%.*s", | |
274 | (int)(eol - plain->buf), plain->buf); | |
275 | if (file_diff->hunk_nr != 1) | |
276 | BUG("mode change in the middle?\n\n%.*s", | |
277 | (int)(eol - plain->buf), plain->buf); | |
278 | if (p - plain->buf != file_diff->hunk->end) | |
279 | BUG("'new mode' does not immediately follow " | |
280 | "'old mode'?\n\n%.*s", | |
281 | (int)(eol - plain->buf), plain->buf); | |
f6aa7ecc JS |
282 | } |
283 | ||
5906d5de JS |
284 | if (file_diff->deleted && file_diff->mode_change) |
285 | BUG("diff contains delete *and* a mode change?!?\n%.*s", | |
286 | (int)(eol - (plain->buf + file_diff->head.start)), | |
287 | plain->buf + file_diff->head.start); | |
288 | ||
f6aa7ecc JS |
289 | p = eol == pend ? pend : eol + 1; |
290 | hunk->end = p - plain->buf; | |
e3bd11b4 JS |
291 | |
292 | if (colored) { | |
293 | char *colored_eol = memchr(colored_p, '\n', | |
294 | colored_pend - colored_p); | |
295 | if (colored_eol) | |
296 | colored_p = colored_eol + 1; | |
297 | else | |
298 | colored_p = colored_pend; | |
299 | ||
300 | hunk->colored_end = colored_p - colored->buf; | |
301 | } | |
5906d5de JS |
302 | |
303 | if (mode_change) { | |
304 | if (file_diff->hunk_nr != 1) | |
305 | BUG("mode change in hunk #%d???", | |
306 | (int)file_diff->hunk_nr); | |
307 | /* Adjust the end of the "mode change" pseudo-hunk */ | |
308 | file_diff->hunk->end = hunk->end; | |
309 | if (colored) | |
310 | file_diff->hunk->colored_end = hunk->colored_end; | |
311 | } | |
f6aa7ecc JS |
312 | } |
313 | ||
314 | return 0; | |
315 | } | |
316 | ||
317 | static void render_hunk(struct add_p_state *s, struct hunk *hunk, | |
25ea47af | 318 | ssize_t delta, int colored, struct strbuf *out) |
f6aa7ecc | 319 | { |
25ea47af JS |
320 | struct hunk_header *header = &hunk->header; |
321 | ||
322 | if (hunk->header.old_offset != 0 || hunk->header.new_offset != 0) { | |
323 | /* | |
324 | * Generate the hunk header dynamically, except for special | |
325 | * hunks (such as the diff header). | |
326 | */ | |
327 | const char *p; | |
328 | size_t len; | |
329 | unsigned long old_offset = header->old_offset; | |
330 | unsigned long new_offset = header->new_offset; | |
331 | ||
332 | if (!colored) { | |
333 | p = s->plain.buf + header->extra_start; | |
334 | len = header->extra_end - header->extra_start; | |
335 | } else { | |
336 | strbuf_addstr(out, s->s.fraginfo_color); | |
337 | p = s->colored.buf + header->colored_extra_start; | |
338 | len = header->colored_extra_end | |
339 | - header->colored_extra_start; | |
340 | } | |
341 | ||
342 | new_offset += delta; | |
343 | ||
344 | strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@", | |
345 | old_offset, header->old_count, | |
346 | new_offset, header->new_count); | |
347 | if (len) | |
348 | strbuf_add(out, p, len); | |
349 | else if (colored) | |
350 | strbuf_addf(out, "%s\n", GIT_COLOR_RESET); | |
351 | else | |
352 | strbuf_addch(out, '\n'); | |
353 | } | |
354 | ||
e3bd11b4 JS |
355 | if (colored) |
356 | strbuf_add(out, s->colored.buf + hunk->colored_start, | |
357 | hunk->colored_end - hunk->colored_start); | |
358 | else | |
359 | strbuf_add(out, s->plain.buf + hunk->start, | |
360 | hunk->end - hunk->start); | |
f6aa7ecc JS |
361 | } |
362 | ||
5906d5de JS |
363 | static void render_diff_header(struct add_p_state *s, |
364 | struct file_diff *file_diff, int colored, | |
365 | struct strbuf *out) | |
366 | { | |
367 | /* | |
368 | * If there was a mode change, the first hunk is a pseudo hunk that | |
369 | * corresponds to the mode line in the header. If the user did not want | |
370 | * to stage that "hunk", we actually have to cut it out from the header. | |
371 | */ | |
372 | int skip_mode_change = | |
373 | file_diff->mode_change && file_diff->hunk->use != USE_HUNK; | |
374 | struct hunk *head = &file_diff->head, *first = file_diff->hunk; | |
375 | ||
376 | if (!skip_mode_change) { | |
377 | render_hunk(s, head, 0, colored, out); | |
378 | return; | |
379 | } | |
380 | ||
381 | if (colored) { | |
382 | const char *p = s->colored.buf; | |
383 | ||
384 | strbuf_add(out, p + head->colored_start, | |
385 | first->colored_start - head->colored_start); | |
386 | strbuf_add(out, p + first->colored_end, | |
387 | head->colored_end - first->colored_end); | |
388 | } else { | |
389 | const char *p = s->plain.buf; | |
390 | ||
391 | strbuf_add(out, p + head->start, first->start - head->start); | |
392 | strbuf_add(out, p + first->end, head->end - first->end); | |
393 | } | |
394 | } | |
395 | ||
80399aec JS |
396 | static void reassemble_patch(struct add_p_state *s, |
397 | struct file_diff *file_diff, struct strbuf *out) | |
f6aa7ecc JS |
398 | { |
399 | struct hunk *hunk; | |
400 | size_t i; | |
25ea47af | 401 | ssize_t delta = 0; |
f6aa7ecc | 402 | |
5906d5de | 403 | render_diff_header(s, file_diff, 0, out); |
f6aa7ecc | 404 | |
5906d5de | 405 | for (i = file_diff->mode_change; i < file_diff->hunk_nr; i++) { |
80399aec | 406 | hunk = file_diff->hunk + i; |
25ea47af JS |
407 | if (hunk->use != USE_HUNK) |
408 | delta += hunk->header.old_count | |
409 | - hunk->header.new_count; | |
410 | else | |
411 | render_hunk(s, hunk, delta, 0, out); | |
f6aa7ecc JS |
412 | } |
413 | } | |
414 | ||
415 | static const char help_patch_text[] = | |
416 | N_("y - stage this hunk\n" | |
417 | "n - do not stage this hunk\n" | |
418 | "a - stage this and all the remaining hunks\n" | |
419 | "d - do not stage this hunk nor any of the remaining hunks\n" | |
420 | "j - leave this hunk undecided, see next undecided hunk\n" | |
421 | "J - leave this hunk undecided, see next hunk\n" | |
422 | "k - leave this hunk undecided, see previous undecided hunk\n" | |
423 | "K - leave this hunk undecided, see previous hunk\n" | |
424 | "? - print help\n"); | |
425 | ||
80399aec JS |
426 | static int patch_update_file(struct add_p_state *s, |
427 | struct file_diff *file_diff) | |
f6aa7ecc JS |
428 | { |
429 | size_t hunk_index = 0; | |
430 | ssize_t i, undecided_previous, undecided_next; | |
431 | struct hunk *hunk; | |
432 | char ch; | |
433 | struct child_process cp = CHILD_PROCESS_INIT; | |
e3bd11b4 | 434 | int colored = !!s->colored.len; |
0ecd9d27 | 435 | enum prompt_mode_type prompt_mode_type; |
f6aa7ecc | 436 | |
80399aec | 437 | if (!file_diff->hunk_nr) |
f6aa7ecc JS |
438 | return 0; |
439 | ||
440 | strbuf_reset(&s->buf); | |
5906d5de | 441 | render_diff_header(s, file_diff, colored, &s->buf); |
f6aa7ecc JS |
442 | fputs(s->buf.buf, stdout); |
443 | for (;;) { | |
80399aec | 444 | if (hunk_index >= file_diff->hunk_nr) |
f6aa7ecc | 445 | hunk_index = 0; |
80399aec | 446 | hunk = file_diff->hunk + hunk_index; |
f6aa7ecc JS |
447 | |
448 | undecided_previous = -1; | |
449 | for (i = hunk_index - 1; i >= 0; i--) | |
80399aec | 450 | if (file_diff->hunk[i].use == UNDECIDED_HUNK) { |
f6aa7ecc JS |
451 | undecided_previous = i; |
452 | break; | |
453 | } | |
454 | ||
455 | undecided_next = -1; | |
80399aec JS |
456 | for (i = hunk_index + 1; i < file_diff->hunk_nr; i++) |
457 | if (file_diff->hunk[i].use == UNDECIDED_HUNK) { | |
f6aa7ecc JS |
458 | undecided_next = i; |
459 | break; | |
460 | } | |
461 | ||
462 | /* Everything decided? */ | |
463 | if (undecided_previous < 0 && undecided_next < 0 && | |
464 | hunk->use != UNDECIDED_HUNK) | |
465 | break; | |
466 | ||
467 | strbuf_reset(&s->buf); | |
25ea47af | 468 | render_hunk(s, hunk, 0, colored, &s->buf); |
f6aa7ecc JS |
469 | fputs(s->buf.buf, stdout); |
470 | ||
471 | strbuf_reset(&s->buf); | |
472 | if (undecided_previous >= 0) | |
473 | strbuf_addstr(&s->buf, ",k"); | |
474 | if (hunk_index) | |
475 | strbuf_addstr(&s->buf, ",K"); | |
476 | if (undecided_next >= 0) | |
477 | strbuf_addstr(&s->buf, ",j"); | |
80399aec | 478 | if (hunk_index + 1 < file_diff->hunk_nr) |
f6aa7ecc | 479 | strbuf_addstr(&s->buf, ",J"); |
0ecd9d27 JS |
480 | |
481 | if (file_diff->deleted) | |
482 | prompt_mode_type = PROMPT_DELETION; | |
483 | else if (file_diff->mode_change && !hunk_index) | |
484 | prompt_mode_type = PROMPT_MODE_CHANGE; | |
485 | else | |
486 | prompt_mode_type = PROMPT_HUNK; | |
487 | ||
12c24cf8 JS |
488 | color_fprintf(stdout, s->s.prompt_color, |
489 | "(%"PRIuMAX"/%"PRIuMAX") ", | |
80399aec JS |
490 | (uintmax_t)hunk_index + 1, |
491 | (uintmax_t)file_diff->hunk_nr); | |
12c24cf8 | 492 | color_fprintf(stdout, s->s.prompt_color, |
0ecd9d27 | 493 | _(prompt_mode[prompt_mode_type]), s->buf.buf); |
f6aa7ecc JS |
494 | fflush(stdout); |
495 | if (strbuf_getline(&s->answer, stdin) == EOF) | |
496 | break; | |
497 | strbuf_trim_trailing_newline(&s->answer); | |
498 | ||
499 | if (!s->answer.len) | |
500 | continue; | |
501 | ch = tolower(s->answer.buf[0]); | |
502 | if (ch == 'y') { | |
503 | hunk->use = USE_HUNK; | |
504 | soft_increment: | |
505 | hunk_index = undecided_next < 0 ? | |
80399aec | 506 | file_diff->hunk_nr : undecided_next; |
f6aa7ecc JS |
507 | } else if (ch == 'n') { |
508 | hunk->use = SKIP_HUNK; | |
509 | goto soft_increment; | |
510 | } else if (ch == 'a') { | |
80399aec JS |
511 | for (; hunk_index < file_diff->hunk_nr; hunk_index++) { |
512 | hunk = file_diff->hunk + hunk_index; | |
f6aa7ecc JS |
513 | if (hunk->use == UNDECIDED_HUNK) |
514 | hunk->use = USE_HUNK; | |
515 | } | |
516 | } else if (ch == 'd') { | |
80399aec JS |
517 | for (; hunk_index < file_diff->hunk_nr; hunk_index++) { |
518 | hunk = file_diff->hunk + hunk_index; | |
f6aa7ecc JS |
519 | if (hunk->use == UNDECIDED_HUNK) |
520 | hunk->use = SKIP_HUNK; | |
521 | } | |
7584dd3c JS |
522 | } else if (s->answer.buf[0] == 'K') { |
523 | if (hunk_index) | |
524 | hunk_index--; | |
525 | else | |
526 | err(s, _("No previous hunk")); | |
527 | } else if (s->answer.buf[0] == 'J') { | |
80399aec | 528 | if (hunk_index + 1 < file_diff->hunk_nr) |
7584dd3c JS |
529 | hunk_index++; |
530 | else | |
531 | err(s, _("No next hunk")); | |
532 | } else if (s->answer.buf[0] == 'k') { | |
533 | if (undecided_previous >= 0) | |
534 | hunk_index = undecided_previous; | |
535 | else | |
536 | err(s, _("No previous hunk")); | |
537 | } else if (s->answer.buf[0] == 'j') { | |
538 | if (undecided_next >= 0) | |
539 | hunk_index = undecided_next; | |
540 | else | |
541 | err(s, _("No next hunk")); | |
542 | } else | |
12c24cf8 JS |
543 | color_fprintf(stdout, s->s.help_color, |
544 | _(help_patch_text)); | |
f6aa7ecc JS |
545 | } |
546 | ||
547 | /* Any hunk to be used? */ | |
80399aec JS |
548 | for (i = 0; i < file_diff->hunk_nr; i++) |
549 | if (file_diff->hunk[i].use == USE_HUNK) | |
f6aa7ecc JS |
550 | break; |
551 | ||
80399aec | 552 | if (i < file_diff->hunk_nr) { |
f6aa7ecc JS |
553 | /* At least one hunk selected: apply */ |
554 | strbuf_reset(&s->buf); | |
80399aec | 555 | reassemble_patch(s, file_diff, &s->buf); |
f6aa7ecc | 556 | |
25ea47af | 557 | discard_index(s->s.r->index); |
f6aa7ecc JS |
558 | setup_child_process(s, &cp, "apply", "--cached", NULL); |
559 | if (pipe_command(&cp, s->buf.buf, s->buf.len, | |
560 | NULL, 0, NULL, 0)) | |
561 | error(_("'git apply --cached' failed")); | |
25ea47af JS |
562 | if (!repo_read_index(s->s.r)) |
563 | repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0, | |
f6aa7ecc JS |
564 | 1, NULL, NULL, NULL); |
565 | } | |
566 | ||
567 | putchar('\n'); | |
568 | return 0; | |
569 | } | |
570 | ||
571 | int run_add_p(struct repository *r, const struct pathspec *ps) | |
572 | { | |
25ea47af JS |
573 | struct add_p_state s = { |
574 | { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT | |
575 | }; | |
80399aec | 576 | size_t i; |
25ea47af JS |
577 | |
578 | init_add_i_state(&s.s, r); | |
f6aa7ecc JS |
579 | |
580 | if (discard_index(r->index) < 0 || repo_read_index(r) < 0 || | |
581 | repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1, | |
582 | NULL, NULL, NULL) < 0 || | |
583 | parse_diff(&s, ps) < 0) { | |
584 | strbuf_release(&s.plain); | |
e3bd11b4 | 585 | strbuf_release(&s.colored); |
f6aa7ecc JS |
586 | return -1; |
587 | } | |
588 | ||
80399aec JS |
589 | for (i = 0; i < s.file_diff_nr; i++) |
590 | if (patch_update_file(&s, s.file_diff + i)) | |
591 | break; | |
f6aa7ecc JS |
592 | |
593 | strbuf_release(&s.answer); | |
594 | strbuf_release(&s.buf); | |
595 | strbuf_release(&s.plain); | |
e3bd11b4 | 596 | strbuf_release(&s.colored); |
f6aa7ecc JS |
597 | return 0; |
598 | } |