built-in add -p: implement the "checkout" patch modes
This patch teaches the built-in `git add -p` machinery all the tricks it needs to know in order to act as the work horse for `git checkout -p`. Apart from the minor changes (slightly reworded messages, different `diff` and `apply --check` invocations), it requires a new function to actually apply the changes, as `git checkout -p` is a bit special in that respect: when the desired changes do not apply to the index, but apply to the work tree, Git does not fail straight away, but asks the user whether to apply the changes to the worktree at least. 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
52628f94fc35f57f0b3c54e4f849e490bfa44449
3 files changed
+137
-7
add-interactive.h
+1
@@ -27,6 +27,7 @@ enum add_p_mode {
27
ADD_P_ADD,
28
ADD_P_STASH,
29
ADD_P_RESET,
30
+ ADD_P_CHECKOUT,
31
};
32
33
int run_add_p(struct repository *r, enum add_p_mode mode,
add-patch.c
+133
-5
@@ -111,6 +111,71 @@ static struct patch_mode patch_mode_reset_nothead = {
111
"the file\n"),
112
};
113
114
+static struct patch_mode patch_mode_checkout_index = {
115
+ .diff_cmd = { "diff-files", NULL },
116
+ .apply_args = { "-R", NULL },
117
+ .apply_check_args = { "-R", NULL },
118
+ .is_reverse = 1,
119
+ .prompt_mode = {
120
+ N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
121
+ N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
122
+ N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
123
+ },
124
+ .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
125
+ "will immediately be marked for discarding."),
126
+ .help_patch_text =
127
+ N_("y - discard this hunk from worktree\n"
128
+ "n - do not discard this hunk from worktree\n"
129
+ "q - quit; do not discard this hunk or any of the remaining "
130
+ "ones\n"
131
+ "a - discard this hunk and all later hunks in the file\n"
132
+ "d - do not discard this hunk or any of the later hunks in "
133
+ "the file\n"),
134
+};
135
+
136
+static struct patch_mode patch_mode_checkout_head = {
137
+ .diff_cmd = { "diff-index", NULL },
138
+ .apply_for_checkout = 1,
139
+ .apply_check_args = { "-R", NULL },
140
+ .is_reverse = 1,
141
+ .prompt_mode = {
142
+ N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
143
+ N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
144
+ N_("Discard this hunk from index and worktree [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 index and worktree\n"
150
+ "n - do not discard this hunk from index and 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_nothead = {
159
+ .diff_cmd = { "diff-index", "-R", NULL },
160
+ .apply_for_checkout = 1,
161
+ .apply_check_args = { NULL },
162
+ .prompt_mode = {
163
+ N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
164
+ N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
165
+ N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
166
+ },
167
+ .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
168
+ "will immediately be marked for applying."),
169
+ .help_patch_text =
170
+ N_("y - apply this hunk to index and worktree\n"
171
+ "n - do not apply this hunk to index and worktree\n"
172
+ "q - quit; do not apply this hunk or any of the remaining "
173
+ "ones\n"
174
+ "a - apply this hunk and all later hunks in the file\n"
175
+ "d - do not apply this hunk or any of the later hunks in "
176
+ "the file\n"),
177
+};
178
+
179
struct hunk_header {
180
unsigned long old_offset, old_count, new_offset, new_count;
181
/*
@@ -1067,6 +1132,57 @@ static int edit_hunk_loop(struct add_p_state *s,
1132
}
1133
}
1134
1135
+static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
1136
+ int is_reverse)
1137
+{
1138
+ const char *reverse = is_reverse ? "-R" : NULL;
1139
+ struct child_process check_index = CHILD_PROCESS_INIT;
1140
+ struct child_process check_worktree = CHILD_PROCESS_INIT;
1141
+ struct child_process apply_index = CHILD_PROCESS_INIT;
1142
+ struct child_process apply_worktree = CHILD_PROCESS_INIT;
1143
+ int applies_index, applies_worktree;
1144
+
1145
+ setup_child_process(s, &check_index,
1146
+ "apply", "--cached", "--check", reverse, NULL);
1147
+ applies_index = !pipe_command(&check_index, diff->buf, diff->len,
1148
+ NULL, 0, NULL, 0);
1149
+
1150
+ setup_child_process(s, &check_worktree,
1151
+ "apply", "--check", reverse, NULL);
1152
+ applies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,
1153
+ NULL, 0, NULL, 0);
1154
+
1155
+ if (applies_worktree && applies_index) {
1156
+ setup_child_process(s, &apply_index,
1157
+ "apply", "--cached", reverse, NULL);
1158
+ pipe_command(&apply_index, diff->buf, diff->len,
1159
+ NULL, 0, NULL, 0);
1160
+
1161
+ setup_child_process(s, &apply_worktree,
1162
+ "apply", reverse, NULL);
1163
+ pipe_command(&apply_worktree, diff->buf, diff->len,
1164
+ NULL, 0, NULL, 0);
1165
+
1166
+ return 1;
1167
+ }
1168
+
1169
+ if (!applies_index) {
1170
+ err(s, _("The selected hunks do not apply to the index!"));
1171
+ if (prompt_yesno(s, _("Apply them to the worktree "
1172
+ "anyway? ")) > 0) {
1173
+ setup_child_process(s, &apply_worktree,
1174
+ "apply", reverse, NULL);
1175
+ return pipe_command(&apply_worktree, diff->buf,
1176
+ diff->len, NULL, 0, NULL, 0);
1177
+ }
1178
+ err(s, _("Nothing was applied.\n"));
1179
+ } else
1180
+ /* As a last resort, show the diff to the user */
1181
+ fwrite(diff->buf, diff->len, 1, stderr);
1182
+
1183
+ return 0;
1184
+}
1185
+
1186
#define SUMMARY_HEADER_WIDTH 20
1187
#define SUMMARY_LINE_WIDTH 80
1188
static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
@@ -1392,11 +1508,16 @@ soft_increment:
1508
reassemble_patch(s, file_diff, 0, &s->buf);
1509
1510
discard_index(s->s.r->index);
1395
- setup_child_process(s, &cp, "apply", NULL);
1396
- argv_array_pushv(&cp.args, s->mode->apply_args);
1397
- if (pipe_command(&cp, s->buf.buf, s->buf.len,
1398
- NULL, 0, NULL, 0))
1399
- error(_("'git apply' failed"));
1511
+ if (s->mode->apply_for_checkout)
1512
+ apply_for_checkout(s, &s->buf,
1513
+ s->mode->is_reverse);
1514
+ else {
1515
+ setup_child_process(s, &cp, "apply", NULL);
1516
+ argv_array_pushv(&cp.args, s->mode->apply_args);
1517
+ if (pipe_command(&cp, s->buf.buf, s->buf.len,
1518
+ NULL, 0, NULL, 0))
1519
+ error(_("'git apply' failed"));
1520
+ }
1521
if (!repo_read_index(s->s.r))
1522
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
1523
1, NULL, NULL, NULL);
@@ -1423,6 +1544,13 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
1544
s.mode = &patch_mode_reset_head;
1545
else
1546
s.mode = &patch_mode_reset_nothead;
1547
+ } else if (mode == ADD_P_CHECKOUT) {
1548
+ if (!revision)
1549
+ s.mode = &patch_mode_checkout_index;
1550
+ else if (!strcmp(revision, "HEAD"))
1551
+ s.mode = &patch_mode_checkout_head;
1552
+ else
1553
+ s.mode = &patch_mode_checkout_nothead;
1554
} else
1555
s.mode = &patch_mode_add;
1556
s.revision = revision;
builtin/add.c
+3
-2
@@ -206,9 +206,10 @@ int run_add_interactive(const char *revision, const char *patch_mode,
206
mode = ADD_P_STASH;
207
else if (!strcmp(patch_mode, "--patch=reset"))
208
mode = ADD_P_RESET;
209
+ else if (!strcmp(patch_mode, "--patch=checkout"))
210
+ mode = ADD_P_CHECKOUT;
211
else
210
- die("'%s' not yet supported in the built-in add -p",
211
- patch_mode);
212
+ die("'%s' not supported", patch_mode);
213
214
return !!run_add_p(the_repository, mode, revision, pathspec);
215
}