Skip to content

Commit 52628f9

Browse files
dschogitster
authored andcommitted
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>
1 parent 6610e46 commit 52628f9

File tree

3 files changed

+137
-7
lines changed

3 files changed

+137
-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_ADD,
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: 133 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,71 @@ static struct patch_mode patch_mode_reset_nothead = {
111111
"the file\n"),
112112
};
113113

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+
114179
struct hunk_header {
115180
unsigned long old_offset, old_count, new_offset, new_count;
116181
/*
@@ -1067,6 +1132,57 @@ static int edit_hunk_loop(struct add_p_state *s,
10671132
}
10681133
}
10691134

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+
10701186
#define SUMMARY_HEADER_WIDTH 20
10711187
#define SUMMARY_LINE_WIDTH 80
10721188
static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
@@ -1392,11 +1508,16 @@ static int patch_update_file(struct add_p_state *s,
13921508
reassemble_patch(s, file_diff, 0, &s->buf);
13931509

13941510
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+
}
14001521
if (!repo_read_index(s->s.r))
14011522
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
14021523
1, NULL, NULL, NULL);
@@ -1423,6 +1544,13 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
14231544
s.mode = &patch_mode_reset_head;
14241545
else
14251546
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;
14261554
} else
14271555
s.mode = &patch_mode_add;
14281556
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)