Skip to content

Commit 70886a0

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 f46bcf1 commit 70886a0

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
@@ -36,6 +36,7 @@ enum add_p_mode {
3636
ADD_P_STAGE,
3737
ADD_P_STASH,
3838
ADD_P_RESET,
39+
ADD_P_CHECKOUT,
3940
};
4041

4142
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
@@ -106,6 +106,72 @@ static struct patch_mode patch_mode_reset_nothead = {
106106
"the file\n"),
107107
};
108108

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

1076+
static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
1077+
int is_reverse)
1078+
{
1079+
const char *reverse = is_reverse ? "-R" : NULL;
1080+
struct child_process check_index = CHILD_PROCESS_INIT;
1081+
struct child_process check_worktree = CHILD_PROCESS_INIT;
1082+
struct child_process apply_index = CHILD_PROCESS_INIT;
1083+
struct child_process apply_worktree = CHILD_PROCESS_INIT;
1084+
int applies_index, applies_worktree;
1085+
1086+
setup_child_process(&check_index, s,
1087+
"apply", "--cached", "--check", reverse, NULL);
1088+
applies_index = !pipe_command(&check_index, diff->buf, diff->len,
1089+
NULL, 0, NULL, 0);
1090+
1091+
setup_child_process(&check_worktree, s,
1092+
"apply", "--check", reverse, NULL);
1093+
applies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,
1094+
NULL, 0, NULL, 0);
1095+
1096+
if (applies_worktree && applies_index) {
1097+
setup_child_process(&apply_index, s,
1098+
"apply", "--cached", reverse, NULL);
1099+
pipe_command(&apply_index, diff->buf, diff->len,
1100+
NULL, 0, NULL, 0);
1101+
1102+
setup_child_process(&apply_worktree, s,
1103+
"apply", reverse, NULL);
1104+
pipe_command(&apply_worktree, diff->buf, diff->len,
1105+
NULL, 0, NULL, 0);
1106+
1107+
return 1;
1108+
}
1109+
1110+
if (!applies_index) {
1111+
err(s, _("The selected hunks do not apply to the index!"));
1112+
if (prompt_yesno(s, _("Apply them to the worktree "
1113+
"anyway? ")) > 0) {
1114+
setup_child_process(&apply_worktree, s,
1115+
"apply", reverse, NULL);
1116+
return pipe_command(&apply_worktree, diff->buf,
1117+
diff->len, NULL, 0, NULL, 0);
1118+
}
1119+
err(s, _("Nothing was applied.\n"));
1120+
} else
1121+
/* As a last resort, show the diff to the user */
1122+
fwrite(diff->buf, diff->len, 1, stderr);
1123+
1124+
return 0;
1125+
}
1126+
10101127
#define SUMMARY_HEADER_WIDTH 20
10111128
#define SUMMARY_LINE_WIDTH 80
10121129
static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
@@ -1327,11 +1444,16 @@ static int patch_update_file(struct add_p_state *s,
13271444
strbuf_reset(&s->buf);
13281445
reassemble_patch(s, file_diff, 0, &s->buf);
13291446

1330-
setup_child_process(&cp, s, "apply", NULL);
1331-
argv_array_pushv(&cp.args, s->mode->apply);
1332-
if (pipe_command(&cp, s->buf.buf, s->buf.len,
1333-
NULL, 0, NULL, 0))
1334-
error(_("'git apply' failed"));
1447+
if (s->mode->apply_for_checkout)
1448+
apply_for_checkout(s, &s->buf,
1449+
s->mode->is_reverse);
1450+
else {
1451+
setup_child_process(&cp, s, "apply", NULL);
1452+
argv_array_pushv(&cp.args, s->mode->apply);
1453+
if (pipe_command(&cp, s->buf.buf, s->buf.len,
1454+
NULL, 0, NULL, 0))
1455+
error(_("'git apply' failed"));
1456+
}
13351457
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0);
13361458
}
13371459

@@ -1357,6 +1479,13 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
13571479
s.mode = &patch_mode_reset_head;
13581480
else
13591481
s.mode = &patch_mode_reset_nothead;
1482+
} else if (mode == ADD_P_CHECKOUT) {
1483+
if (!revision)
1484+
s.mode = &patch_mode_checkout_index;
1485+
else if (!strcmp(revision, "HEAD"))
1486+
s.mode = &patch_mode_checkout_head;
1487+
else
1488+
s.mode = &patch_mode_checkout_nothead;
13601489
} else
13611490
s.mode = &patch_mode_stage;
13621491
s.revision = revision;

builtin/add.c

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

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

0 commit comments

Comments
 (0)