]>
git.ipfire.org Git - thirdparty/git.git/blob - add-patch.c
2 #include "add-interactive.h"
4 #include "run-command.h"
5 #include "argv-array.h"
11 unsigned long old_offset
, old_count
, new_offset
, new_count
;
13 * Start/end offsets to the extra text after the second `@@` in the
14 * hunk header, e.g. the function signature. This is expected to
15 * include the newline.
17 size_t extra_start
, extra_end
, colored_extra_start
, colored_extra_end
;
21 size_t start
, end
, colored_start
, colored_end
;
22 enum { UNDECIDED_HUNK
= 0, SKIP_HUNK
, USE_HUNK
} use
;
23 struct hunk_header header
;
28 struct strbuf answer
, buf
;
31 struct strbuf plain
, colored
;
34 size_t hunk_nr
, hunk_alloc
;
37 static void setup_child_process(struct add_p_state
*s
,
38 struct child_process
*cp
, ...)
44 while ((arg
= va_arg(ap
, const char *)))
45 argv_array_push(&cp
->args
, arg
);
49 argv_array_pushf(&cp
->env_array
,
50 INDEX_ENVIRONMENT
"=%s", s
->s
.r
->index_file
);
53 static int parse_range(const char **p
,
54 unsigned long *offset
, unsigned long *count
)
58 *offset
= strtoul(*p
, &pend
, 10);
66 *count
= strtoul(pend
+ 1, (char **)p
, 10);
67 return *p
== pend
+ 1 ? -1 : 0;
70 static int parse_hunk_header(struct add_p_state
*s
, struct hunk
*hunk
)
72 struct hunk_header
*header
= &hunk
->header
;
73 const char *line
= s
->plain
.buf
+ hunk
->start
, *p
= line
;
74 char *eol
= memchr(p
, '\n', s
->plain
.len
- hunk
->start
);
77 eol
= s
->plain
.buf
+ s
->plain
.len
;
79 if (!skip_prefix(p
, "@@ -", &p
) ||
80 parse_range(&p
, &header
->old_offset
, &header
->old_count
) < 0 ||
81 !skip_prefix(p
, " +", &p
) ||
82 parse_range(&p
, &header
->new_offset
, &header
->new_count
) < 0 ||
83 !skip_prefix(p
, " @@", &p
))
84 return error(_("could not parse hunk header '%.*s'"),
85 (int)(eol
- line
), line
);
87 hunk
->start
= eol
- s
->plain
.buf
+ (*eol
== '\n');
88 header
->extra_start
= p
- s
->plain
.buf
;
89 header
->extra_end
= hunk
->start
;
91 if (!s
->colored
.len
) {
92 header
->colored_extra_start
= header
->colored_extra_end
= 0;
96 /* Now find the extra text in the colored diff */
97 line
= s
->colored
.buf
+ hunk
->colored_start
;
98 eol
= memchr(line
, '\n', s
->colored
.len
- hunk
->colored_start
);
100 eol
= s
->colored
.buf
+ s
->colored
.len
;
101 p
= memmem(line
, eol
- line
, "@@ -", 4);
103 return error(_("could not parse colored hunk header '%.*s'"),
104 (int)(eol
- line
), line
);
105 p
= memmem(p
+ 4, eol
- p
- 4, " @@", 3);
107 return error(_("could not parse colored hunk header '%.*s'"),
108 (int)(eol
- line
), line
);
109 hunk
->colored_start
= eol
- s
->colored
.buf
+ (*eol
== '\n');
110 header
->colored_extra_start
= p
+ 3 - s
->colored
.buf
;
111 header
->colored_extra_end
= hunk
->colored_start
;
116 static int parse_diff(struct add_p_state
*s
, const struct pathspec
*ps
)
118 struct argv_array args
= ARGV_ARRAY_INIT
;
119 struct strbuf
*plain
= &s
->plain
, *colored
= NULL
;
120 struct child_process cp
= CHILD_PROCESS_INIT
;
121 char *p
, *pend
, *colored_p
= NULL
, *colored_pend
= NULL
;
122 size_t i
, color_arg_index
;
123 struct hunk
*hunk
= NULL
;
126 /* Use `--no-color` explicitly, just in case `diff.color = always`. */
127 argv_array_pushl(&args
, "diff-files", "-p", "--no-color", "--", NULL
);
128 color_arg_index
= args
.argc
- 2;
129 for (i
= 0; i
< ps
->nr
; i
++)
130 argv_array_push(&args
, ps
->items
[i
].original
);
132 setup_child_process(s
, &cp
, NULL
);
134 res
= capture_command(&cp
, plain
, 0);
136 argv_array_clear(&args
);
137 return error(_("could not parse diff"));
140 argv_array_clear(&args
);
143 strbuf_complete_line(plain
);
145 if (want_color_fd(1, -1)) {
146 struct child_process colored_cp
= CHILD_PROCESS_INIT
;
148 setup_child_process(s
, &colored_cp
, NULL
);
149 xsnprintf((char *)args
.argv
[color_arg_index
], 8, "--color");
150 colored_cp
.argv
= args
.argv
;
151 colored
= &s
->colored
;
152 res
= capture_command(&colored_cp
, colored
, 0);
153 argv_array_clear(&args
);
155 return error(_("could not parse colored diff"));
156 strbuf_complete_line(colored
);
157 colored_p
= colored
->buf
;
158 colored_pend
= colored_p
+ colored
->len
;
160 argv_array_clear(&args
);
164 pend
= p
+ plain
->len
;
166 char *eol
= memchr(p
, '\n', pend
- p
);
170 if (starts_with(p
, "diff ")) {
172 BUG("multi-file diff not yet handled");
174 } else if (p
== plain
->buf
)
175 BUG("diff starts with unexpected line:\n"
176 "%.*s\n", (int)(eol
- p
), p
);
177 else if (starts_with(p
, "@@ ")) {
179 ALLOC_GROW(s
->hunk
, s
->hunk_nr
,
181 hunk
= s
->hunk
+ s
->hunk_nr
- 1;
182 memset(hunk
, 0, sizeof(*hunk
));
184 hunk
->start
= p
- plain
->buf
;
186 hunk
->colored_start
= colored_p
- colored
->buf
;
188 if (parse_hunk_header(s
, hunk
) < 0)
192 p
= eol
== pend
? pend
: eol
+ 1;
193 hunk
->end
= p
- plain
->buf
;
196 char *colored_eol
= memchr(colored_p
, '\n',
197 colored_pend
- colored_p
);
199 colored_p
= colored_eol
+ 1;
201 colored_p
= colored_pend
;
203 hunk
->colored_end
= colored_p
- colored
->buf
;
210 static void render_hunk(struct add_p_state
*s
, struct hunk
*hunk
,
211 ssize_t delta
, int colored
, struct strbuf
*out
)
213 struct hunk_header
*header
= &hunk
->header
;
215 if (hunk
->header
.old_offset
!= 0 || hunk
->header
.new_offset
!= 0) {
217 * Generate the hunk header dynamically, except for special
218 * hunks (such as the diff header).
222 unsigned long old_offset
= header
->old_offset
;
223 unsigned long new_offset
= header
->new_offset
;
226 p
= s
->plain
.buf
+ header
->extra_start
;
227 len
= header
->extra_end
- header
->extra_start
;
229 strbuf_addstr(out
, s
->s
.fraginfo_color
);
230 p
= s
->colored
.buf
+ header
->colored_extra_start
;
231 len
= header
->colored_extra_end
232 - header
->colored_extra_start
;
237 strbuf_addf(out
, "@@ -%lu,%lu +%lu,%lu @@",
238 old_offset
, header
->old_count
,
239 new_offset
, header
->new_count
);
241 strbuf_add(out
, p
, len
);
243 strbuf_addf(out
, "%s\n", GIT_COLOR_RESET
);
245 strbuf_addch(out
, '\n');
249 strbuf_add(out
, s
->colored
.buf
+ hunk
->colored_start
,
250 hunk
->colored_end
- hunk
->colored_start
);
252 strbuf_add(out
, s
->plain
.buf
+ hunk
->start
,
253 hunk
->end
- hunk
->start
);
256 static void reassemble_patch(struct add_p_state
*s
, struct strbuf
*out
)
262 render_hunk(s
, &s
->head
, 0, 0, out
);
264 for (i
= 0; i
< s
->hunk_nr
; i
++) {
266 if (hunk
->use
!= USE_HUNK
)
267 delta
+= hunk
->header
.old_count
268 - hunk
->header
.new_count
;
270 render_hunk(s
, hunk
, delta
, 0, out
);
274 static const char help_patch_text
[] =
275 N_("y - stage this hunk\n"
276 "n - do not stage this hunk\n"
277 "a - stage this and all the remaining hunks\n"
278 "d - do not stage this hunk nor any of the remaining hunks\n"
279 "j - leave this hunk undecided, see next undecided hunk\n"
280 "J - leave this hunk undecided, see next hunk\n"
281 "k - leave this hunk undecided, see previous undecided hunk\n"
282 "K - leave this hunk undecided, see previous hunk\n"
285 static int patch_update_file(struct add_p_state
*s
)
287 size_t hunk_index
= 0;
288 ssize_t i
, undecided_previous
, undecided_next
;
291 struct child_process cp
= CHILD_PROCESS_INIT
;
292 int colored
= !!s
->colored
.len
;
297 strbuf_reset(&s
->buf
);
298 render_hunk(s
, &s
->head
, 0, colored
, &s
->buf
);
299 fputs(s
->buf
.buf
, stdout
);
301 if (hunk_index
>= s
->hunk_nr
)
303 hunk
= s
->hunk
+ hunk_index
;
305 undecided_previous
= -1;
306 for (i
= hunk_index
- 1; i
>= 0; i
--)
307 if (s
->hunk
[i
].use
== UNDECIDED_HUNK
) {
308 undecided_previous
= i
;
313 for (i
= hunk_index
+ 1; i
< s
->hunk_nr
; i
++)
314 if (s
->hunk
[i
].use
== UNDECIDED_HUNK
) {
319 /* Everything decided? */
320 if (undecided_previous
< 0 && undecided_next
< 0 &&
321 hunk
->use
!= UNDECIDED_HUNK
)
324 strbuf_reset(&s
->buf
);
325 render_hunk(s
, hunk
, 0, colored
, &s
->buf
);
326 fputs(s
->buf
.buf
, stdout
);
328 strbuf_reset(&s
->buf
);
329 if (undecided_previous
>= 0)
330 strbuf_addstr(&s
->buf
, ",k");
332 strbuf_addstr(&s
->buf
, ",K");
333 if (undecided_next
>= 0)
334 strbuf_addstr(&s
->buf
, ",j");
335 if (hunk_index
+ 1 < s
->hunk_nr
)
336 strbuf_addstr(&s
->buf
, ",J");
337 printf("(%"PRIuMAX
"/%"PRIuMAX
") ",
338 (uintmax_t)hunk_index
+ 1, (uintmax_t)s
->hunk_nr
);
339 printf(_("Stage this hunk [y,n,a,d%s,?]? "), s
->buf
.buf
);
341 if (strbuf_getline(&s
->answer
, stdin
) == EOF
)
343 strbuf_trim_trailing_newline(&s
->answer
);
347 ch
= tolower(s
->answer
.buf
[0]);
349 hunk
->use
= USE_HUNK
;
351 hunk_index
= undecided_next
< 0 ?
352 s
->hunk_nr
: undecided_next
;
353 } else if (ch
== 'n') {
354 hunk
->use
= SKIP_HUNK
;
356 } else if (ch
== 'a') {
357 for (; hunk_index
< s
->hunk_nr
; hunk_index
++) {
358 hunk
= s
->hunk
+ hunk_index
;
359 if (hunk
->use
== UNDECIDED_HUNK
)
360 hunk
->use
= USE_HUNK
;
362 } else if (ch
== 'd') {
363 for (; hunk_index
< s
->hunk_nr
; hunk_index
++) {
364 hunk
= s
->hunk
+ hunk_index
;
365 if (hunk
->use
== UNDECIDED_HUNK
)
366 hunk
->use
= SKIP_HUNK
;
368 } else if (hunk_index
&& s
->answer
.buf
[0] == 'K')
370 else if (hunk_index
+ 1 < s
->hunk_nr
&&
371 s
->answer
.buf
[0] == 'J')
373 else if (undecided_previous
>= 0 &&
374 s
->answer
.buf
[0] == 'k')
375 hunk_index
= undecided_previous
;
376 else if (undecided_next
>= 0 && s
->answer
.buf
[0] == 'j')
377 hunk_index
= undecided_next
;
379 puts(_(help_patch_text
));
382 /* Any hunk to be used? */
383 for (i
= 0; i
< s
->hunk_nr
; i
++)
384 if (s
->hunk
[i
].use
== USE_HUNK
)
387 if (i
< s
->hunk_nr
) {
388 /* At least one hunk selected: apply */
389 strbuf_reset(&s
->buf
);
390 reassemble_patch(s
, &s
->buf
);
392 discard_index(s
->s
.r
->index
);
393 setup_child_process(s
, &cp
, "apply", "--cached", NULL
);
394 if (pipe_command(&cp
, s
->buf
.buf
, s
->buf
.len
,
396 error(_("'git apply --cached' failed"));
397 if (!repo_read_index(s
->s
.r
))
398 repo_refresh_and_write_index(s
->s
.r
, REFRESH_QUIET
, 0,
399 1, NULL
, NULL
, NULL
);
406 int run_add_p(struct repository
*r
, const struct pathspec
*ps
)
408 struct add_p_state s
= {
409 { r
}, STRBUF_INIT
, STRBUF_INIT
, STRBUF_INIT
, STRBUF_INIT
412 init_add_i_state(&s
.s
, r
);
414 if (discard_index(r
->index
) < 0 || repo_read_index(r
) < 0 ||
415 repo_refresh_and_write_index(r
, REFRESH_QUIET
, 0, 1,
416 NULL
, NULL
, NULL
) < 0 ||
417 parse_diff(&s
, ps
) < 0) {
418 strbuf_release(&s
.plain
);
419 strbuf_release(&s
.colored
);
424 patch_update_file(&s
);
426 strbuf_release(&s
.answer
);
427 strbuf_release(&s
.buf
);
428 strbuf_release(&s
.plain
);
429 strbuf_release(&s
.colored
);