in add-patch.c [1365:1689]
static int patch_update_file(struct add_p_state *s,
struct file_diff *file_diff)
{
size_t hunk_index = 0;
ssize_t i, undecided_previous, undecided_next;
struct hunk *hunk;
char ch;
struct child_process cp = CHILD_PROCESS_INIT;
int colored = !!s->colored.len, quit = 0;
enum prompt_mode_type prompt_mode_type;
enum {
ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
ALLOW_GOTO_NEXT_HUNK = 1 << 2,
ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,
ALLOW_SEARCH_AND_GOTO = 1 << 4,
ALLOW_SPLIT = 1 << 5,
ALLOW_EDIT = 1 << 6
} permitted = 0;
/* Empty added files have no hunks */
if (!file_diff->hunk_nr && !file_diff->added)
return 0;
strbuf_reset(&s->buf);
render_diff_header(s, file_diff, colored, &s->buf);
fputs(s->buf.buf, stdout);
for (;;) {
if (hunk_index >= file_diff->hunk_nr)
hunk_index = 0;
hunk = file_diff->hunk_nr
? file_diff->hunk + hunk_index
: &file_diff->head;
undecided_previous = -1;
undecided_next = -1;
if (file_diff->hunk_nr) {
for (i = hunk_index - 1; i >= 0; i--)
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
undecided_previous = i;
break;
}
for (i = hunk_index + 1; i < file_diff->hunk_nr; i++)
if (file_diff->hunk[i].use == UNDECIDED_HUNK) {
undecided_next = i;
break;
}
}
/* Everything decided? */
if (undecided_previous < 0 && undecided_next < 0 &&
hunk->use != UNDECIDED_HUNK)
break;
strbuf_reset(&s->buf);
if (file_diff->hunk_nr) {
render_hunk(s, hunk, 0, colored, &s->buf);
fputs(s->buf.buf, stdout);
strbuf_reset(&s->buf);
if (undecided_previous >= 0) {
permitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;
strbuf_addstr(&s->buf, ",k");
}
if (hunk_index) {
permitted |= ALLOW_GOTO_PREVIOUS_HUNK;
strbuf_addstr(&s->buf, ",K");
}
if (undecided_next >= 0) {
permitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;
strbuf_addstr(&s->buf, ",j");
}
if (hunk_index + 1 < file_diff->hunk_nr) {
permitted |= ALLOW_GOTO_NEXT_HUNK;
strbuf_addstr(&s->buf, ",J");
}
if (file_diff->hunk_nr > 1) {
permitted |= ALLOW_SEARCH_AND_GOTO;
strbuf_addstr(&s->buf, ",g,/");
}
if (hunk->splittable_into > 1) {
permitted |= ALLOW_SPLIT;
strbuf_addstr(&s->buf, ",s");
}
if (hunk_index + 1 > file_diff->mode_change &&
!file_diff->deleted) {
permitted |= ALLOW_EDIT;
strbuf_addstr(&s->buf, ",e");
}
}
if (file_diff->deleted)
prompt_mode_type = PROMPT_DELETION;
else if (file_diff->added)
prompt_mode_type = PROMPT_ADDITION;
else if (file_diff->mode_change && !hunk_index)
prompt_mode_type = PROMPT_MODE_CHANGE;
else
prompt_mode_type = PROMPT_HUNK;
printf("%s(%"PRIuMAX"/%"PRIuMAX") ", s->s.prompt_color,
(uintmax_t)hunk_index + 1,
(uintmax_t)(file_diff->hunk_nr
? file_diff->hunk_nr
: 1));
printf(_(s->mode->prompt_mode[prompt_mode_type]),
s->buf.buf);
if (*s->s.reset_color)
fputs(s->s.reset_color, stdout);
fflush(stdout);
if (read_single_character(s) == EOF)
break;
if (!s->answer.len)
continue;
ch = tolower(s->answer.buf[0]);
if (ch == 'y') {
hunk->use = USE_HUNK;
soft_increment:
hunk_index = undecided_next < 0 ?
file_diff->hunk_nr : undecided_next;
} else if (ch == 'n') {
hunk->use = SKIP_HUNK;
goto soft_increment;
} else if (ch == 'a') {
if (file_diff->hunk_nr) {
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
hunk = file_diff->hunk + hunk_index;
if (hunk->use == UNDECIDED_HUNK)
hunk->use = USE_HUNK;
}
} else if (hunk->use == UNDECIDED_HUNK) {
hunk->use = USE_HUNK;
}
} else if (ch == 'd' || ch == 'q') {
if (file_diff->hunk_nr) {
for (; hunk_index < file_diff->hunk_nr; hunk_index++) {
hunk = file_diff->hunk + hunk_index;
if (hunk->use == UNDECIDED_HUNK)
hunk->use = SKIP_HUNK;
}
} else if (hunk->use == UNDECIDED_HUNK) {
hunk->use = SKIP_HUNK;
}
if (ch == 'q') {
quit = 1;
break;
}
} else if (s->answer.buf[0] == 'K') {
if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)
hunk_index--;
else
err(s, _("No previous hunk"));
} else if (s->answer.buf[0] == 'J') {
if (permitted & ALLOW_GOTO_NEXT_HUNK)
hunk_index++;
else
err(s, _("No next hunk"));
} else if (s->answer.buf[0] == 'k') {
if (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)
hunk_index = undecided_previous;
else
err(s, _("No previous hunk"));
} else if (s->answer.buf[0] == 'j') {
if (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)
hunk_index = undecided_next;
else
err(s, _("No next hunk"));
} else if (s->answer.buf[0] == 'g') {
char *pend;
unsigned long response;
if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
err(s, _("No other hunks to goto"));
continue;
}
strbuf_remove(&s->answer, 0, 1);
strbuf_trim(&s->answer);
i = hunk_index - DISPLAY_HUNKS_LINES / 2;
if (i < file_diff->mode_change)
i = file_diff->mode_change;
while (s->answer.len == 0) {
i = display_hunks(s, file_diff, i);
printf("%s", i < file_diff->hunk_nr ?
_("go to which hunk (<ret> to see "
"more)? ") : _("go to which hunk? "));
fflush(stdout);
if (strbuf_getline(&s->answer,
stdin) == EOF)
break;
strbuf_trim_trailing_newline(&s->answer);
}
strbuf_trim(&s->answer);
response = strtoul(s->answer.buf, &pend, 10);
if (*pend || pend == s->answer.buf)
err(s, _("Invalid number: '%s'"),
s->answer.buf);
else if (0 < response && response <= file_diff->hunk_nr)
hunk_index = response - 1;
else
err(s, Q_("Sorry, only %d hunk available.",
"Sorry, only %d hunks available.",
file_diff->hunk_nr),
(int)file_diff->hunk_nr);
} else if (s->answer.buf[0] == '/') {
regex_t regex;
int ret;
if (!(permitted & ALLOW_SEARCH_AND_GOTO)) {
err(s, _("No other hunks to search"));
continue;
}
strbuf_remove(&s->answer, 0, 1);
strbuf_trim_trailing_newline(&s->answer);
if (s->answer.len == 0) {
printf("%s", _("search for regex? "));
fflush(stdout);
if (strbuf_getline(&s->answer,
stdin) == EOF)
break;
strbuf_trim_trailing_newline(&s->answer);
if (s->answer.len == 0)
continue;
}
ret = regcomp(®ex, s->answer.buf,
REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
if (ret) {
char errbuf[1024];
regerror(ret, ®ex, errbuf, sizeof(errbuf));
err(s, _("Malformed search regexp %s: %s"),
s->answer.buf, errbuf);
continue;
}
i = hunk_index;
for (;;) {
/* render the hunk into a scratch buffer */
render_hunk(s, file_diff->hunk + i, 0, 0,
&s->buf);
if (regexec(®ex, s->buf.buf, 0, NULL, 0)
!= REG_NOMATCH)
break;
i++;
if (i == file_diff->hunk_nr)
i = 0;
if (i != hunk_index)
continue;
err(s, _("No hunk matches the given pattern"));
break;
}
hunk_index = i;
} else if (s->answer.buf[0] == 's') {
size_t splittable_into = hunk->splittable_into;
if (!(permitted & ALLOW_SPLIT))
err(s, _("Sorry, cannot split this hunk"));
else if (!split_hunk(s, file_diff,
hunk - file_diff->hunk))
color_fprintf_ln(stdout, s->s.header_color,
_("Split into %d hunks."),
(int)splittable_into);
} else if (s->answer.buf[0] == 'e') {
if (!(permitted & ALLOW_EDIT))
err(s, _("Sorry, cannot edit this hunk"));
else if (edit_hunk_loop(s, file_diff, hunk) >= 0) {
hunk->use = USE_HUNK;
goto soft_increment;
}
} else {
const char *p = _(help_patch_remainder), *eol = p;
color_fprintf(stdout, s->s.help_color, "%s",
_(s->mode->help_patch_text));
/*
* Show only those lines of the remainder that are
* actually applicable with the current hunk.
*/
for (; *p; p = eol + (*eol == '\n')) {
eol = strchrnul(p, '\n');
/*
* `s->buf` still contains the part of the
* commands shown in the prompt that are not
* always available.
*/
if (*p != '?' && !strchr(s->buf.buf, *p))
continue;
color_fprintf_ln(stdout, s->s.help_color,
"%.*s", (int)(eol - p), p);
}
}
}
/* Any hunk to be used? */
for (i = 0; i < file_diff->hunk_nr; i++)
if (file_diff->hunk[i].use == USE_HUNK)
break;
if (i < file_diff->hunk_nr ||
(!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {
/* At least one hunk selected: apply */
strbuf_reset(&s->buf);
reassemble_patch(s, file_diff, 0, &s->buf);
discard_index(s->s.r->index);
if (s->mode->apply_for_checkout)
apply_for_checkout(s, &s->buf,
s->mode->is_reverse);
else {
setup_child_process(s, &cp, "apply", NULL);
strvec_pushv(&cp.args, s->mode->apply_args);
if (pipe_command(&cp, s->buf.buf, s->buf.len,
NULL, 0, NULL, 0))
error(_("'git apply' failed"));
}
if (repo_read_index(s->s.r) >= 0)
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
1, NULL, NULL, NULL);
}
putchar('\n');
return quit;
}