Skip to content

Commit 3c55f10

Browse files
committed
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 <[email protected]>
1 parent 40c0030 commit 3c55f10

File tree

3 files changed

+138
-7
lines changed

3 files changed

+138
-7
lines changed

add-interactive.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ enum add_p_mode {
2727
ADD_P_STAGE,
2828
ADD_P_STASH,
2929
ADD_P_RESET,
30+
ADD_P_CHECKOUT,
3031
};
3132

3233
int run_add_p(struct repository *r, enum add_p_mode mode,

add-patch.c

Lines changed: 134 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,72 @@ static struct patch_mode patch_mode_reset_nothead = {
108108
"the file\n"),
109109
};
110110

111+
static struct patch_mode patch_mode_checkout_index = {
112+
.diff = { "diff-files", NULL },
113+
.apply = { "-R", NULL },
114+
.apply_check = { "-R", NULL },
115+
.is_reverse = 1,
116+
.prompt_mode = {
117+
N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
118+
N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
119+
N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
120+
},
121+
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
122+
"will immediately be marked for discarding."),
123+
.help_patch_text =
124+
N_("y - discard this hunk from worktree\n"
125+
"n - do not discard this hunk from worktree\n"
126+
"q - quit; do not discard this hunk or any of the remaining "
127+
"ones\n"
128+
"a - discard this hunk and all later hunks in the file\n"
129+
"d - do not discard this hunk or any of the later hunks in "
130+
"the file\n"),
131+
};
132+
133+
static struct patch_mode patch_mode_checkout_head = {
134+
.diff = { "diff-index", NULL },
135+
.apply_for_checkout = 1,
136+
.apply_check = { "-R", NULL },
137+
.is_reverse = 1,
138+
.prompt_mode = {
139+
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
140+
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
141+
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
142+
},
143+
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
144+
"will immediately be marked for discarding."),
145+
.help_patch_text =
146+
N_("y - discard this hunk from index and worktree\n"
147+
"n - do not discard this hunk from index and worktree\n"
148+
"q - quit; do not discard this hunk or any of the remaining "
149+
"ones\n"
150+
"a - discard this hunk and all later hunks in the file\n"
151+
"d - do not discard this hunk or any of the later hunks in "
152+
"the file\n"),
153+
};
154+
155+
static struct patch_mode patch_mode_checkout_nothead = {
156+
.diff = { "diff-index", "-R", NULL },
157+
.apply_for_checkout = 1,
158+
.apply_check = { NULL },
159+
.is_reverse = 0,
160+
.prompt_mode = {
161+
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
162+
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
163+
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
164+
},
165+
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
166+
"will immediately be marked for applying."),
167+
.help_patch_text =
168+
N_("y - apply this hunk to index and worktree\n"
169+
"n - do not apply this hunk to index and worktree\n"
170+
"q - quit; do not apply this hunk or any of the remaining "
171+
"ones\n"
172+
"a - apply this hunk and all later hunks in the file\n"
173+
"d - do not apply this hunk or any of the later hunks in "
174+
"the file\n"),
175+
};
176+
111177
struct hunk_header {
112178
unsigned long old_offset, old_count, new_offset, new_count;
113179
/*
@@ -1064,6 +1130,57 @@ static int edit_hunk_loop(struct add_p_state *s,
10641130
}
10651131
}
10661132

1133+
static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
1134+
int is_reverse)
1135+
{
1136+
const char *reverse = is_reverse ? "-R" : NULL;
1137+
struct child_process check_index = CHILD_PROCESS_INIT;
1138+
struct child_process check_worktree = CHILD_PROCESS_INIT;
1139+
struct child_process apply_index = CHILD_PROCESS_INIT;
1140+
struct child_process apply_worktree = CHILD_PROCESS_INIT;
1141+
int applies_index, applies_worktree;
1142+
1143+
setup_child_process(s, &check_index,
1144+
"apply", "--cached", "--check", reverse, NULL);
1145+
applies_index = !pipe_command(&check_index, diff->buf, diff->len,
1146+
NULL, 0, NULL, 0);
1147+
1148+
setup_child_process(s, &check_worktree,
1149+
"apply", "--check", reverse, NULL);
1150+
applies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,
1151+
NULL, 0, NULL, 0);
1152+
1153+
if (applies_worktree && applies_index) {
1154+
setup_child_process(s, &apply_index,
1155+
"apply", "--cached", reverse, NULL);
1156+
pipe_command(&apply_index, diff->buf, diff->len,
1157+
NULL, 0, NULL, 0);
1158+
1159+
setup_child_process(s, &apply_worktree,
1160+
"apply", reverse, NULL);
1161+
pipe_command(&apply_worktree, diff->buf, diff->len,
1162+
NULL, 0, NULL, 0);
1163+
1164+
return 1;
1165+
}
1166+
1167+
if (!applies_index) {
1168+
err(s, _("The selected hunks do not apply to the index!"));
1169+
if (prompt_yesno(s, _("Apply them to the worktree "
1170+
"anyway? ")) > 0) {
1171+
setup_child_process(s, &apply_worktree,
1172+
"apply", reverse, NULL);
1173+
return pipe_command(&apply_worktree, diff->buf,
1174+
diff->len, NULL, 0, NULL, 0);
1175+
}
1176+
err(s, _("Nothing was applied.\n"));
1177+
} else
1178+
/* As a last resort, show the diff to the user */
1179+
fwrite(diff->buf, diff->len, 1, stderr);
1180+
1181+
return 0;
1182+
}
1183+
10671184
#define SUMMARY_HEADER_WIDTH 20
10681185
#define SUMMARY_LINE_WIDTH 80
10691186
static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
@@ -1389,11 +1506,16 @@ static int patch_update_file(struct add_p_state *s,
13891506
reassemble_patch(s, file_diff, 0, &s->buf);
13901507

13911508
discard_index(s->s.r->index);
1392-
setup_child_process(s, &cp, "apply", NULL);
1393-
argv_array_pushv(&cp.args, s->mode->apply);
1394-
if (pipe_command(&cp, s->buf.buf, s->buf.len,
1395-
NULL, 0, NULL, 0))
1396-
error(_("'git apply' failed"));
1509+
if (s->mode->apply_for_checkout)
1510+
apply_for_checkout(s, &s->buf,
1511+
s->mode->is_reverse);
1512+
else {
1513+
setup_child_process(s, &cp, "apply", NULL);
1514+
argv_array_pushv(&cp.args, s->mode->apply);
1515+
if (pipe_command(&cp, s->buf.buf, s->buf.len,
1516+
NULL, 0, NULL, 0))
1517+
error(_("'git apply' failed"));
1518+
}
13971519
if (!repo_read_index(s->s.r))
13981520
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
13991521
1, NULL, NULL, NULL);
@@ -1420,6 +1542,13 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
14201542
s.mode = &patch_mode_reset_head;
14211543
else
14221544
s.mode = &patch_mode_reset_nothead;
1545+
} else if (mode == ADD_P_CHECKOUT) {
1546+
if (!revision)
1547+
s.mode = &patch_mode_checkout_index;
1548+
else if (!strcmp(revision, "HEAD"))
1549+
s.mode = &patch_mode_checkout_head;
1550+
else
1551+
s.mode = &patch_mode_checkout_nothead;
14231552
} else
14241553
s.mode = &patch_mode_stage;
14251554
s.revision = revision;

builtin/add.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,10 @@ int run_add_interactive(const char *revision, const char *patch_mode,
206206
mode = ADD_P_STASH;
207207
else if (!strcmp(patch_mode, "--patch=reset"))
208208
mode = ADD_P_RESET;
209+
else if (!strcmp(patch_mode, "--patch=checkout"))
210+
mode = ADD_P_CHECKOUT;
209211
else
210-
die("'%s' not yet supported in the built-in add -p",
211-
patch_mode);
212+
die("'%s' not supported", patch_mode);
212213

213214
return !!run_add_p(the_repository, mode, revision, pathspec);
214215
}

0 commit comments

Comments
 (0)