@ -8,13 +8,41 @@
#include "diff.h"
#include "diff.h"
enum prompt_mode_type {
enum prompt_mode_type {
PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK
PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,
PROMPT_MODE_MAX, /* must be last */
};
};
static const char *prompt_mode[] = {
struct patch_mode {
N_("Stage mode change [y,n,a,q,d%s,?]? "),
/*
N_("Stage deletion [y,n,a,q,d%s,?]? "),
* The magic constant 4 is chosen such that all patch modes
N_("Stage this hunk [y,n,a,q,d%s,?]? ")
* provide enough space for three command-line arguments followed by a
* trailing `NULL`.
*/
const char *diff_cmd[4], *apply_args[4], *apply_check_args[4];
unsigned is_reverse:1, apply_for_checkout:1;
const char *prompt_mode[PROMPT_MODE_MAX];
const char *edit_hunk_hint, *help_patch_text;
};
static struct patch_mode patch_mode_add = {
.diff_cmd = { "diff-files", NULL },
.apply_args = { "--cached", NULL },
.apply_check_args = { "--cached", NULL },
.prompt_mode = {
N_("Stage mode change [y,n,q,a,d%s,?]? "),
N_("Stage deletion [y,n,q,a,d%s,?]? "),
N_("Stage this hunk [y,n,q,a,d%s,?]? ")
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
"will immediately be marked for staging."),
.help_patch_text =
N_("y - stage this hunk\n"
"n - do not stage this hunk\n"
"q - quit; do not stage this hunk or any of the remaining "
"ones\n"
"a - stage this hunk and all later hunks in the file\n"
"d - do not stage this hunk or any of the later hunks in "
"the file\n")
};
};
struct hunk_header {
struct hunk_header {
@ -47,6 +75,10 @@ struct add_p_state {
unsigned deleted:1, mode_change:1,binary:1;
unsigned deleted:1, mode_change:1,binary:1;
} *file_diff;
} *file_diff;
size_t file_diff_nr;
size_t file_diff_nr;
/* patch mode */
struct patch_mode *mode;
const char *revision;
};
};
static void err(struct add_p_state *s, const char *fmt, ...)
static void err(struct add_p_state *s, const char *fmt, ...)
@ -162,9 +194,18 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
struct hunk *hunk = NULL;
struct hunk *hunk = NULL;
int res;
int res;
argv_array_pushv(&args, s->mode->diff_cmd);
if (s->revision) {
struct object_id oid;
argv_array_push(&args,
/* could be on an unborn branch */
!strcmp("HEAD", s->revision) &&
get_oid("HEAD", &oid) ?
empty_tree_oid_hex() : s->revision);
}
color_arg_index = args.argc;
/* Use `--no-color` explicitly, just in case `diff.color = always`. */
/* Use `--no-color` explicitly, just in case `diff.color = always`. */
argv_array_pushl(&args, "diff-files", "-p", "--no-color", "--", NULL);
argv_array_pushl(&args, "--no-color", "-p", "--", NULL);
color_arg_index = args.argc - 2;
for (i = 0; i < ps->nr; i++)
for (i = 0; i < ps->nr; i++)
argv_array_push(&args, ps->items[i].original);
argv_array_push(&args, ps->items[i].original);
@ -382,7 +423,10 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
- header->colored_extra_start;
- header->colored_extra_start;
}
}
new_offset += delta;
if (s->mode->is_reverse)
old_offset -= delta;
else
new_offset += delta;
strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@",
strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@",
old_offset, header->old_count,
old_offset, header->old_count,
@ -805,11 +849,10 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
"(context).\n"
"(context).\n"
"To remove '%c' lines, delete them.\n"
"To remove '%c' lines, delete them.\n"
"Lines starting with %c will be removed.\n"),
"Lines starting with %c will be removed.\n"),
'-', '+', comment_line_char);
s->mode->is_reverse ? '+' : '-',
strbuf_commented_addf(&s->buf,
s->mode->is_reverse ? '-' : '+',
_("If the patch applies cleanly, the edited hunk "
comment_line_char);
"will immediately be\n"
strbuf_commented_addf(&s->buf, "%s", _(s->mode->edit_hunk_hint));
"marked for staging.\n"));
/*
/*
* TRANSLATORS: 'it' refers to the patch mentioned in the previous
* TRANSLATORS: 'it' refers to the patch mentioned in the previous
* messages.
* messages.
@ -890,7 +933,8 @@ static int run_apply_check(struct add_p_state *s,
reassemble_patch(s, file_diff, 1, &s->buf);
reassemble_patch(s, file_diff, 1, &s->buf);
setup_child_process(s, &cp,
setup_child_process(s, &cp,
"apply", "--cached", "--check", NULL);
"apply", "--check", NULL);
argv_array_pushv(&cp.args, s->mode->apply_check_args);
if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
return error(_("'git apply --cached' failed"));
return error(_("'git apply --cached' failed"));
@ -1005,13 +1049,6 @@ static size_t display_hunks(struct add_p_state *s,
return end_index;
return end_index;
}
}
static const char help_patch_text[] =
N_("y - stage this hunk\n"
"n - do not stage this hunk\n"
"q - quit; do not stage this hunk or any of the remaining ones\n"
"a - stage this and all the remaining hunks\n"
"d - do not stage this hunk nor any of the remaining hunks\n");
static const char help_patch_remainder[] =
static const char help_patch_remainder[] =
N_("j - leave this hunk undecided, see next undecided hunk\n"
N_("j - leave this hunk undecided, see next undecided hunk\n"
"J - leave this hunk undecided, see next hunk\n"
"J - leave this hunk undecided, see next hunk\n"
@ -1097,7 +1134,8 @@ static int patch_update_file(struct add_p_state *s,
(uintmax_t)hunk_index + 1,
(uintmax_t)hunk_index + 1,
(uintmax_t)file_diff->hunk_nr);
(uintmax_t)file_diff->hunk_nr);
color_fprintf(stdout, s->s.prompt_color,
color_fprintf(stdout, s->s.prompt_color,
_(prompt_mode[prompt_mode_type]), s->buf.buf);
_(s->mode->prompt_mode[prompt_mode_type]),
s->buf.buf);
fflush(stdout);
fflush(stdout);
if (strbuf_getline(&s->answer, stdin) == EOF)
if (strbuf_getline(&s->answer, stdin) == EOF)
break;
break;
@ -1254,7 +1292,7 @@ soft_increment:
const char *p = _(help_patch_remainder), *eol = p;
const char *p = _(help_patch_remainder), *eol = p;
color_fprintf(stdout, s->s.help_color, "%s",
color_fprintf(stdout, s->s.help_color, "%s",
_(help_patch_text));
_(s->mode->help_patch_text));
/*
/*
* Show only those lines of the remainder that are
* Show only those lines of the remainder that are
@ -1288,10 +1326,11 @@ soft_increment:
reassemble_patch(s, file_diff, 0, &s->buf);
reassemble_patch(s, file_diff, 0, &s->buf);
discard_index(s->s.r->index);
discard_index(s->s.r->index);
setup_child_process(s, &cp, "apply", "--cached", NULL);
setup_child_process(s, &cp, "apply", NULL);
argv_array_pushv(&cp.args, s->mode->apply_args);
if (pipe_command(&cp, s->buf.buf, s->buf.len,
if (pipe_command(&cp, s->buf.buf, s->buf.len,
NULL, 0, NULL, 0))
NULL, 0, NULL, 0))
error(_("'git apply --cached' failed"));
error(_("'git apply' failed"));
if (!repo_read_index(s->s.r))
if (!repo_read_index(s->s.r))
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
1, NULL, NULL, NULL);
1, NULL, NULL, NULL);
@ -1301,7 +1340,8 @@ soft_increment:
return quit;
return quit;
}
}
int run_add_p(struct repository *r, const struct pathspec *ps)
int run_add_p(struct repository *r, enum add_p_mode mode,
const char *revision, const struct pathspec *ps)
{
{
struct add_p_state s = {
struct add_p_state s = {
{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
@ -1310,6 +1350,9 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
init_add_i_state(&s.s, r);
init_add_i_state(&s.s, r);
s.mode = &patch_mode_add;
s.revision = revision;
if (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||
if (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||
repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
NULL, NULL, NULL) < 0 ||
NULL, NULL, NULL) < 0 ||