Merge branch 'js/add-p-leftover-bits'
The final leg of rewriting "add -i/-p" in C. * js/add-p-leftover-bits: ci: include the built-in `git add -i` in the `linux-gcc` job built-in add -p: handle Escape sequences more efficiently built-in add -p: handle Escape sequences in interactive.singlekey mode built-in add -p: respect the `interactive.singlekey` config setting terminal: add a new function to read a single keystroke terminal: accommodate Git for Windows' default terminal terminal: make the code of disable_echo() reusable built-in add -p: handle diff.algorithm built-in add -p: support interactive.diffFilter t3701: adjust difffilter test
This commit is contained in:
commit
76c57fedfa
@ -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));
|
diff_get_color(s->use_color, DIFF_FILE_OLD));
|
||||||
init_color(r, s, "new", s->file_new_color,
|
init_color(r, s, "new", s->file_new_color,
|
||||||
diff_get_color(s->use_color, DIFF_FILE_NEW));
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1152,6 +1170,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
|
|||||||
strbuf_release(&print_file_item_data.worktree);
|
strbuf_release(&print_file_item_data.worktree);
|
||||||
strbuf_release(&header);
|
strbuf_release(&header);
|
||||||
prefix_item_list_clear(&commands);
|
prefix_item_list_clear(&commands);
|
||||||
|
clear_add_i_state(&s);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,13 @@ struct add_i_state {
|
|||||||
char context_color[COLOR_MAXLEN];
|
char context_color[COLOR_MAXLEN];
|
||||||
char file_old_color[COLOR_MAXLEN];
|
char file_old_color[COLOR_MAXLEN];
|
||||||
char file_new_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 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 repository;
|
||||||
struct pathspec;
|
struct pathspec;
|
||||||
|
57
add-patch.c
57
add-patch.c
@ -6,6 +6,7 @@
|
|||||||
#include "pathspec.h"
|
#include "pathspec.h"
|
||||||
#include "color.h"
|
#include "color.h"
|
||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
|
#include "compat/terminal.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,
|
||||||
@ -360,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)
|
static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
||||||
{
|
{
|
||||||
struct argv_array args = ARGV_ARRAY_INIT;
|
struct argv_array args = ARGV_ARRAY_INIT;
|
||||||
|
const char *diff_algorithm = s->s.interactive_diff_algorithm;
|
||||||
struct strbuf *plain = &s->plain, *colored = NULL;
|
struct strbuf *plain = &s->plain, *colored = NULL;
|
||||||
struct child_process cp = CHILD_PROCESS_INIT;
|
struct child_process cp = CHILD_PROCESS_INIT;
|
||||||
char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
|
char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
|
||||||
@ -369,6 +371,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
|||||||
int res;
|
int res;
|
||||||
|
|
||||||
argv_array_pushv(&args, s->mode->diff_cmd);
|
argv_array_pushv(&args, s->mode->diff_cmd);
|
||||||
|
if (diff_algorithm)
|
||||||
|
argv_array_pushf(&args, "--diff-algorithm=%s", diff_algorithm);
|
||||||
if (s->revision) {
|
if (s->revision) {
|
||||||
struct object_id oid;
|
struct object_id oid;
|
||||||
argv_array_push(&args,
|
argv_array_push(&args,
|
||||||
@ -398,6 +402,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
|||||||
|
|
||||||
if (want_color_fd(1, -1)) {
|
if (want_color_fd(1, -1)) {
|
||||||
struct child_process colored_cp = CHILD_PROCESS_INIT;
|
struct child_process colored_cp = CHILD_PROCESS_INIT;
|
||||||
|
const char *diff_filter = s->s.interactive_diff_filter;
|
||||||
|
|
||||||
setup_child_process(s, &colored_cp, NULL);
|
setup_child_process(s, &colored_cp, NULL);
|
||||||
xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
|
xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
|
||||||
@ -407,6 +412,24 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
|||||||
argv_array_clear(&args);
|
argv_array_clear(&args);
|
||||||
if (res)
|
if (res)
|
||||||
return error(_("could not parse colored diff"));
|
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);
|
strbuf_complete_line(colored);
|
||||||
colored_p = colored->buf;
|
colored_p = colored->buf;
|
||||||
colored_pend = colored_p + colored->len;
|
colored_pend = colored_p + colored->len;
|
||||||
@ -531,6 +554,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
|||||||
colored_pend - colored_p);
|
colored_pend - colored_p);
|
||||||
if (colored_eol)
|
if (colored_eol)
|
||||||
colored_p = colored_eol + 1;
|
colored_p = colored_eol + 1;
|
||||||
|
else if (p != pend)
|
||||||
|
/* colored shorter than non-colored? */
|
||||||
|
goto mismatched_output;
|
||||||
else
|
else
|
||||||
colored_p = colored_pend;
|
colored_p = colored_pend;
|
||||||
|
|
||||||
@ -555,6 +581,15 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
|
|||||||
*/
|
*/
|
||||||
hunk->splittable_into++;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1115,14 +1150,27 @@ static int run_apply_check(struct add_p_state *s,
|
|||||||
return 0;
|
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)
|
static int prompt_yesno(struct add_p_state *s, const char *prompt)
|
||||||
{
|
{
|
||||||
for (;;) {
|
for (;;) {
|
||||||
color_fprintf(stdout, s->s.prompt_color, "%s", _(prompt));
|
color_fprintf(stdout, s->s.prompt_color, "%s", _(prompt));
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
if (strbuf_getline(&s->answer, stdin) == EOF)
|
if (read_single_character(s) == EOF)
|
||||||
return -1;
|
return -1;
|
||||||
strbuf_trim_trailing_newline(&s->answer);
|
|
||||||
switch (tolower(s->answer.buf[0])) {
|
switch (tolower(s->answer.buf[0])) {
|
||||||
case 'n': return 0;
|
case 'n': return 0;
|
||||||
case 'y': return 1;
|
case 'y': return 1;
|
||||||
@ -1362,9 +1410,8 @@ static int patch_update_file(struct add_p_state *s,
|
|||||||
_(s->mode->prompt_mode[prompt_mode_type]),
|
_(s->mode->prompt_mode[prompt_mode_type]),
|
||||||
s->buf.buf);
|
s->buf.buf);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
if (strbuf_getline(&s->answer, stdin) == EOF)
|
if (read_single_character(s) == EOF)
|
||||||
break;
|
break;
|
||||||
strbuf_trim_trailing_newline(&s->answer);
|
|
||||||
|
|
||||||
if (!s->answer.len)
|
if (!s->answer.len)
|
||||||
continue;
|
continue;
|
||||||
@ -1612,6 +1659,7 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
|
|||||||
parse_diff(&s, ps) < 0) {
|
parse_diff(&s, ps) < 0) {
|
||||||
strbuf_release(&s.plain);
|
strbuf_release(&s.plain);
|
||||||
strbuf_release(&s.colored);
|
strbuf_release(&s.colored);
|
||||||
|
clear_add_i_state(&s.s);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1630,5 +1678,6 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
|
|||||||
strbuf_release(&s.buf);
|
strbuf_release(&s.buf);
|
||||||
strbuf_release(&s.plain);
|
strbuf_release(&s.plain);
|
||||||
strbuf_release(&s.colored);
|
strbuf_release(&s.colored);
|
||||||
|
clear_add_i_state(&s.s);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ linux-gcc)
|
|||||||
export GIT_TEST_OE_DELTA_SIZE=5
|
export GIT_TEST_OE_DELTA_SIZE=5
|
||||||
export GIT_TEST_COMMIT_GRAPH=1
|
export GIT_TEST_COMMIT_GRAPH=1
|
||||||
export GIT_TEST_MULTI_PACK_INDEX=1
|
export GIT_TEST_MULTI_PACK_INDEX=1
|
||||||
|
export GIT_TEST_ADD_I_USE_BUILTIN=1
|
||||||
make test
|
make test
|
||||||
;;
|
;;
|
||||||
linux-gcc-4.8)
|
linux-gcc-4.8)
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
#include "compat/terminal.h"
|
#include "compat/terminal.h"
|
||||||
#include "sigchain.h"
|
#include "sigchain.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
|
#include "run-command.h"
|
||||||
|
#include "string-list.h"
|
||||||
|
#include "hashmap.h"
|
||||||
|
|
||||||
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
|
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
|
||||||
|
|
||||||
@ -32,7 +35,7 @@ static void restore_term(void)
|
|||||||
term_fd = -1;
|
term_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int disable_echo(void)
|
static int disable_bits(tcflag_t bits)
|
||||||
{
|
{
|
||||||
struct termios t;
|
struct termios t;
|
||||||
|
|
||||||
@ -43,7 +46,7 @@ static int disable_echo(void)
|
|||||||
old_term = t;
|
old_term = t;
|
||||||
sigchain_push_common(restore_term_on_signal);
|
sigchain_push_common(restore_term_on_signal);
|
||||||
|
|
||||||
t.c_lflag &= ~ECHO;
|
t.c_lflag &= ~bits;
|
||||||
if (!tcsetattr(term_fd, TCSAFLUSH, &t))
|
if (!tcsetattr(term_fd, TCSAFLUSH, &t))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -53,17 +56,44 @@ error:
|
|||||||
return -1;
|
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)
|
#elif defined(GIT_WINDOWS_NATIVE)
|
||||||
|
|
||||||
#define INPUT_PATH "CONIN$"
|
#define INPUT_PATH "CONIN$"
|
||||||
#define OUTPUT_PATH "CONOUT$"
|
#define OUTPUT_PATH "CONOUT$"
|
||||||
#define FORCE_TEXT "t"
|
#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 HANDLE hconin = INVALID_HANDLE_VALUE;
|
||||||
static DWORD cmode;
|
static DWORD cmode;
|
||||||
|
|
||||||
static void restore_term(void)
|
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)
|
if (hconin == INVALID_HANDLE_VALUE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -72,8 +102,39 @@ static void restore_term(void)
|
|||||||
hconin = INVALID_HANDLE_VALUE;
|
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,
|
hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
|
||||||
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
@ -82,7 +143,7 @@ static int disable_echo(void)
|
|||||||
|
|
||||||
GetConsoleMode(hconin, &cmode);
|
GetConsoleMode(hconin, &cmode);
|
||||||
sigchain_push_common(restore_term_on_signal);
|
sigchain_push_common(restore_term_on_signal);
|
||||||
if (!SetConsoleMode(hconin, cmode & (~ENABLE_ECHO_INPUT))) {
|
if (!SetConsoleMode(hconin, cmode & ~bits)) {
|
||||||
CloseHandle(hconin);
|
CloseHandle(hconin);
|
||||||
hconin = INVALID_HANDLE_VALUE;
|
hconin = INVALID_HANDLE_VALUE;
|
||||||
return -1;
|
return -1;
|
||||||
@ -91,6 +152,47 @@ static int disable_echo(void)
|
|||||||
return 0;
|
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
|
#endif
|
||||||
|
|
||||||
#ifndef FORCE_TEXT
|
#ifndef FORCE_TEXT
|
||||||
@ -137,6 +239,126 @@ char *git_terminal_prompt(const char *prompt, int echo)
|
|||||||
return buf.buf;
|
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
|
#else
|
||||||
|
|
||||||
char *git_terminal_prompt(const char *prompt, int echo)
|
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);
|
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
|
#endif
|
||||||
|
@ -3,4 +3,7 @@
|
|||||||
|
|
||||||
char *git_terminal_prompt(const char *prompt, int echo);
|
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 */
|
#endif /* COMPAT_TERMINAL_H */
|
||||||
|
@ -570,7 +570,7 @@ test_expect_success 'detect bogus diffFilter output' '
|
|||||||
git reset --hard &&
|
git reset --hard &&
|
||||||
|
|
||||||
echo content >test &&
|
echo content >test &&
|
||||||
test_config interactive.diffFilter "echo too-short" &&
|
test_config interactive.diffFilter "sed 1d" &&
|
||||||
printf y >y &&
|
printf y >y &&
|
||||||
test_must_fail force_color git add -p <y
|
test_must_fail force_color git add -p <y
|
||||||
'
|
'
|
||||||
|
Loading…
Reference in New Issue
Block a user