mirror of
https://github.com/git-for-windows/git.git
synced 2026-03-21 19:26:54 -05:00
Merge remote-tracking branch 'dscho/add-p' into add-p-g4w
Let's test this for a while. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
@@ -52,6 +52,24 @@ void init_add_i_state(struct add_i_state *s, struct repository *r)
|
||||
diff_get_color(s->use_color, DIFF_FILE_OLD));
|
||||
init_color(r, s, "new", s->file_new_color,
|
||||
diff_get_color(s->use_color, DIFF_FILE_NEW));
|
||||
|
||||
FREE_AND_NULL(s->interactive_diff_filter);
|
||||
git_config_get_string("interactive.difffilter",
|
||||
&s->interactive_diff_filter);
|
||||
|
||||
FREE_AND_NULL(s->interactive_diff_algorithm);
|
||||
git_config_get_string("diff.algorithm",
|
||||
&s->interactive_diff_algorithm);
|
||||
|
||||
git_config_get_bool("interactive.singlekey", &s->use_single_key);
|
||||
}
|
||||
|
||||
void clear_add_i_state(struct add_i_state *s)
|
||||
{
|
||||
FREE_AND_NULL(s->interactive_diff_filter);
|
||||
FREE_AND_NULL(s->interactive_diff_algorithm);
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->use_color = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -924,7 +942,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
|
||||
parse_pathspec(&ps_selected,
|
||||
PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
|
||||
PATHSPEC_LITERAL_PATH, "", args.argv);
|
||||
res = run_add_p(s->r, &ps_selected);
|
||||
res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected);
|
||||
argv_array_clear(&args);
|
||||
clear_pathspec(&ps_selected);
|
||||
}
|
||||
@@ -1149,6 +1167,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
|
||||
strbuf_release(&print_file_item_data.worktree);
|
||||
strbuf_release(&header);
|
||||
prefix_item_list_clear(&commands);
|
||||
clear_add_i_state(&s);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -15,13 +15,27 @@ struct add_i_state {
|
||||
char context_color[COLOR_MAXLEN];
|
||||
char file_old_color[COLOR_MAXLEN];
|
||||
char file_new_color[COLOR_MAXLEN];
|
||||
|
||||
int use_single_key;
|
||||
char *interactive_diff_filter, *interactive_diff_algorithm;
|
||||
};
|
||||
|
||||
void init_add_i_state(struct add_i_state *s, struct repository *r);
|
||||
void clear_add_i_state(struct add_i_state *s);
|
||||
|
||||
struct repository;
|
||||
struct pathspec;
|
||||
int run_add_i(struct repository *r, const struct pathspec *ps);
|
||||
int run_add_p(struct repository *r, const struct pathspec *ps);
|
||||
|
||||
enum add_p_mode {
|
||||
ADD_P_ADD,
|
||||
ADD_P_STASH,
|
||||
ADD_P_RESET,
|
||||
ADD_P_CHECKOUT,
|
||||
ADD_P_WORKTREE,
|
||||
};
|
||||
|
||||
int run_add_p(struct repository *r, enum add_p_mode mode,
|
||||
const char *revision, const struct pathspec *ps);
|
||||
|
||||
#endif
|
||||
|
||||
413
add-patch.c
413
add-patch.c
@@ -6,15 +6,218 @@
|
||||
#include "pathspec.h"
|
||||
#include "color.h"
|
||||
#include "diff.h"
|
||||
#include "compat/terminal.h"
|
||||
|
||||
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[] = {
|
||||
N_("Stage mode change [y,n,a,q,d%s,?]? "),
|
||||
N_("Stage deletion [y,n,a,q,d%s,?]? "),
|
||||
N_("Stage this hunk [y,n,a,q,d%s,?]? ")
|
||||
struct patch_mode {
|
||||
/*
|
||||
* The magic constant 4 is chosen such that all patch modes
|
||||
* 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, index_only: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")
|
||||
};
|
||||
|
||||
static struct patch_mode patch_mode_stash = {
|
||||
.diff_cmd = { "diff-index", "HEAD", NULL },
|
||||
.apply_args = { "--cached", NULL },
|
||||
.apply_check_args = { "--cached", NULL },
|
||||
.prompt_mode = {
|
||||
N_("Stash mode change [y,n,q,a,d%s,?]? "),
|
||||
N_("Stash deletion [y,n,q,a,d%s,?]? "),
|
||||
N_("Stash 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 stashing."),
|
||||
.help_patch_text =
|
||||
N_("y - stash this hunk\n"
|
||||
"n - do not stash this hunk\n"
|
||||
"q - quit; do not stash this hunk or any of the remaining "
|
||||
"ones\n"
|
||||
"a - stash this hunk and all later hunks in the file\n"
|
||||
"d - do not stash this hunk or any of the later hunks in "
|
||||
"the file\n"),
|
||||
};
|
||||
|
||||
static struct patch_mode patch_mode_reset_head = {
|
||||
.diff_cmd = { "diff-index", "--cached", NULL },
|
||||
.apply_args = { "-R", "--cached", NULL },
|
||||
.apply_check_args = { "-R", "--cached", NULL },
|
||||
.is_reverse = 1,
|
||||
.index_only = 1,
|
||||
.prompt_mode = {
|
||||
N_("Unstage mode change [y,n,q,a,d%s,?]? "),
|
||||
N_("Unstage deletion [y,n,q,a,d%s,?]? "),
|
||||
N_("Unstage 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 unstaging."),
|
||||
.help_patch_text =
|
||||
N_("y - unstage this hunk\n"
|
||||
"n - do not unstage this hunk\n"
|
||||
"q - quit; do not unstage this hunk or any of the remaining "
|
||||
"ones\n"
|
||||
"a - unstage this hunk and all later hunks in the file\n"
|
||||
"d - do not unstage this hunk or any of the later hunks in "
|
||||
"the file\n"),
|
||||
};
|
||||
|
||||
static struct patch_mode patch_mode_reset_nothead = {
|
||||
.diff_cmd = { "diff-index", "-R", "--cached", NULL },
|
||||
.apply_args = { "--cached", NULL },
|
||||
.apply_check_args = { "--cached", NULL },
|
||||
.index_only = 1,
|
||||
.prompt_mode = {
|
||||
N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
|
||||
N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
|
||||
N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
|
||||
},
|
||||
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
|
||||
"will immediately be marked for applying."),
|
||||
.help_patch_text =
|
||||
N_("y - apply this hunk to index\n"
|
||||
"n - do not apply this hunk to index\n"
|
||||
"q - quit; do not apply this hunk or any of the remaining "
|
||||
"ones\n"
|
||||
"a - apply this hunk and all later hunks in the file\n"
|
||||
"d - do not apply this hunk or any of the later hunks in "
|
||||
"the file\n"),
|
||||
};
|
||||
|
||||
static struct patch_mode patch_mode_checkout_index = {
|
||||
.diff_cmd = { "diff-files", NULL },
|
||||
.apply_args = { "-R", NULL },
|
||||
.apply_check_args = { "-R", NULL },
|
||||
.is_reverse = 1,
|
||||
.prompt_mode = {
|
||||
N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
|
||||
N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
|
||||
N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
|
||||
},
|
||||
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
|
||||
"will immediately be marked for discarding."),
|
||||
.help_patch_text =
|
||||
N_("y - discard this hunk from worktree\n"
|
||||
"n - do not discard this hunk from worktree\n"
|
||||
"q - quit; do not discard this hunk or any of the remaining "
|
||||
"ones\n"
|
||||
"a - discard this hunk and all later hunks in the file\n"
|
||||
"d - do not discard this hunk or any of the later hunks in "
|
||||
"the file\n"),
|
||||
};
|
||||
|
||||
static struct patch_mode patch_mode_checkout_head = {
|
||||
.diff_cmd = { "diff-index", NULL },
|
||||
.apply_for_checkout = 1,
|
||||
.apply_check_args = { "-R", NULL },
|
||||
.is_reverse = 1,
|
||||
.prompt_mode = {
|
||||
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
|
||||
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
|
||||
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
|
||||
},
|
||||
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
|
||||
"will immediately be marked for discarding."),
|
||||
.help_patch_text =
|
||||
N_("y - discard this hunk from index and worktree\n"
|
||||
"n - do not discard this hunk from index and worktree\n"
|
||||
"q - quit; do not discard this hunk or any of the remaining "
|
||||
"ones\n"
|
||||
"a - discard this hunk and all later hunks in the file\n"
|
||||
"d - do not discard this hunk or any of the later hunks in "
|
||||
"the file\n"),
|
||||
};
|
||||
|
||||
static struct patch_mode patch_mode_checkout_nothead = {
|
||||
.diff_cmd = { "diff-index", "-R", NULL },
|
||||
.apply_for_checkout = 1,
|
||||
.apply_check_args = { NULL },
|
||||
.prompt_mode = {
|
||||
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
|
||||
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
|
||||
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
|
||||
},
|
||||
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
|
||||
"will immediately be marked for applying."),
|
||||
.help_patch_text =
|
||||
N_("y - apply this hunk to index and worktree\n"
|
||||
"n - do not apply this hunk to index and worktree\n"
|
||||
"q - quit; do not apply this hunk or any of the remaining "
|
||||
"ones\n"
|
||||
"a - apply this hunk and all later hunks in the file\n"
|
||||
"d - do not apply this hunk or any of the later hunks in "
|
||||
"the file\n"),
|
||||
};
|
||||
|
||||
static struct patch_mode patch_mode_worktree_head = {
|
||||
.diff_cmd = { "diff-index", NULL },
|
||||
.apply_args = { "-R", NULL },
|
||||
.apply_check_args = { "-R", NULL },
|
||||
.is_reverse = 1,
|
||||
.prompt_mode = {
|
||||
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
|
||||
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
|
||||
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
|
||||
},
|
||||
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
|
||||
"will immediately be marked for discarding."),
|
||||
.help_patch_text =
|
||||
N_("y - discard this hunk from worktree\n"
|
||||
"n - do not discard this hunk from worktree\n"
|
||||
"q - quit; do not discard this hunk or any of the remaining "
|
||||
"ones\n"
|
||||
"a - discard this hunk and all later hunks in the file\n"
|
||||
"d - do not discard this hunk or any of the later hunks in "
|
||||
"the file\n"),
|
||||
};
|
||||
|
||||
static struct patch_mode patch_mode_worktree_nothead = {
|
||||
.diff_cmd = { "diff-index", "-R", NULL },
|
||||
.apply_args = { NULL },
|
||||
.apply_check_args = { NULL },
|
||||
.prompt_mode = {
|
||||
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
|
||||
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
|
||||
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
|
||||
},
|
||||
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
|
||||
"will immediately be marked for applying."),
|
||||
.help_patch_text =
|
||||
N_("y - apply this hunk to worktree\n"
|
||||
"n - do not apply this hunk to worktree\n"
|
||||
"q - quit; do not apply this hunk or any of the remaining "
|
||||
"ones\n"
|
||||
"a - apply this hunk and all later hunks in the file\n"
|
||||
"d - do not apply this hunk or any of the later hunks in "
|
||||
"the file\n"),
|
||||
};
|
||||
|
||||
struct hunk_header {
|
||||
@@ -47,6 +250,10 @@ struct add_p_state {
|
||||
unsigned deleted:1, mode_change:1,binary:1;
|
||||
} *file_diff;
|
||||
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, ...)
|
||||
@@ -154,6 +361,7 @@ static int is_octal(const char *p, size_t len)
|
||||
static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
||||
{
|
||||
struct argv_array args = ARGV_ARRAY_INIT;
|
||||
const char *diff_algorithm = s->s.interactive_diff_algorithm;
|
||||
struct strbuf *plain = &s->plain, *colored = NULL;
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
|
||||
@@ -162,9 +370,20 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
||||
struct hunk *hunk = NULL;
|
||||
int res;
|
||||
|
||||
argv_array_pushv(&args, s->mode->diff_cmd);
|
||||
if (diff_algorithm)
|
||||
argv_array_pushf(&args, "--diff-algorithm=%s", diff_algorithm);
|
||||
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`. */
|
||||
argv_array_pushl(&args, "diff-files", "-p", "--no-color", "--", NULL);
|
||||
color_arg_index = args.argc - 2;
|
||||
argv_array_pushl(&args, "--no-color", "-p", "--", NULL);
|
||||
for (i = 0; i < ps->nr; i++)
|
||||
argv_array_push(&args, ps->items[i].original);
|
||||
|
||||
@@ -183,6 +402,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
||||
|
||||
if (want_color_fd(1, -1)) {
|
||||
struct child_process colored_cp = CHILD_PROCESS_INIT;
|
||||
const char *diff_filter = s->s.interactive_diff_filter;
|
||||
|
||||
setup_child_process(s, &colored_cp, NULL);
|
||||
xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
|
||||
@@ -192,6 +412,24 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
||||
argv_array_clear(&args);
|
||||
if (res)
|
||||
return error(_("could not parse colored diff"));
|
||||
|
||||
if (diff_filter) {
|
||||
struct child_process filter_cp = CHILD_PROCESS_INIT;
|
||||
|
||||
setup_child_process(s, &filter_cp,
|
||||
diff_filter, NULL);
|
||||
filter_cp.git_cmd = 0;
|
||||
filter_cp.use_shell = 1;
|
||||
strbuf_reset(&s->buf);
|
||||
if (pipe_command(&filter_cp,
|
||||
colored->buf, colored->len,
|
||||
&s->buf, colored->len,
|
||||
NULL, 0) < 0)
|
||||
return error(_("failed to run '%s'"),
|
||||
diff_filter);
|
||||
strbuf_swap(colored, &s->buf);
|
||||
}
|
||||
|
||||
strbuf_complete_line(colored);
|
||||
colored_p = colored->buf;
|
||||
colored_pend = colored_p + colored->len;
|
||||
@@ -316,6 +554,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
||||
colored_pend - colored_p);
|
||||
if (colored_eol)
|
||||
colored_p = colored_eol + 1;
|
||||
else if (p != pend)
|
||||
/* colored shorter than non-colored? */
|
||||
goto mismatched_output;
|
||||
else
|
||||
colored_p = colored_pend;
|
||||
|
||||
@@ -340,6 +581,15 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
||||
*/
|
||||
hunk->splittable_into++;
|
||||
|
||||
/* non-colored shorter than colored? */
|
||||
if (colored_p != colored_pend) {
|
||||
mismatched_output:
|
||||
error(_("mismatched output from interactive.diffFilter"));
|
||||
advise(_("Your filter must maintain a one-to-one correspondence\n"
|
||||
"between its input and output lines."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -382,7 +632,10 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
|
||||
- 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 @@",
|
||||
old_offset, header->old_count,
|
||||
@@ -805,11 +1058,10 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
|
||||
"(context).\n"
|
||||
"To remove '%c' lines, delete them.\n"
|
||||
"Lines starting with %c will be removed.\n"),
|
||||
'-', '+', comment_line_char);
|
||||
strbuf_commented_addf(&s->buf,
|
||||
_("If the patch applies cleanly, the edited hunk "
|
||||
"will immediately be\n"
|
||||
"marked for staging.\n"));
|
||||
s->mode->is_reverse ? '+' : '-',
|
||||
s->mode->is_reverse ? '-' : '+',
|
||||
comment_line_char);
|
||||
strbuf_commented_addf(&s->buf, "%s", _(s->mode->edit_hunk_hint));
|
||||
/*
|
||||
* TRANSLATORS: 'it' refers to the patch mentioned in the previous
|
||||
* messages.
|
||||
@@ -890,21 +1142,35 @@ static int run_apply_check(struct add_p_state *s,
|
||||
reassemble_patch(s, file_diff, 1, &s->buf);
|
||||
|
||||
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))
|
||||
return error(_("'git apply --cached' failed"));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_single_character(struct add_p_state *s)
|
||||
{
|
||||
if (s->s.use_single_key) {
|
||||
int res = read_key_without_echo(&s->answer);
|
||||
printf("%s\n", res == EOF ? "" : s->answer.buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (strbuf_getline(&s->answer, stdin) == EOF)
|
||||
return EOF;
|
||||
strbuf_trim_trailing_newline(&s->answer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prompt_yesno(struct add_p_state *s, const char *prompt)
|
||||
{
|
||||
for (;;) {
|
||||
color_fprintf(stdout, s->s.prompt_color, "%s", _(prompt));
|
||||
fflush(stdout);
|
||||
if (strbuf_getline(&s->answer, stdin) == EOF)
|
||||
if (read_single_character(s) == EOF)
|
||||
return -1;
|
||||
strbuf_trim_trailing_newline(&s->answer);
|
||||
switch (tolower(s->answer.buf[0])) {
|
||||
case 'n': return 0;
|
||||
case 'y': return 1;
|
||||
@@ -957,6 +1223,57 @@ static int edit_hunk_loop(struct add_p_state *s,
|
||||
}
|
||||
}
|
||||
|
||||
static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
|
||||
int is_reverse)
|
||||
{
|
||||
const char *reverse = is_reverse ? "-R" : NULL;
|
||||
struct child_process check_index = CHILD_PROCESS_INIT;
|
||||
struct child_process check_worktree = CHILD_PROCESS_INIT;
|
||||
struct child_process apply_index = CHILD_PROCESS_INIT;
|
||||
struct child_process apply_worktree = CHILD_PROCESS_INIT;
|
||||
int applies_index, applies_worktree;
|
||||
|
||||
setup_child_process(s, &check_index,
|
||||
"apply", "--cached", "--check", reverse, NULL);
|
||||
applies_index = !pipe_command(&check_index, diff->buf, diff->len,
|
||||
NULL, 0, NULL, 0);
|
||||
|
||||
setup_child_process(s, &check_worktree,
|
||||
"apply", "--check", reverse, NULL);
|
||||
applies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,
|
||||
NULL, 0, NULL, 0);
|
||||
|
||||
if (applies_worktree && applies_index) {
|
||||
setup_child_process(s, &apply_index,
|
||||
"apply", "--cached", reverse, NULL);
|
||||
pipe_command(&apply_index, diff->buf, diff->len,
|
||||
NULL, 0, NULL, 0);
|
||||
|
||||
setup_child_process(s, &apply_worktree,
|
||||
"apply", reverse, NULL);
|
||||
pipe_command(&apply_worktree, diff->buf, diff->len,
|
||||
NULL, 0, NULL, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!applies_index) {
|
||||
err(s, _("The selected hunks do not apply to the index!"));
|
||||
if (prompt_yesno(s, _("Apply them to the worktree "
|
||||
"anyway? ")) > 0) {
|
||||
setup_child_process(s, &apply_worktree,
|
||||
"apply", reverse, NULL);
|
||||
return pipe_command(&apply_worktree, diff->buf,
|
||||
diff->len, NULL, 0, NULL, 0);
|
||||
}
|
||||
err(s, _("Nothing was applied.\n"));
|
||||
} else
|
||||
/* As a last resort, show the diff to the user */
|
||||
fwrite(diff->buf, diff->len, 1, stderr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SUMMARY_HEADER_WIDTH 20
|
||||
#define SUMMARY_LINE_WIDTH 80
|
||||
static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
|
||||
@@ -1005,13 +1322,6 @@ static size_t display_hunks(struct add_p_state *s,
|
||||
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[] =
|
||||
N_("j - leave this hunk undecided, see next undecided hunk\n"
|
||||
"J - leave this hunk undecided, see next hunk\n"
|
||||
@@ -1097,11 +1407,11 @@ static int patch_update_file(struct add_p_state *s,
|
||||
(uintmax_t)hunk_index + 1,
|
||||
(uintmax_t)file_diff->hunk_nr);
|
||||
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);
|
||||
if (strbuf_getline(&s->answer, stdin) == EOF)
|
||||
if (read_single_character(s) == EOF)
|
||||
break;
|
||||
strbuf_trim_trailing_newline(&s->answer);
|
||||
|
||||
if (!s->answer.len)
|
||||
continue;
|
||||
@@ -1254,7 +1564,7 @@ soft_increment:
|
||||
const char *p = _(help_patch_remainder), *eol = p;
|
||||
|
||||
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
|
||||
@@ -1288,10 +1598,16 @@ soft_increment:
|
||||
reassemble_patch(s, file_diff, 0, &s->buf);
|
||||
|
||||
discard_index(s->s.r->index);
|
||||
setup_child_process(s, &cp, "apply", "--cached", NULL);
|
||||
if (pipe_command(&cp, s->buf.buf, s->buf.len,
|
||||
NULL, 0, NULL, 0))
|
||||
error(_("'git apply --cached' failed"));
|
||||
if (s->mode->apply_for_checkout)
|
||||
apply_for_checkout(s, &s->buf,
|
||||
s->mode->is_reverse);
|
||||
else {
|
||||
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,
|
||||
NULL, 0, NULL, 0))
|
||||
error(_("'git apply' failed"));
|
||||
}
|
||||
if (!repo_read_index(s->s.r))
|
||||
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
|
||||
1, NULL, NULL, NULL);
|
||||
@@ -1301,7 +1617,8 @@ soft_increment:
|
||||
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 = {
|
||||
{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
|
||||
@@ -1310,12 +1627,39 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
|
||||
|
||||
init_add_i_state(&s.s, r);
|
||||
|
||||
if (mode == ADD_P_STASH)
|
||||
s.mode = &patch_mode_stash;
|
||||
else if (mode == ADD_P_RESET) {
|
||||
if (!revision || !strcmp(revision, "HEAD"))
|
||||
s.mode = &patch_mode_reset_head;
|
||||
else
|
||||
s.mode = &patch_mode_reset_nothead;
|
||||
} else if (mode == ADD_P_CHECKOUT) {
|
||||
if (!revision)
|
||||
s.mode = &patch_mode_checkout_index;
|
||||
else if (!strcmp(revision, "HEAD"))
|
||||
s.mode = &patch_mode_checkout_head;
|
||||
else
|
||||
s.mode = &patch_mode_checkout_nothead;
|
||||
} else if (mode == ADD_P_WORKTREE) {
|
||||
if (!revision)
|
||||
s.mode = &patch_mode_checkout_index;
|
||||
else if (!strcmp(revision, "HEAD"))
|
||||
s.mode = &patch_mode_worktree_head;
|
||||
else
|
||||
s.mode = &patch_mode_worktree_nothead;
|
||||
} else
|
||||
s.mode = &patch_mode_add;
|
||||
s.revision = revision;
|
||||
|
||||
if (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||
|
||||
repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
|
||||
NULL, NULL, NULL) < 0 ||
|
||||
(!s.mode->index_only &&
|
||||
repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
|
||||
NULL, NULL, NULL) < 0) ||
|
||||
parse_diff(&s, ps) < 0) {
|
||||
strbuf_release(&s.plain);
|
||||
strbuf_release(&s.colored);
|
||||
clear_add_i_state(&s.s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1334,5 +1678,6 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
|
||||
strbuf_release(&s.buf);
|
||||
strbuf_release(&s.plain);
|
||||
strbuf_release(&s.colored);
|
||||
clear_add_i_state(&s.s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ static int take_worktree_changes;
|
||||
static int add_renormalize;
|
||||
static int pathspec_file_nul;
|
||||
static const char *pathspec_from_file;
|
||||
static int legacy_stash_p; /* support for the scripted `git stash` */
|
||||
|
||||
struct update_callback_data {
|
||||
int flags;
|
||||
@@ -196,12 +197,25 @@ int run_add_interactive(const char *revision, const char *patch_mode,
|
||||
&use_builtin_add_i);
|
||||
|
||||
if (use_builtin_add_i == 1) {
|
||||
enum add_p_mode mode;
|
||||
|
||||
if (!patch_mode)
|
||||
return !!run_add_i(the_repository, pathspec);
|
||||
if (strcmp(patch_mode, "--patch"))
|
||||
die("'%s' not yet supported in the built-in add -p",
|
||||
patch_mode);
|
||||
return !!run_add_p(the_repository, pathspec);
|
||||
|
||||
if (!strcmp(patch_mode, "--patch"))
|
||||
mode = ADD_P_ADD;
|
||||
else if (!strcmp(patch_mode, "--patch=stash"))
|
||||
mode = ADD_P_STASH;
|
||||
else if (!strcmp(patch_mode, "--patch=reset"))
|
||||
mode = ADD_P_RESET;
|
||||
else if (!strcmp(patch_mode, "--patch=checkout"))
|
||||
mode = ADD_P_CHECKOUT;
|
||||
else if (!strcmp(patch_mode, "--patch=worktree"))
|
||||
mode = ADD_P_WORKTREE;
|
||||
else
|
||||
die("'%s' not supported", patch_mode);
|
||||
|
||||
return !!run_add_p(the_repository, mode, revision, pathspec);
|
||||
}
|
||||
|
||||
argv_array_push(&argv, "add--interactive");
|
||||
@@ -329,6 +343,8 @@ static struct option builtin_add_options[] = {
|
||||
N_("warn when adding an embedded repository")),
|
||||
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
|
||||
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
|
||||
OPT_HIDDEN_BOOL(0, "legacy-stash-p", &legacy_stash_p,
|
||||
N_("backend for `git stash -p`")),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
@@ -429,6 +445,18 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
|
||||
}
|
||||
|
||||
if (legacy_stash_p) {
|
||||
struct pathspec pathspec;
|
||||
|
||||
parse_pathspec(&pathspec, 0,
|
||||
PATHSPEC_PREFER_FULL |
|
||||
PATHSPEC_SYMLINK_LEADING_PATH |
|
||||
PATHSPEC_PREFIX_ORIGIN,
|
||||
prefix, argv);
|
||||
|
||||
return run_add_interactive(NULL, "--patch=stash", &pathspec);
|
||||
}
|
||||
|
||||
if (edit_interactive) {
|
||||
if (pathspec_from_file)
|
||||
die(_("--pathspec-from-file is incompatible with --edit"));
|
||||
|
||||
@@ -367,7 +367,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
|
||||
die(_("index file corrupt"));
|
||||
|
||||
if (interactive) {
|
||||
char *old_index_env = NULL;
|
||||
char *old_index_env = NULL, *old_repo_index_file;
|
||||
hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
|
||||
|
||||
refresh_cache_or_die(refresh_flags);
|
||||
@@ -375,12 +375,16 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
|
||||
if (write_locked_index(&the_index, &index_lock, 0))
|
||||
die(_("unable to create temporary index"));
|
||||
|
||||
old_repo_index_file = the_repository->index_file;
|
||||
the_repository->index_file =
|
||||
(char *)get_lock_file_path(&index_lock);
|
||||
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
|
||||
setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1);
|
||||
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
|
||||
|
||||
if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
|
||||
die(_("interactive add failed"));
|
||||
|
||||
the_repository->index_file = old_repo_index_file;
|
||||
if (old_index_env && *old_index_env)
|
||||
setenv(INDEX_ENVIRONMENT, old_index_env, 1);
|
||||
else
|
||||
|
||||
@@ -998,9 +998,9 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
|
||||
{
|
||||
int ret = 0;
|
||||
struct child_process cp_read_tree = CHILD_PROCESS_INIT;
|
||||
struct child_process cp_add_i = CHILD_PROCESS_INIT;
|
||||
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
|
||||
struct index_state istate = { NULL };
|
||||
char *old_index_env = NULL, *old_repo_index_file;
|
||||
|
||||
remove_path(stash_index_path.buf);
|
||||
|
||||
@@ -1014,16 +1014,19 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
|
||||
}
|
||||
|
||||
/* Find out what the user wants. */
|
||||
cp_add_i.git_cmd = 1;
|
||||
argv_array_pushl(&cp_add_i.args, "add--interactive", "--patch=stash",
|
||||
"--", NULL);
|
||||
add_pathspecs(&cp_add_i.args, ps);
|
||||
argv_array_pushf(&cp_add_i.env_array, "GIT_INDEX_FILE=%s",
|
||||
stash_index_path.buf);
|
||||
if (run_command(&cp_add_i)) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
old_repo_index_file = the_repository->index_file;
|
||||
the_repository->index_file = stash_index_path.buf;
|
||||
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
|
||||
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
|
||||
|
||||
ret = run_add_interactive(NULL, "--patch=stash", ps);
|
||||
|
||||
the_repository->index_file = old_repo_index_file;
|
||||
if (old_index_env && *old_index_env)
|
||||
setenv(INDEX_ENVIRONMENT, old_index_env, 1);
|
||||
else
|
||||
unsetenv(INDEX_ENVIRONMENT);
|
||||
FREE_AND_NULL(old_index_env);
|
||||
|
||||
/* State of the working tree. */
|
||||
if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
|
||||
@@ -1033,7 +1036,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
|
||||
}
|
||||
|
||||
cp_diff_tree.git_cmd = 1;
|
||||
argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "HEAD",
|
||||
argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
|
||||
oid_to_hex(&info->w_tree), "--", NULL);
|
||||
if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
|
||||
ret = -1;
|
||||
|
||||
@@ -20,6 +20,7 @@ linux-gcc)
|
||||
export GIT_TEST_OE_DELTA_SIZE=5
|
||||
export GIT_TEST_COMMIT_GRAPH=1
|
||||
export GIT_TEST_MULTI_PACK_INDEX=1
|
||||
export GIT_TEST_ADD_I_USE_BUILTIN=1
|
||||
make test
|
||||
;;
|
||||
linux-gcc-4.8)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include "compat/terminal.h"
|
||||
#include "sigchain.h"
|
||||
#include "strbuf.h"
|
||||
#include "run-command.h"
|
||||
#include "string-list.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
|
||||
|
||||
@@ -32,7 +35,7 @@ static void restore_term(void)
|
||||
term_fd = -1;
|
||||
}
|
||||
|
||||
static int disable_echo(void)
|
||||
static int disable_bits(tcflag_t bits)
|
||||
{
|
||||
struct termios t;
|
||||
|
||||
@@ -43,7 +46,7 @@ static int disable_echo(void)
|
||||
old_term = t;
|
||||
sigchain_push_common(restore_term_on_signal);
|
||||
|
||||
t.c_lflag &= ~ECHO;
|
||||
t.c_lflag &= ~bits;
|
||||
if (!tcsetattr(term_fd, TCSAFLUSH, &t))
|
||||
return 0;
|
||||
|
||||
@@ -53,17 +56,44 @@ error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int disable_echo(void)
|
||||
{
|
||||
return disable_bits(ECHO);
|
||||
}
|
||||
|
||||
static int enable_non_canonical(void)
|
||||
{
|
||||
return disable_bits(ICANON | ECHO);
|
||||
}
|
||||
|
||||
#elif defined(GIT_WINDOWS_NATIVE)
|
||||
|
||||
#define INPUT_PATH "CONIN$"
|
||||
#define OUTPUT_PATH "CONOUT$"
|
||||
#define FORCE_TEXT "t"
|
||||
|
||||
static int use_stty = 1;
|
||||
static struct string_list stty_restore = STRING_LIST_INIT_DUP;
|
||||
static HANDLE hconin = INVALID_HANDLE_VALUE;
|
||||
static DWORD cmode;
|
||||
|
||||
static void restore_term(void)
|
||||
{
|
||||
if (use_stty) {
|
||||
int i;
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
||||
if (stty_restore.nr == 0)
|
||||
return;
|
||||
|
||||
argv_array_push(&cp.args, "stty");
|
||||
for (i = 0; i < stty_restore.nr; i++)
|
||||
argv_array_push(&cp.args, stty_restore.items[i].string);
|
||||
run_command(&cp);
|
||||
string_list_clear(&stty_restore, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hconin == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
@@ -72,8 +102,39 @@ static void restore_term(void)
|
||||
hconin = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
static int disable_echo(void)
|
||||
static int disable_bits(DWORD bits)
|
||||
{
|
||||
if (use_stty) {
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
||||
argv_array_push(&cp.args, "stty");
|
||||
|
||||
if (bits & ENABLE_LINE_INPUT) {
|
||||
string_list_append(&stty_restore, "icanon");
|
||||
argv_array_push(&cp.args, "-icanon");
|
||||
}
|
||||
|
||||
if (bits & ENABLE_ECHO_INPUT) {
|
||||
string_list_append(&stty_restore, "echo");
|
||||
argv_array_push(&cp.args, "-echo");
|
||||
}
|
||||
|
||||
if (bits & ENABLE_PROCESSED_INPUT) {
|
||||
string_list_append(&stty_restore, "-ignbrk");
|
||||
string_list_append(&stty_restore, "intr");
|
||||
string_list_append(&stty_restore, "^c");
|
||||
argv_array_push(&cp.args, "ignbrk");
|
||||
argv_array_push(&cp.args, "intr");
|
||||
argv_array_push(&cp.args, "");
|
||||
}
|
||||
|
||||
if (run_command(&cp) == 0)
|
||||
return 0;
|
||||
|
||||
/* `stty` could not be executed; access the Console directly */
|
||||
use_stty = 0;
|
||||
}
|
||||
|
||||
hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
@@ -82,7 +143,7 @@ static int disable_echo(void)
|
||||
|
||||
GetConsoleMode(hconin, &cmode);
|
||||
sigchain_push_common(restore_term_on_signal);
|
||||
if (!SetConsoleMode(hconin, cmode & (~ENABLE_ECHO_INPUT))) {
|
||||
if (!SetConsoleMode(hconin, cmode & ~bits)) {
|
||||
CloseHandle(hconin);
|
||||
hconin = INVALID_HANDLE_VALUE;
|
||||
return -1;
|
||||
@@ -91,6 +152,47 @@ static int disable_echo(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disable_echo(void)
|
||||
{
|
||||
return disable_bits(ENABLE_ECHO_INPUT);
|
||||
}
|
||||
|
||||
static int enable_non_canonical(void)
|
||||
{
|
||||
return disable_bits(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Override `getchar()`, as the default implementation does not use
|
||||
* `ReadFile()`.
|
||||
*
|
||||
* This poses a problem when we want to see whether the standard
|
||||
* input has more characters, as the default of Git for Windows is to start the
|
||||
* Bash in a MinTTY, which uses a named pipe to emulate a pty, in which case
|
||||
* our `poll()` emulation calls `PeekNamedPipe()`, which seems to require
|
||||
* `ReadFile()` to be called first to work properly (it only reports 0
|
||||
* available bytes, otherwise).
|
||||
*
|
||||
* So let's just override `getchar()` with a version backed by `ReadFile()` and
|
||||
* go our merry ways from here.
|
||||
*/
|
||||
static int mingw_getchar(void)
|
||||
{
|
||||
DWORD read = 0;
|
||||
unsigned char ch;
|
||||
|
||||
if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), &ch, 1, &read, NULL))
|
||||
return EOF;
|
||||
|
||||
if (!read) {
|
||||
error("Unexpected 0 read");
|
||||
return EOF;
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
#define getchar mingw_getchar
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef FORCE_TEXT
|
||||
@@ -137,6 +239,126 @@ char *git_terminal_prompt(const char *prompt, int echo)
|
||||
return buf.buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* The `is_known_escape_sequence()` function returns 1 if the passed string
|
||||
* corresponds to an Escape sequence that the terminal capabilities contains.
|
||||
*
|
||||
* To avoid depending on ncurses or other platform-specific libraries, we rely
|
||||
* on the presence of the `infocmp` executable to do the job for us (failing
|
||||
* silently if the program is not available or refused to run).
|
||||
*/
|
||||
struct escape_sequence_entry {
|
||||
struct hashmap_entry entry;
|
||||
char sequence[FLEX_ARRAY];
|
||||
};
|
||||
|
||||
static int sequence_entry_cmp(const void *hashmap_cmp_fn_data,
|
||||
const struct escape_sequence_entry *e1,
|
||||
const struct escape_sequence_entry *e2,
|
||||
const void *keydata)
|
||||
{
|
||||
return strcmp(e1->sequence, keydata ? keydata : e2->sequence);
|
||||
}
|
||||
|
||||
static int is_known_escape_sequence(const char *sequence)
|
||||
{
|
||||
static struct hashmap sequences;
|
||||
static int initialized;
|
||||
|
||||
if (!initialized) {
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
char *p, *eol;
|
||||
|
||||
hashmap_init(&sequences, (hashmap_cmp_fn)sequence_entry_cmp,
|
||||
NULL, 0);
|
||||
|
||||
argv_array_pushl(&cp.args, "infocmp", "-L", "-1", NULL);
|
||||
if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0))
|
||||
strbuf_setlen(&buf, 0);
|
||||
|
||||
for (eol = p = buf.buf; *p; p = eol + 1) {
|
||||
p = strchr(p, '=');
|
||||
if (!p)
|
||||
break;
|
||||
p++;
|
||||
eol = strchrnul(p, '\n');
|
||||
|
||||
if (starts_with(p, "\\E")) {
|
||||
char *comma = memchr(p, ',', eol - p);
|
||||
struct escape_sequence_entry *e;
|
||||
|
||||
p[0] = '^';
|
||||
p[1] = '[';
|
||||
FLEX_ALLOC_MEM(e, sequence, p, comma - p);
|
||||
hashmap_entry_init(&e->entry,
|
||||
strhash(e->sequence));
|
||||
hashmap_add(&sequences, &e->entry);
|
||||
}
|
||||
if (!*eol)
|
||||
break;
|
||||
}
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
return !!hashmap_get_from_hash(&sequences, strhash(sequence), sequence);
|
||||
}
|
||||
|
||||
int read_key_without_echo(struct strbuf *buf)
|
||||
{
|
||||
static int warning_displayed;
|
||||
int ch;
|
||||
|
||||
if (warning_displayed || enable_non_canonical() < 0) {
|
||||
if (!warning_displayed) {
|
||||
warning("reading single keystrokes not supported on "
|
||||
"this platform; reading line instead");
|
||||
warning_displayed = 1;
|
||||
}
|
||||
|
||||
return strbuf_getline(buf, stdin);
|
||||
}
|
||||
|
||||
strbuf_reset(buf);
|
||||
ch = getchar();
|
||||
if (ch == EOF) {
|
||||
restore_term();
|
||||
return EOF;
|
||||
}
|
||||
strbuf_addch(buf, ch);
|
||||
|
||||
if (ch == '\033' /* ESC */) {
|
||||
/*
|
||||
* We are most likely looking at an Escape sequence. Let's try
|
||||
* to read more bytes, waiting at most half a second, assuming
|
||||
* that the sequence is complete if we did not receive any byte
|
||||
* within that time.
|
||||
*
|
||||
* Start by replacing the Escape byte with ^[ */
|
||||
strbuf_splice(buf, buf->len - 1, 1, "^[", 2);
|
||||
|
||||
/*
|
||||
* Query the terminal capabilities once about all the Escape
|
||||
* sequences it knows about, so that we can avoid waiting for
|
||||
* half a second when we know that the sequence is complete.
|
||||
*/
|
||||
while (!is_known_escape_sequence(buf->buf)) {
|
||||
struct pollfd pfd = { .fd = 0, .events = POLLIN };
|
||||
|
||||
if (poll(&pfd, 1, 500) < 1)
|
||||
break;
|
||||
|
||||
ch = getchar();
|
||||
if (ch == EOF)
|
||||
return 0;
|
||||
strbuf_addch(buf, ch);
|
||||
}
|
||||
}
|
||||
|
||||
restore_term();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
char *git_terminal_prompt(const char *prompt, int echo)
|
||||
@@ -144,4 +366,23 @@ char *git_terminal_prompt(const char *prompt, int echo)
|
||||
return getpass(prompt);
|
||||
}
|
||||
|
||||
int read_key_without_echo(struct strbuf *buf)
|
||||
{
|
||||
static int warning_displayed;
|
||||
const char *res;
|
||||
|
||||
if (!warning_displayed) {
|
||||
warning("reading single keystrokes not supported on this "
|
||||
"platform; reading line instead");
|
||||
warning_displayed = 1;
|
||||
}
|
||||
|
||||
res = getpass("");
|
||||
strbuf_reset(buf);
|
||||
if (!res)
|
||||
return EOF;
|
||||
strbuf_addstr(buf, res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,4 +3,7 @@
|
||||
|
||||
char *git_terminal_prompt(const char *prompt, int echo);
|
||||
|
||||
/* Read a single keystroke, without echoing it to the terminal */
|
||||
int read_key_without_echo(struct strbuf *buf);
|
||||
|
||||
#endif /* COMPAT_TERMINAL_H */
|
||||
|
||||
@@ -207,13 +207,13 @@ create_stash () {
|
||||
|
||||
# find out what the user wants
|
||||
GIT_INDEX_FILE="$TMP-index" \
|
||||
git add--interactive --patch=stash -- "$@" &&
|
||||
git add --legacy-stash-p -- "$@" &&
|
||||
|
||||
# state of the working tree
|
||||
w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
|
||||
die "$(gettext "Cannot save the current worktree state")"
|
||||
|
||||
git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
|
||||
git diff-tree -p -U1 HEAD $w_tree -- >"$TMP-patch" &&
|
||||
test -s "$TMP-patch" ||
|
||||
die "$(gettext "No changes selected")"
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ test_expect_success 'none of this moved HEAD' '
|
||||
verify_saved_head
|
||||
'
|
||||
|
||||
test_expect_failure 'stash -p with split hunk' '
|
||||
test_expect_success 'stash -p with split hunk' '
|
||||
git reset --hard &&
|
||||
cat >test <<-\EOF &&
|
||||
aaa
|
||||
@@ -106,8 +106,8 @@ test_expect_failure 'stash -p with split hunk' '
|
||||
ccc
|
||||
EOF
|
||||
printf "%s\n" s n y q |
|
||||
test_might_fail git stash -p 2>error &&
|
||||
! test_must_be_empty error &&
|
||||
git stash -p 2>error &&
|
||||
test_must_be_empty error &&
|
||||
grep "added line 1" test &&
|
||||
! grep "added line 2" test
|
||||
'
|
||||
|
||||
Reference in New Issue
Block a user