@matheusbsbs / matheus-bagliano / commits / d2a233cb8b
Fork 0

built-in add -p: prepare for patch modes other than "stage"

The Perl script backing `git add -p` is used not only for that command, but also for `git stash -p`, `git reset -p` and `git checkout -p`. In preparation for teaching the C version of `git add -p` to support also the latter commands, let's abstract away what is "stage" specific into a dedicated data structure describing the differences between the patch modes. Finally, please note that the Perl version tries to make sure that the diffs are only generated for the modified files. This is not actually necessary, as the calls to Git's diff machinery already perform that work, and perform it well. This makes it unnecessary to port the `FILTER` field of the `%patch_modes` struct, as well as the `get_diff_reference()` function. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Dec 21, 2019 at 21:57 UTC d2a233cb8b9e0f7210df68c35e751e00419a8488
4 files changed +85 -30
add-interactive.c
+1 -1
@@ -924,7 +924,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
924 parse_pathspec(&ps_selected,
925 PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
926 PATHSPEC_LITERAL_PATH, "", args.argv);
927 - res = run_add_p(s->r, &ps_selected);
927 + res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected);
928 argv_array_clear(&args);
929 clear_pathspec(&ps_selected);
930 }
add-interactive.h
+7 -1
@@ -22,6 +22,12 @@ void init_add_i_state(struct add_i_state *s, struct repository *r);
22 struct repository;
23 struct pathspec;
24 int run_add_i(struct repository *r, const struct pathspec *ps);
25 -int run_add_p(struct repository *r, const struct pathspec *ps);
25 +
26 +enum add_p_mode {
27 + ADD_P_ADD,
28 +};
29 +
30 +int run_add_p(struct repository *r, enum add_p_mode mode,
31 + const char *revision, const struct pathspec *ps);
32
33 #endif
add-patch.c
+69 -26
@@ -8,13 +8,41 @@
8 #include "diff.h"
9
10 enum prompt_mode_type {
11 - PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK
11 + PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,
12 + PROMPT_MODE_MAX, /* must be last */
13 };
14
14 -static const char *prompt_mode[] = {
15 - N_("Stage mode change [y,n,a,q,d%s,?]? "),
16 - N_("Stage deletion [y,n,a,q,d%s,?]? "),
17 - N_("Stage this hunk [y,n,a,q,d%s,?]? ")
15 +struct patch_mode {
16 + /*
17 + * The magic constant 4 is chosen such that all patch modes
18 + * provide enough space for three command-line arguments followed by a
19 + * trailing `NULL`.
20 + */
21 + const char *diff_cmd[4], *apply_args[4], *apply_check_args[4];
22 + unsigned is_reverse:1, apply_for_checkout:1;
23 + const char *prompt_mode[PROMPT_MODE_MAX];
24 + const char *edit_hunk_hint, *help_patch_text;
25 +};
26 +
27 +static struct patch_mode patch_mode_add = {
28 + .diff_cmd = { "diff-files", NULL },
29 + .apply_args = { "--cached", NULL },
30 + .apply_check_args = { "--cached", NULL },
31 + .prompt_mode = {
32 + N_("Stage mode change [y,n,q,a,d%s,?]? "),
33 + N_("Stage deletion [y,n,q,a,d%s,?]? "),
34 + N_("Stage this hunk [y,n,q,a,d%s,?]? ")
35 + },
36 + .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
37 + "will immediately be marked for staging."),
38 + .help_patch_text =
39 + N_("y - stage this hunk\n"
40 + "n - do not stage this hunk\n"
41 + "q - quit; do not stage this hunk or any of the remaining "
42 + "ones\n"
43 + "a - stage this hunk and all later hunks in the file\n"
44 + "d - do not stage this hunk or any of the later hunks in "
45 + "the file\n")
46 };
47
48 struct hunk_header {
@@ -47,6 +75,10 @@ struct add_p_state {
75 unsigned deleted:1, mode_change:1,binary:1;
76 } *file_diff;
77 size_t file_diff_nr;
78 +
79 + /* patch mode */
80 + struct patch_mode *mode;
81 + const char *revision;
82 };
83
84 static void err(struct add_p_state *s, const char *fmt, ...)
@@ -162,9 +194,18 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
194 struct hunk *hunk = NULL;
195 int res;
196
197 + argv_array_pushv(&args, s->mode->diff_cmd);
198 + if (s->revision) {
199 + struct object_id oid;
200 + argv_array_push(&args,
201 + /* could be on an unborn branch */
202 + !strcmp("HEAD", s->revision) &&
203 + get_oid("HEAD", &oid) ?
204 + empty_tree_oid_hex() : s->revision);
205 + }
206 + color_arg_index = args.argc;
207 /* Use `--no-color` explicitly, just in case `diff.color = always`. */
166 - argv_array_pushl(&args, "diff-files", "-p", "--no-color", "--", NULL);
167 - color_arg_index = args.argc - 2;
208 + argv_array_pushl(&args, "--no-color", "-p", "--", NULL);
209 for (i = 0; i < ps->nr; i++)
210 argv_array_push(&args, ps->items[i].original);
211
@@ -382,7 +423,10 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
423 - header->colored_extra_start;
424 }
425
385 - new_offset += delta;
426 + if (s->mode->is_reverse)
427 + old_offset -= delta;
428 + else
429 + new_offset += delta;
430
431 strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@",
432 old_offset, header->old_count,
@@ -805,11 +849,10 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
849 "(context).\n"
850 "To remove '%c' lines, delete them.\n"
851 "Lines starting with %c will be removed.\n"),
808 - '-', '+', comment_line_char);
809 - strbuf_commented_addf(&s->buf,
810 - _("If the patch applies cleanly, the edited hunk "
811 - "will immediately be\n"
812 - "marked for staging.\n"));
852 + s->mode->is_reverse ? '+' : '-',
853 + s->mode->is_reverse ? '-' : '+',
854 + comment_line_char);
855 + strbuf_commented_addf(&s->buf, "%s", _(s->mode->edit_hunk_hint));
856 /*
857 * TRANSLATORS: 'it' refers to the patch mentioned in the previous
858 * messages.
@@ -890,7 +933,8 @@ static int run_apply_check(struct add_p_state *s,
933 reassemble_patch(s, file_diff, 1, &s->buf);
934
935 setup_child_process(s, &cp,
893 - "apply", "--cached", "--check", NULL);
936 + "apply", "--check", NULL);
937 + argv_array_pushv(&cp.args, s->mode->apply_check_args);
938 if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
939 return error(_("'git apply --cached' failed"));
940
@@ -1005,13 +1049,6 @@ static size_t display_hunks(struct add_p_state *s,
1049 return end_index;
1050 }
1051
1008 -static const char help_patch_text[] =
1009 -N_("y - stage this hunk\n"
1010 - "n - do not stage this hunk\n"
1011 - "q - quit; do not stage this hunk or any of the remaining ones\n"
1012 - "a - stage this and all the remaining hunks\n"
1013 - "d - do not stage this hunk nor any of the remaining hunks\n");
1014 -
1052 static const char help_patch_remainder[] =
1053 N_("j - leave this hunk undecided, see next undecided hunk\n"
1054 "J - leave this hunk undecided, see next hunk\n"
@@ -1097,7 +1134,8 @@ static int patch_update_file(struct add_p_state *s,
1134 (uintmax_t)hunk_index + 1,
1135 (uintmax_t)file_diff->hunk_nr);
1136 color_fprintf(stdout, s->s.prompt_color,
1100 - _(prompt_mode[prompt_mode_type]), s->buf.buf);
1137 + _(s->mode->prompt_mode[prompt_mode_type]),
1138 + s->buf.buf);
1139 fflush(stdout);
1140 if (strbuf_getline(&s->answer, stdin) == EOF)
1141 break;
@@ -1254,7 +1292,7 @@ soft_increment:
1292 const char *p = _(help_patch_remainder), *eol = p;
1293
1294 color_fprintf(stdout, s->s.help_color, "%s",
1257 - _(help_patch_text));
1295 + _(s->mode->help_patch_text));
1296
1297 /*
1298 * Show only those lines of the remainder that are
@@ -1288,10 +1326,11 @@ soft_increment:
1326 reassemble_patch(s, file_diff, 0, &s->buf);
1327
1328 discard_index(s->s.r->index);
1291 - setup_child_process(s, &cp, "apply", "--cached", NULL);
1329 + setup_child_process(s, &cp, "apply", NULL);
1330 + argv_array_pushv(&cp.args, s->mode->apply_args);
1331 if (pipe_command(&cp, s->buf.buf, s->buf.len,
1332 NULL, 0, NULL, 0))
1294 - error(_("'git apply --cached' failed"));
1333 + error(_("'git apply' failed"));
1334 if (!repo_read_index(s->s.r))
1335 repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
1336 1, NULL, NULL, NULL);
@@ -1301,7 +1340,8 @@ soft_increment:
1340 return quit;
1341 }
1342
1304 -int run_add_p(struct repository *r, const struct pathspec *ps)
1343 +int run_add_p(struct repository *r, enum add_p_mode mode,
1344 + const char *revision, const struct pathspec *ps)
1345 {
1346 struct add_p_state s = {
1347 { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
@@ -1310,6 +1350,9 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
1350
1351 init_add_i_state(&s.s, r);
1352
1353 + s.mode = &patch_mode_add;
1354 + s.revision = revision;
1355 +
1356 if (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||
1357 repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
1358 NULL, NULL, NULL) < 0 ||
builtin/add.c
+8 -2
@@ -194,12 +194,18 @@ int run_add_interactive(const char *revision, const char *patch_mode,
194 &use_builtin_add_i);
195
196 if (use_builtin_add_i == 1) {
197 + enum add_p_mode mode;
198 +
199 if (!patch_mode)
200 return !!run_add_i(the_repository, pathspec);
199 - if (strcmp(patch_mode, "--patch"))
201 +
202 + if (!strcmp(patch_mode, "--patch"))
203 + mode = ADD_P_ADD;
204 + else
205 die("'%s' not yet supported in the built-in add -p",
206 patch_mode);
202 - return !!run_add_p(the_repository, pathspec);
207 +
208 + return !!run_add_p(the_repository, mode, revision, pathspec);
209 }
210
211 argv_array_push(&argv, "add--interactive");