Skip to content

Commit d70f320

Browse files
committed
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
2 parents f250b51 + fc87b2f commit d70f320

File tree

4 files changed

+107
-8
lines changed

4 files changed

+107
-8
lines changed

add-patch.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
#include "environment.h"
88
#include "gettext.h"
99
#include "object-name.h"
10+
#include "pager.h"
1011
#include "read-cache-ll.h"
1112
#include "repository.h"
1213
#include "strbuf.h"
14+
#include "sigchain.h"
1315
#include "run-command.h"
1416
#include "strvec.h"
1517
#include "pathspec.h"
@@ -1398,7 +1400,7 @@ N_("j - leave this hunk undecided, see next undecided hunk\n"
13981400
"/ - search for a hunk matching the given regex\n"
13991401
"s - split the current hunk into smaller hunks\n"
14001402
"e - manually edit the current hunk\n"
1401-
"p - print the current hunk\n"
1403+
"p - print the current hunk, 'P' to use the pager\n"
14021404
"? - print help\n");
14031405

14041406
static int patch_update_file(struct add_p_state *s,
@@ -1409,7 +1411,7 @@ static int patch_update_file(struct add_p_state *s,
14091411
struct hunk *hunk;
14101412
char ch;
14111413
struct child_process cp = CHILD_PROCESS_INIT;
1412-
int colored = !!s->colored.len, quit = 0;
1414+
int colored = !!s->colored.len, quit = 0, use_pager = 0;
14131415
enum prompt_mode_type prompt_mode_type;
14141416
enum {
14151417
ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
@@ -1459,9 +1461,18 @@ static int patch_update_file(struct add_p_state *s,
14591461
strbuf_reset(&s->buf);
14601462
if (file_diff->hunk_nr) {
14611463
if (rendered_hunk_index != hunk_index) {
1464+
if (use_pager) {
1465+
setup_pager();
1466+
sigchain_push(SIGPIPE, SIG_IGN);
1467+
}
14621468
render_hunk(s, hunk, 0, colored, &s->buf);
14631469
fputs(s->buf.buf, stdout);
14641470
rendered_hunk_index = hunk_index;
1471+
if (use_pager) {
1472+
sigchain_pop(SIGPIPE);
1473+
wait_for_pager();
1474+
use_pager = 0;
1475+
}
14651476
}
14661477

14671478
strbuf_reset(&s->buf);
@@ -1682,8 +1693,9 @@ static int patch_update_file(struct add_p_state *s,
16821693
hunk->use = USE_HUNK;
16831694
goto soft_increment;
16841695
}
1685-
} else if (s->answer.buf[0] == 'p') {
1696+
} else if (ch == 'p') {
16861697
rendered_hunk_index = -1;
1698+
use_pager = (s->answer.buf[0] == 'P') ? 1 : 0;
16871699
} else if (s->answer.buf[0] == '?') {
16881700
const char *p = _(help_patch_remainder), *eol = p;
16891701

pager.c

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ int pager_use_color = 1;
1414

1515
static struct child_process pager_process;
1616
static char *pager_program;
17+
static int old_fd1 = -1, old_fd2 = -1;
1718

1819
/* Is the value coming back from term_columns() just a guess? */
1920
static int term_columns_guessed;
@@ -23,19 +24,49 @@ static void close_pager_fds(void)
2324
{
2425
/* signal EOF to pager */
2526
close(1);
26-
close(2);
27+
if (old_fd2 != -1)
28+
close(2);
2729
}
2830

29-
static void wait_for_pager_atexit(void)
31+
static void finish_pager(void)
3032
{
3133
fflush(stdout);
3234
fflush(stderr);
3335
close_pager_fds();
3436
finish_command(&pager_process);
3537
}
3638

39+
static void wait_for_pager_atexit(void)
40+
{
41+
if (old_fd1 == -1)
42+
return;
43+
44+
finish_pager();
45+
}
46+
47+
void wait_for_pager(void)
48+
{
49+
if (old_fd1 == -1)
50+
return;
51+
52+
finish_pager();
53+
sigchain_pop_common();
54+
unsetenv("GIT_PAGER_IN_USE");
55+
dup2(old_fd1, 1);
56+
close(old_fd1);
57+
old_fd1 = -1;
58+
if (old_fd2 != -1) {
59+
dup2(old_fd2, 2);
60+
close(old_fd2);
61+
old_fd2 = -1;
62+
}
63+
}
64+
3765
static void wait_for_pager_signal(int signo)
3866
{
67+
if (old_fd1 == -1)
68+
return;
69+
3970
close_pager_fds();
4071
finish_command_in_signal(&pager_process);
4172
sigchain_pop(signo);
@@ -111,6 +142,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)
111142

112143
void setup_pager(void)
113144
{
145+
static int once = 0;
114146
const char *pager = git_pager(isatty(1));
115147

116148
if (!pager)
@@ -140,14 +172,20 @@ void setup_pager(void)
140172
die("unable to execute pager '%s'", pager);
141173

142174
/* original process continues, but writes to the pipe */
175+
old_fd1 = dup(1);
143176
dup2(pager_process.in, 1);
144-
if (isatty(2))
177+
if (isatty(2)) {
178+
old_fd2 = dup(2);
145179
dup2(pager_process.in, 2);
180+
}
146181
close(pager_process.in);
147182

148-
/* this makes sure that the parent terminates after the pager */
149183
sigchain_push_common(wait_for_pager_signal);
150-
atexit(wait_for_pager_atexit);
184+
185+
if (!once) {
186+
once++;
187+
atexit(wait_for_pager_atexit);
188+
}
151189
}
152190

153191
int pager_in_use(void)

pager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ struct child_process;
55

66
const char *git_pager(int stdout_is_tty);
77
void setup_pager(void);
8+
void wait_for_pager(void);
89
int pager_in_use(void);
910
int term_columns(void);
1011
void term_clear_line(void);

t/t3701-add-interactive.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,54 @@ test_expect_success 'navigate to hunk via regex / pattern' '
575575
test_cmp expect actual.trimmed
576576
'
577577

578+
test_expect_success 'print again the hunk' '
579+
test_when_finished "git reset" &&
580+
tr _ " " >expect <<-EOF &&
581+
+15
582+
20
583+
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@
584+
10
585+
+15
586+
20
587+
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
588+
EOF
589+
test_write_lines s y g 1 p | git add -p >actual &&
590+
tail -n 7 <actual >actual.trimmed &&
591+
test_cmp expect actual.trimmed
592+
'
593+
594+
test_expect_success TTY 'print again the hunk (PAGER)' '
595+
test_when_finished "git reset" &&
596+
cat >expect <<-EOF &&
597+
<GREEN>+<RESET><GREEN>15<RESET>
598+
20<RESET>
599+
<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>
600+
PAGER 10<RESET>
601+
PAGER <GREEN>+<RESET><GREEN>15<RESET>
602+
PAGER 20<RESET>
603+
<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
604+
EOF
605+
test_write_lines s y g 1 P |
606+
(
607+
GIT_PAGER="sed s/^/PAGER\ /" &&
608+
export GIT_PAGER &&
609+
test_terminal git add -p >actual
610+
) &&
611+
tail -n 7 <actual | test_decode_color >actual.trimmed &&
612+
test_cmp expect actual.trimmed
613+
'
614+
615+
test_expect_success TTY 'P handles SIGPIPE when writing to pager' '
616+
test_when_finished "rm -f huge_file; git reset" &&
617+
printf "\n%2500000s" Y >huge_file &&
618+
git add -N huge_file &&
619+
test_write_lines P q | (
620+
GIT_PAGER="head -n 1" &&
621+
export GIT_PAGER &&
622+
test_terminal git add -p
623+
)
624+
'
625+
578626
test_expect_success 'split hunk "add -p (edit)"' '
579627
# Split, say Edit and do nothing. Then:
580628
#

0 commit comments

Comments
 (0)