Merge branch 'rj/add-p-pager'
A 'P' command to "git add -p" that passes the patch hunk to the pager has been added. * rj/add-p-pager: add-patch: render hunks through the pager pager: introduce wait_for_pager pager: do not close fd 2 unnecessarily add-patch: test for 'p' command
This commit is contained in:
18
add-patch.c
18
add-patch.c
@@ -7,9 +7,11 @@
|
|||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
#include "object-name.h"
|
#include "object-name.h"
|
||||||
|
#include "pager.h"
|
||||||
#include "read-cache-ll.h"
|
#include "read-cache-ll.h"
|
||||||
#include "repository.h"
|
#include "repository.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
|
#include "sigchain.h"
|
||||||
#include "run-command.h"
|
#include "run-command.h"
|
||||||
#include "strvec.h"
|
#include "strvec.h"
|
||||||
#include "pathspec.h"
|
#include "pathspec.h"
|
||||||
@@ -1398,7 +1400,7 @@ N_("j - leave this hunk undecided, see next undecided hunk\n"
|
|||||||
"/ - search for a hunk matching the given regex\n"
|
"/ - search for a hunk matching the given regex\n"
|
||||||
"s - split the current hunk into smaller hunks\n"
|
"s - split the current hunk into smaller hunks\n"
|
||||||
"e - manually edit the current hunk\n"
|
"e - manually edit the current hunk\n"
|
||||||
"p - print the current hunk\n"
|
"p - print the current hunk, 'P' to use the pager\n"
|
||||||
"? - print help\n");
|
"? - print help\n");
|
||||||
|
|
||||||
static int patch_update_file(struct add_p_state *s,
|
static int patch_update_file(struct add_p_state *s,
|
||||||
@@ -1409,7 +1411,7 @@ static int patch_update_file(struct add_p_state *s,
|
|||||||
struct hunk *hunk;
|
struct hunk *hunk;
|
||||||
char ch;
|
char ch;
|
||||||
struct child_process cp = CHILD_PROCESS_INIT;
|
struct child_process cp = CHILD_PROCESS_INIT;
|
||||||
int colored = !!s->colored.len, quit = 0;
|
int colored = !!s->colored.len, quit = 0, use_pager = 0;
|
||||||
enum prompt_mode_type prompt_mode_type;
|
enum prompt_mode_type prompt_mode_type;
|
||||||
enum {
|
enum {
|
||||||
ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
|
ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
|
||||||
@@ -1459,9 +1461,18 @@ static int patch_update_file(struct add_p_state *s,
|
|||||||
strbuf_reset(&s->buf);
|
strbuf_reset(&s->buf);
|
||||||
if (file_diff->hunk_nr) {
|
if (file_diff->hunk_nr) {
|
||||||
if (rendered_hunk_index != hunk_index) {
|
if (rendered_hunk_index != hunk_index) {
|
||||||
|
if (use_pager) {
|
||||||
|
setup_pager();
|
||||||
|
sigchain_push(SIGPIPE, SIG_IGN);
|
||||||
|
}
|
||||||
render_hunk(s, hunk, 0, colored, &s->buf);
|
render_hunk(s, hunk, 0, colored, &s->buf);
|
||||||
fputs(s->buf.buf, stdout);
|
fputs(s->buf.buf, stdout);
|
||||||
rendered_hunk_index = hunk_index;
|
rendered_hunk_index = hunk_index;
|
||||||
|
if (use_pager) {
|
||||||
|
sigchain_pop(SIGPIPE);
|
||||||
|
wait_for_pager();
|
||||||
|
use_pager = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
strbuf_reset(&s->buf);
|
strbuf_reset(&s->buf);
|
||||||
@@ -1682,8 +1693,9 @@ soft_increment:
|
|||||||
hunk->use = USE_HUNK;
|
hunk->use = USE_HUNK;
|
||||||
goto soft_increment;
|
goto soft_increment;
|
||||||
}
|
}
|
||||||
} else if (s->answer.buf[0] == 'p') {
|
} else if (ch == 'p') {
|
||||||
rendered_hunk_index = -1;
|
rendered_hunk_index = -1;
|
||||||
|
use_pager = (s->answer.buf[0] == 'P') ? 1 : 0;
|
||||||
} else if (s->answer.buf[0] == '?') {
|
} else if (s->answer.buf[0] == '?') {
|
||||||
const char *p = _(help_patch_remainder), *eol = p;
|
const char *p = _(help_patch_remainder), *eol = p;
|
||||||
|
|
||||||
|
|||||||
48
pager.c
48
pager.c
@@ -14,6 +14,7 @@ int pager_use_color = 1;
|
|||||||
|
|
||||||
static struct child_process pager_process;
|
static struct child_process pager_process;
|
||||||
static char *pager_program;
|
static char *pager_program;
|
||||||
|
static int old_fd1 = -1, old_fd2 = -1;
|
||||||
|
|
||||||
/* Is the value coming back from term_columns() just a guess? */
|
/* Is the value coming back from term_columns() just a guess? */
|
||||||
static int term_columns_guessed;
|
static int term_columns_guessed;
|
||||||
@@ -23,10 +24,11 @@ static void close_pager_fds(void)
|
|||||||
{
|
{
|
||||||
/* signal EOF to pager */
|
/* signal EOF to pager */
|
||||||
close(1);
|
close(1);
|
||||||
close(2);
|
if (old_fd2 != -1)
|
||||||
|
close(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wait_for_pager_atexit(void)
|
static void finish_pager(void)
|
||||||
{
|
{
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
@@ -34,8 +36,37 @@ static void wait_for_pager_atexit(void)
|
|||||||
finish_command(&pager_process);
|
finish_command(&pager_process);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wait_for_pager_atexit(void)
|
||||||
|
{
|
||||||
|
if (old_fd1 == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
finish_pager();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_for_pager(void)
|
||||||
|
{
|
||||||
|
if (old_fd1 == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
finish_pager();
|
||||||
|
sigchain_pop_common();
|
||||||
|
unsetenv("GIT_PAGER_IN_USE");
|
||||||
|
dup2(old_fd1, 1);
|
||||||
|
close(old_fd1);
|
||||||
|
old_fd1 = -1;
|
||||||
|
if (old_fd2 != -1) {
|
||||||
|
dup2(old_fd2, 2);
|
||||||
|
close(old_fd2);
|
||||||
|
old_fd2 = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void wait_for_pager_signal(int signo)
|
static void wait_for_pager_signal(int signo)
|
||||||
{
|
{
|
||||||
|
if (old_fd1 == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
close_pager_fds();
|
close_pager_fds();
|
||||||
finish_command_in_signal(&pager_process);
|
finish_command_in_signal(&pager_process);
|
||||||
sigchain_pop(signo);
|
sigchain_pop(signo);
|
||||||
@@ -111,6 +142,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)
|
|||||||
|
|
||||||
void setup_pager(void)
|
void setup_pager(void)
|
||||||
{
|
{
|
||||||
|
static int once = 0;
|
||||||
const char *pager = git_pager(isatty(1));
|
const char *pager = git_pager(isatty(1));
|
||||||
|
|
||||||
if (!pager)
|
if (!pager)
|
||||||
@@ -140,14 +172,20 @@ void setup_pager(void)
|
|||||||
die("unable to execute pager '%s'", pager);
|
die("unable to execute pager '%s'", pager);
|
||||||
|
|
||||||
/* original process continues, but writes to the pipe */
|
/* original process continues, but writes to the pipe */
|
||||||
|
old_fd1 = dup(1);
|
||||||
dup2(pager_process.in, 1);
|
dup2(pager_process.in, 1);
|
||||||
if (isatty(2))
|
if (isatty(2)) {
|
||||||
|
old_fd2 = dup(2);
|
||||||
dup2(pager_process.in, 2);
|
dup2(pager_process.in, 2);
|
||||||
|
}
|
||||||
close(pager_process.in);
|
close(pager_process.in);
|
||||||
|
|
||||||
/* this makes sure that the parent terminates after the pager */
|
|
||||||
sigchain_push_common(wait_for_pager_signal);
|
sigchain_push_common(wait_for_pager_signal);
|
||||||
atexit(wait_for_pager_atexit);
|
|
||||||
|
if (!once) {
|
||||||
|
once++;
|
||||||
|
atexit(wait_for_pager_atexit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int pager_in_use(void)
|
int pager_in_use(void)
|
||||||
|
|||||||
1
pager.h
1
pager.h
@@ -5,6 +5,7 @@ struct child_process;
|
|||||||
|
|
||||||
const char *git_pager(int stdout_is_tty);
|
const char *git_pager(int stdout_is_tty);
|
||||||
void setup_pager(void);
|
void setup_pager(void);
|
||||||
|
void wait_for_pager(void);
|
||||||
int pager_in_use(void);
|
int pager_in_use(void);
|
||||||
int term_columns(void);
|
int term_columns(void);
|
||||||
void term_clear_line(void);
|
void term_clear_line(void);
|
||||||
|
|||||||
@@ -575,6 +575,54 @@ test_expect_success 'navigate to hunk via regex / pattern' '
|
|||||||
test_cmp expect actual.trimmed
|
test_cmp expect actual.trimmed
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'print again the hunk' '
|
||||||
|
test_when_finished "git reset" &&
|
||||||
|
tr _ " " >expect <<-EOF &&
|
||||||
|
+15
|
||||||
|
20
|
||||||
|
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
|
||||||
|
10
|
||||||
|
+15
|
||||||
|
20
|
||||||
|
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
|
||||||
|
EOF
|
||||||
|
test_write_lines s y g 1 p | git add -p >actual &&
|
||||||
|
tail -n 7 <actual >actual.trimmed &&
|
||||||
|
test_cmp expect actual.trimmed
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success TTY 'print again the hunk (PAGER)' '
|
||||||
|
test_when_finished "git reset" &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
<GREEN>+<RESET><GREEN>15<RESET>
|
||||||
|
20<RESET>
|
||||||
|
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
|
||||||
|
PAGER 10<RESET>
|
||||||
|
PAGER <GREEN>+<RESET><GREEN>15<RESET>
|
||||||
|
PAGER 20<RESET>
|
||||||
|
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
|
||||||
|
EOF
|
||||||
|
test_write_lines s y g 1 P |
|
||||||
|
(
|
||||||
|
GIT_PAGER="sed s/^/PAGER\ /" &&
|
||||||
|
export GIT_PAGER &&
|
||||||
|
test_terminal git add -p >actual
|
||||||
|
) &&
|
||||||
|
tail -n 7 <actual | test_decode_color >actual.trimmed &&
|
||||||
|
test_cmp expect actual.trimmed
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success TTY 'P handles SIGPIPE when writing to pager' '
|
||||||
|
test_when_finished "rm -f huge_file; git reset" &&
|
||||||
|
printf "\n%2500000s" Y >huge_file &&
|
||||||
|
git add -N huge_file &&
|
||||||
|
test_write_lines P q | (
|
||||||
|
GIT_PAGER="head -n 1" &&
|
||||||
|
export GIT_PAGER &&
|
||||||
|
test_terminal git add -p
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'split hunk "add -p (edit)"' '
|
test_expect_success 'split hunk "add -p (edit)"' '
|
||||||
# Split, say Edit and do nothing. Then:
|
# Split, say Edit and do nothing. Then:
|
||||||
#
|
#
|
||||||
|
|||||||
Reference in New Issue
Block a user