{"thread":{"id":"61514","subject":"[PATCH 0/5] use the pager in 'add -p'","startedAt":"2024-05-19T07:07:05Z","lastAt":"2024-06-10T21:16:42Z","messageCount":113,"participants":["Rubén Justo","Junio C Hamano","Dragan Simic","Jeff King","Phillip Wood","phillip.wood123@gmail.com"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"495053","messageId":"1d0cb55c-5f32-419a-b593-d5f0969a51fd@gmail.com","threadId":"61514","inReplyTo":null,"subject":"[PATCH 0/5] use the pager in 'add -p'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-19T07:06:59Z","receivedAt":"2024-05-19T07:07:05Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Invoke the pager when displaying hunks during \"add -p\" sessions, to make\nit easier for the user to review hunks longer than one screen height.\n\nRubén Justo (5):\n  add-patch: test for 'p' command\n  pager: do not close fd 2 unnecessarily\n  pager: introduce wait_for_pager\n  test-terminal: introduce --no-stdin-pty\n  add-patch: render hunks through the pager\n\n add-patch.c                |  3 +++\n pager.c                    | 41 ++++++++++++++++++++++++++++++++------\n pager.h                    |  1 +\n t/t3701-add-interactive.sh | 37 ++++++++++++++++++++++++++++++++++\n t/test-terminal.perl       | 32 ++++++++++++++++-------------\n 5 files changed, 94 insertions(+), 20 deletions(-)\n\n-- \n2.45.1.209.gd5886bf9cd\n"},{"id":"495055","messageId":"fc307b09-29ae-4013-b5b9-580b6cff5445@gmail.com","threadId":"61514","inReplyTo":"1d0cb55c-5f32-419a-b593-d5f0969a51fd@gmail.com","subject":"[PATCH 1/5] add-patch: test for 'p' command","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-19T07:10:16Z","receivedAt":"2024-05-19T07:10:22Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Add a test for the 'p' command, which was introduced in 66c14ab592\n(add-patch: introduce 'p' in interactive-patch, 2024-03-29).\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n t/t3701-add-interactive.sh | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 28a95a775d..52d7830de2 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -542,6 +542,22 @@ test_expect_success 'goto hunk' '\n \ttest_cmp expect actual.trimmed\n '\n \n+test_expect_success 'print again the hunk' '\n+\ttest_when_finished \"git reset\" &&\n+\ttr _ \" \" >expect <<-EOF &&\n+\t+15\n+\t 20\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@\n+\t 10\n+\t+15\n+\t 20\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\tEOF\n+\ttest_write_lines s y g 1 p | git add -p >actual &&\n+\ttail -n 7 <actual >actual.trimmed &&\n+\ttest_cmp expect actual.trimmed\n+'\n+\n test_expect_success 'navigate to hunk via regex' '\n \ttest_when_finished \"git reset\" &&\n \ttr _ \" \" >expect <<-EOF &&\n-- \n2.45.1.209.gd5886bf9cd\n"},{"id":"495056","messageId":"80f15223-246e-4cfb-a139-e47af829c938@gmail.com","threadId":"61514","inReplyTo":"1d0cb55c-5f32-419a-b593-d5f0969a51fd@gmail.com","subject":"[PATCH 2/5] pager: do not close fd 2 unnecessarily","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-19T07:12:31Z","receivedAt":"2024-05-19T07:12:36Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"We send errors to the pager since 61b80509e3 (sending errors to stdout\nunder $PAGER, 2008-02-16).\n\nIn a8335024c2 (pager: do not dup2 stderr if it is already redirected,\n2008-12-15) an exception was introduced to avoid redirecting stderr if\nit is not connected to a terminal.\n\nIn such exceptional cases, the close(STDERR_FILENO) we're doing in\nclose_pager_fds, is unnecessary.\n\nFurthermore, in a subsequent commit we're going to introduce changes\nthat might call close_pager_fds multiple times.  With this in mind,\nunconditionally closing stderr will become undesirable.\n\nLet's close(STDERR_FILENO) only when necessary, and pave the way for\nthe coming changes.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n pager.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex b8822a9381..3ef6798f7e 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -14,6 +14,7 @@ int pager_use_color = 1;\n \n static struct child_process pager_process;\n static const char *pager_program;\n+static int old_fd2 = -1;\n \n /* Is the value coming back from term_columns() just a guess? */\n static int term_columns_guessed;\n@@ -23,7 +24,8 @@ static void close_pager_fds(void)\n {\n \t/* signal EOF to pager */\n \tclose(1);\n-\tclose(2);\n+\tif (old_fd2 != -1)\n+\t\tclose(2);\n }\n \n static void wait_for_pager_atexit(void)\n@@ -141,8 +143,10 @@ void setup_pager(void)\n \n \t/* original process continues, but writes to the pipe */\n \tdup2(pager_process.in, 1);\n-\tif (isatty(2))\n+\tif (isatty(2)) {\n+\t\told_fd2 = 1;\n \t\tdup2(pager_process.in, 2);\n+\t}\n \tclose(pager_process.in);\n \n \t/* this makes sure that the parent terminates after the pager */\n-- \n2.45.1.209.gd5886bf9cd\n"},{"id":"495057","messageId":"d090a84c-6bdc-454b-98d5-4310f8b73697@gmail.com","threadId":"61514","inReplyTo":"1d0cb55c-5f32-419a-b593-d5f0969a51fd@gmail.com","subject":"[PATCH 3/5] pager: introduce wait_for_pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-19T07:13:09Z","receivedAt":"2024-05-19T07:13:14Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Since f67b45f862 (Introduce trivial new pager.c helper infrastructure,\n2006-02-28) we have the machinery to send our output to a pager.\n\nThat machinery, once set up, does not allow us to regain the original\nstdio streams.\n\nIn the interactive commands (i.e.: add -p) we want to use the pager for\nsome output, while maintaining the interaction with the user.\n\nModify the pager machinery so that we can use setup_pager and, once\nwe've finished sending the desired output for the pager, wait for the\npager termination using a new function wait_for_pager.  Make this\nfunction reset the pager machinery before returning.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n pager.c | 37 +++++++++++++++++++++++++++++++------\n pager.h |  1 +\n 2 files changed, 32 insertions(+), 6 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex 3ef6798f7e..2fa06c43c4 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -14,12 +14,11 @@ int pager_use_color = 1;\n \n static struct child_process pager_process;\n static const char *pager_program;\n-static int old_fd2 = -1;\n+static int old_fd1 = -1, old_fd2 = -1;\n \n /* Is the value coming back from term_columns() just a guess? */\n static int term_columns_guessed;\n \n-\n static void close_pager_fds(void)\n {\n \t/* signal EOF to pager */\n@@ -30,14 +29,35 @@ static void close_pager_fds(void)\n \n static void wait_for_pager_atexit(void)\n {\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n \tfflush(stdout);\n \tfflush(stderr);\n \tclose_pager_fds();\n \tfinish_command(&pager_process);\n }\n \n+void wait_for_pager(void)\n+{\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n+\twait_for_pager_atexit();\n+\tunsetenv(\"GIT_PAGER_IN_USE\");\n+\tdup2(old_fd1, 1);\n+\told_fd1 = -1;\n+\tif (old_fd2 != -1) {\n+\t\tdup2(old_fd2, 2);\n+\t\told_fd2 = -1;\n+\t}\n+}\n+\n static void wait_for_pager_signal(int signo)\n {\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n \tclose_pager_fds();\n \tfinish_command_in_signal(&pager_process);\n \tsigchain_pop(signo);\n@@ -113,11 +133,14 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)\n \n void setup_pager(void)\n {\n+\tstatic int once = 0;\n \tconst char *pager = git_pager(isatty(1));\n \n \tif (!pager)\n \t\treturn;\n \n+\tassert(old_fd1 == -1);\n+\n \t/*\n \t * After we redirect standard output, we won't be able to use an ioctl\n \t * to get the terminal size. Let's grab it now, and then set $COLUMNS\n@@ -142,16 +165,18 @@ void setup_pager(void)\n \t\treturn;\n \n \t/* original process continues, but writes to the pipe */\n+\told_fd1 = dup(1);\n \tdup2(pager_process.in, 1);\n \tif (isatty(2)) {\n-\t\told_fd2 = 1;\n+\t\told_fd2 = dup(2);\n \t\tdup2(pager_process.in, 2);\n \t}\n \tclose(pager_process.in);\n \n-\t/* this makes sure that the parent terminates after the pager */\n-\tsigchain_push_common(wait_for_pager_signal);\n-\tatexit(wait_for_pager_atexit);\n+\tif (!once++) {\n+\t\tsigchain_push_common(wait_for_pager_signal);\n+\t\tatexit(wait_for_pager_atexit);\n+\t}\n }\n \n int pager_in_use(void)\ndiff --git a/pager.h b/pager.h\nindex b77433026d..103ecac476 100644\n--- a/pager.h\n+++ b/pager.h\n@@ -5,6 +5,7 @@ struct child_process;\n \n const char *git_pager(int stdout_is_tty);\n void setup_pager(void);\n+void wait_for_pager(void);\n int pager_in_use(void);\n int term_columns(void);\n void term_clear_line(void);\n-- \n2.45.1.209.gd5886bf9cd\n"},{"id":"495058","messageId":"d42a55b1-1ba9-4cfb-9c3d-98ea4d86da33@gmail.com","threadId":"61514","inReplyTo":"1d0cb55c-5f32-419a-b593-d5f0969a51fd@gmail.com","subject":"[PATCH 4/5] test-terminal: introduce --no-stdin-pty","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-19T07:14:31Z","receivedAt":"2024-05-19T07:14:37Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"In 18d8c26930 (test_terminal: redirect child process' stdin to a pty,\n2015-08-04), t/test-terminal.perl learned to connect the child process'\nstdin to a pty.  It works well for what was intended: satisfying an\n`isatty(STDIN_FILENO)` check.\n\nHowever, the fork introduced, that copies the stdin to the child\nprocess, does not always manage to send all the information.\n\nTo illustrate this behavior, we can use a function like this:\n\n    f ()\n    {\n    \tdd if=/dev/zero bs=1 count=10000 status=none |\n    \tt/test-terminal.perl cat - 2>/dev/null |\n    \twc -c;\n    }\n\nWe do not obtain the expected results when executing this function\n100 times:\n\n    $ for i in $(seq 100); do f; done | sort | uniq -c\n         36 0\n          4 1\n         53 4095\n          7 4159\n\nIf we do the same with a version that does not redirect stdin, a version\nprior to 18d8c26930, the expected result is obtained:\n\n    $ git checkout 18d8c26930~1\n    $ for i in $(seq 100); do f; done | sort | uniq -c\n        100 10000\n\nIn a subsequent commit, a new test is going to rely on test-terminate,\nand it does not require stdin to be connected to a terminal, but all\npiped data needs to be successfully transmitted to the child process.\n\nTo make this possible, add a new parameter \"--no-stdin-pty\" to allow\ndisabling the stdin redirection though a pty.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n t/test-terminal.perl | 32 ++++++++++++++++++--------------\n 1 file changed, 18 insertions(+), 14 deletions(-)\n\ndiff --git a/t/test-terminal.perl b/t/test-terminal.perl\nindex 3810e9bb43..85edc9e8b9 100755\n--- a/t/test-terminal.perl\n+++ b/t/test-terminal.perl\n@@ -12,10 +12,10 @@ sub start_child {\n \tif (not defined $pid) {\n \t\tdie \"fork failed: $!\"\n \t} elsif ($pid == 0) {\n-\t\topen STDIN, \"<&\", $in;\n+\t\topen STDIN, \"<&\", $in if $in;\n \t\topen STDOUT, \">&\", $out;\n \t\topen STDERR, \">&\", $err;\n-\t\tclose $in;\n+\t\tclose $in if $in;\n \t\tclose $out;\n \t\texec(@$argv) or die \"cannot exec '$argv->[0]': $!\"\n \t}\n@@ -78,28 +78,32 @@ sub copy_stdio {\n }\n \n if ($#ARGV < 1) {\n-\tdie \"usage: test-terminal program args\";\n+\tdie \"usage: test-terminal [--no-stdin-pty] program args\";\n }\n+my $no_stdin_pty = $ARGV[0] eq '--no-stdin-pty';\n+shift @ARGV if $no_stdin_pty;\n $ENV{TERM} = 'vt100';\n-my $parent_in = new IO::Pty;\n+my $parent_in = $no_stdin_pty ? undef : IO::Pty->new;\n my $parent_out = new IO::Pty;\n my $parent_err = new IO::Pty;\n-$parent_in->set_raw();\n+$parent_in->set_raw() if $parent_in;\n $parent_out->set_raw();\n $parent_err->set_raw();\n-$parent_in->slave->set_raw();\n+$parent_in->slave->set_raw() if $parent_in;\n $parent_out->slave->set_raw();\n $parent_err->slave->set_raw();\n-my $pid = start_child(\\@ARGV, $parent_in->slave, $parent_out->slave, $parent_err->slave);\n-close $parent_in->slave;\n+my $pid = start_child(\\@ARGV,$parent_in ? $parent_in->slave : undef, $parent_out->slave, $parent_err->slave);\n+close $parent_in->slave if $parent_in;\n close $parent_out->slave;\n close $parent_err->slave;\n-my $in_pid = copy_stdin($parent_in);\n+my $in_pid = $no_stdin_pty ? 0 : copy_stdin($parent_in);\n copy_stdio($parent_out, $parent_err);\n my $ret = finish_child($pid);\n-# If the child process terminates before our copy_stdin() process is able to\n-# write all of its data to $parent_in, the copy_stdin() process could stall.\n-# Send SIGTERM to it to ensure it terminates.\n-kill 'TERM', $in_pid;\n-finish_child($in_pid);\n+if ($in_pid) {\n+\t# If the child process terminates before our copy_stdin() process is able to\n+\t# write all of its data to $parent_in, the copy_stdin() process could stall.\n+\t# Send SIGTERM to it to ensure it terminates.\n+\tkill 'TERM', $in_pid;\n+\tfinish_child($in_pid);\n+}\n exit($ret);\n-- \n2.45.1.209.gd5886bf9cd\n"},{"id":"495059","messageId":"eb0438e8-d7b6-478f-b2be-336e83f5d9ab@gmail.com","threadId":"61514","inReplyTo":"1d0cb55c-5f32-419a-b593-d5f0969a51fd@gmail.com","subject":"[PATCH 5/5] add-patch: render hunks through the pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-19T07:14:58Z","receivedAt":"2024-05-19T07:15:03Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Invoke the pager when displaying hunks during \"add -p\" sessions, to make\nit easier for the user to review hunks longer than one screen height.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n add-patch.c                |  3 +++\n t/t3701-add-interactive.sh | 21 +++++++++++++++++++++\n 2 files changed, 24 insertions(+)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 2252895c28..cefa3941a3 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -5,6 +5,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"object-name.h\"\n+#include \"pager.h\"\n #include \"read-cache-ll.h\"\n #include \"repository.h\"\n #include \"strbuf.h\"\n@@ -1448,9 +1449,11 @@ static int patch_update_file(struct add_p_state *s,\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n \t\t\tif (rendered_hunk_index != hunk_index) {\n+\t\t\t\tsetup_pager();\n \t\t\t\trender_hunk(s, hunk, 0, colored, &s->buf);\n \t\t\t\tfputs(s->buf.buf, stdout);\n \t\t\t\trendered_hunk_index = hunk_index;\n+\t\t\t\twait_for_pager();\n \t\t\t}\n \n \t\t\tstrbuf_reset(&s->buf);\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 52d7830de2..6c4af8904e 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -558,6 +558,27 @@ test_expect_success 'print again the hunk' '\n \ttest_cmp expect actual.trimmed\n '\n \n+test_expect_success TTY 'print again the hunk (PAGER)' '\n+\ttest_when_finished \"git reset\" &&\n+\tcat >expect <<-EOF &&\n+\tPAGER <GREEN>+<RESET><GREEN>15<RESET>\n+\tPAGER  20<RESET>\n+\t<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>\n+\tPAGER  10<RESET>\n+\tPAGER <GREEN>+<RESET><GREEN>15<RESET>\n+\tPAGER  20<RESET>\n+\t<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>\n+\tEOF\n+\ttest_write_lines s y g 1 p |\n+\t(\n+\t\tGIT_PAGER=\"sed s/^/PAGER\\ /\" &&\n+\t\texport GIT_PAGER &&\n+\t\ttest_terminal --no-stdin-pty git add -p >actual\n+\t) &&\n+\ttail -n 7 <actual | test_decode_color >actual.trimmed &&\n+\ttest_cmp expect actual.trimmed\n+'\n+\n test_expect_success 'navigate to hunk via regex' '\n \ttest_when_finished \"git reset\" &&\n \ttr _ \" \" >expect <<-EOF &&\n-- \n2.45.1.209.gd5886bf9cd\n"},{"id":"495101","messageId":"xmqqo790fg8z.fsf@gitster.g","threadId":"61514","inReplyTo":"80f15223-246e-4cfb-a139-e47af829c938@gmail.com","subject":"Re: [PATCH 2/5] pager: do not close fd 2 unnecessarily","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-20T19:14:04Z","receivedAt":"2024-05-20T19:14:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n> We send errors to the pager since 61b80509e3 (sending errors to stdout\n> under $PAGER, 2008-02-16).\n>\n> In a8335024c2 (pager: do not dup2 stderr if it is already redirected,\n> 2008-12-15) an exception was introduced to avoid redirecting stderr if\n> it is not connected to a terminal.\n>\n> In such exceptional cases, the close(STDERR_FILENO) we're doing in\n> close_pager_fds, is unnecessary.\n\n\n> Furthermore, in a subsequent commit we're going to introduce changes\n> that might call close_pager_fds multiple times.  With this in mind,\n> unconditionally closing stderr will become undesirable.\n\nIn a new world with such a change, what does it mean to call\nclose_pager_fds()?  It used to mean \"we are really done with the\npager and we no longer need them, ever\".\n\nAnd we still call the helper for that purpose after this change,\nfrom wait_for_pager_atexit() and wait_for_pager_signal().\n\nSo no matter what \"a subsequent commit\" does, it feels conceptually\nwrong to call it more than once in the first place.  In other words,\nwhat is wrong is that this function closes stderr, but \"a subsequent\ncommit\" calls this function multiple times, no?\n\n>  static struct child_process pager_process;\n>  static const char *pager_program;\n> +static int old_fd2 = -1;\n\nWhat does the magic number \"-1\" mean?  We often use it to signal\n\"uninitialized\", but then what are concrete \"initialized\" values\nmean?  \"We dup2()'ed something else to stderr/fd #2 but before doing\nso we saved the original fd #2 away to this variable, so that we can\nrestore fd #2 by another dup2() of the value of this variable when\nwe declare that we are done with the standard error stream\"?\n\nBut that does not look like what is happening here.\n\n>  /* Is the value coming back from term_columns() just a guess? */\n>  static int term_columns_guessed;\n> @@ -23,7 +24,8 @@ static void close_pager_fds(void)\n>  {\n>  \t/* signal EOF to pager */\n>  \tclose(1);\n> -\tclose(2);\n> +\tif (old_fd2 != -1)\n> +\t\tclose(2);\n>  }\n>  \n>  static void wait_for_pager_atexit(void)\n> @@ -141,8 +143,10 @@ void setup_pager(void)\n>  \n>  \t/* original process continues, but writes to the pipe */\n>  \tdup2(pager_process.in, 1);\n> -\tif (isatty(2))\n> +\tif (isatty(2)) {\n> +\t\told_fd2 = 1;\n\nEqually unclear magic number \"1\" is used here.\n\nThis value is different from pager_process.in, and my earlier \"we\nare saving away\" does not apply, either.\n\n>  \t\tdup2(pager_process.in, 2);\n> +\t}\n>  \tclose(pager_process.in);\n\nPuzzled...\n"},{"id":"495106","messageId":"xmqqh6esffh1.fsf@gitster.g","threadId":"61514","inReplyTo":"eb0438e8-d7b6-478f-b2be-336e83f5d9ab@gmail.com","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-20T19:30:50Z","receivedAt":"2024-05-20T19:30:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n> Invoke the pager when displaying hunks during \"add -p\" sessions, to make\n> it easier for the user to review hunks longer than one screen height.\n\nIf the hunk fits on one screen, it is annoying to see a pager\ninvoked and then torn down immediately, even with \"less -F\"\n(--quit-if-one-screen).  As we know how much output we are throwing\nat the user, we'd want to make this conditional to the size of the\nhunk being shown and the terminal height.\n\nOr perhaps show them without such a trick, and add a new variant to\n'p' that allows the user to request the output be sent to a pager\n(perhaps 'P')?  It would certainly be an alternative with much\nsmaller damage.  The existing end-user experience would not degrade,\nbut when the user wants to see a huge hunk, they can send it to the\npager.\n\nAnother, ulteriour, motive here behind this suggestion is to\nencourage users to work with smaller hunks.  Being able to scroll\naround and view lines on demand (i.e. use of pager) is one thing.\nBeing able to view all relevant lines at once (i.e. not wasting\nvertical screen real estate and making things fit on one screen) is\nvery different and much nicer.\n\n\n"},{"id":"495108","messageId":"ec5d73e22a6e4587f3d87314a9c0e422@manjaro.org","threadId":"61514","inReplyTo":"xmqqh6esffh1.fsf@gitster.g","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-05-20T19:45:51Z","receivedAt":"2024-05-20T19:45:53Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"Hello Junio and Ruben,\n\nOn 2024-05-20 21:30, Junio C Hamano wrote:\n> Rubén Justo <rjusto@gmail.com> writes:\n> \n>> Invoke the pager when displaying hunks during \"add -p\" sessions, to \n>> make\n>> it easier for the user to review hunks longer than one screen height.\n> \n> If the hunk fits on one screen, it is annoying to see a pager\n> invoked and then torn down immediately, even with \"less -F\"\n> (--quit-if-one-screen).  As we know how much output we are throwing\n> at the user, we'd want to make this conditional to the size of the\n> hunk being shown and the terminal height.\n> \n> Or perhaps show them without such a trick, and add a new variant to\n> 'p' that allows the user to request the output be sent to a pager\n> (perhaps 'P')?  It would certainly be an alternative with much\n> smaller damage.  The existing end-user experience would not degrade,\n> but when the user wants to see a huge hunk, they can send it to the\n> pager.\n> \n> Another, ulteriour, motive here behind this suggestion is to\n> encourage users to work with smaller hunks.  Being able to scroll\n> around and view lines on demand (i.e. use of pager) is one thing.\n> Being able to view all relevant lines at once (i.e. not wasting\n> vertical screen real estate and making things fit on one screen) is\n> very different and much nicer.\n\nThere's another thing to consider, which makes the introduction of\n\"P\" as the new option even more desirable.  Let me explain.\n\nWith the upcoming changes to the way less(1) as the pager works,\nwhich was already discussed at length and even required new features\nto be implemented in less(1), [1] displaying anything through less(1)\nwill not leave an accessible scrollback in the terminal emulator.\nOnly one screen worth of text will be displayed, even after quitting\nless(1).  That's what we have to do, to fix age-old issues with the\npager-generated scrollback that easily gets corrupted and actually\nbecomes misleading.\n\nThus, if someone wants to have a complete longer-than-one-screen hunk\ndisplayed and use the terminal emulator scrollback to inspect the\nhunk in its entirety, passing such (or all) hunks through the pager\nwould make such inspection impossible.  I'd assume that at least some\nGit users already do that (I do, for example), and we surely don't want\nto make that no longer possible.  That's why introducing \"P\" as the\nnew option would be the desired approach.\n\n[1] \nhttps://lore.kernel.org/git/8289ef15266172cbfa10bb146afe9797@manjaro.org/\n"},{"id":"495122","messageId":"a9f199d8-bb06-479f-88c2-63d80338a4e9@gmail.com","threadId":"61514","inReplyTo":"xmqqo790fg8z.fsf@gitster.g","subject":"Re: [PATCH 2/5] pager: do not close fd 2 unnecessarily","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-20T22:33:46Z","receivedAt":"2024-05-20T22:33:49Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Mon, May 20, 2024 at 12:14:04PM -0700, Junio C Hamano wrote:\n> Rubén Justo <rjusto@gmail.com> writes:\n> \n> > We send errors to the pager since 61b80509e3 (sending errors to stdout\n> > under $PAGER, 2008-02-16).\n> >\n> > In a8335024c2 (pager: do not dup2 stderr if it is already redirected,\n> > 2008-12-15) an exception was introduced to avoid redirecting stderr if\n> > it is not connected to a terminal.\n> >\n> > In such exceptional cases, the close(STDERR_FILENO) we're doing in\n> > close_pager_fds, is unnecessary.\n> \n> \n> > Furthermore, in a subsequent commit we're going to introduce changes\n> > that might call close_pager_fds multiple times.  With this in mind,\n> > unconditionally closing stderr will become undesirable.\n> \n> In a new world with such a change, what does it mean to call\n> close_pager_fds()?\n\nThere is no change in what calling close_pager_fds() means.\n\n> It used to mean \"we are really done with the\n> pager and we no longer need them, ever\".\n> \n> And we still call the helper for that purpose after this change,\n> from wait_for_pager_atexit() and wait_for_pager_signal().\n\nYes, no change here either.  In the next commit, a new client of the\nhelper is introduced, the new API: wait_for_pager().\n\n> \n> So no matter what \"a subsequent commit\" does, it feels conceptually\n> wrong to call it more than once in the first place.\n> In other words,\n> what is wrong is that this function closes stderr, but \"a subsequent\n> commit\" calls this function multiple times, no?\n\nThis series is trying to allow triggering the pager multiple times.\nReaching to close_pager_fds() multiple times is a consequence of it.\n\n> \n> >  static struct child_process pager_process;\n> >  static const char *pager_program;\n> > +static int old_fd2 = -1;\n> \n> What does the magic number \"-1\" mean?\n\nInvalid fd.\n\n> We often use it to signal\n> \"uninitialized\", but then what are concrete \"initialized\" values\n> mean?  \"We dup2()'ed something else to stderr/fd #2 but before doing\n> so we saved the original fd #2 away to this variable, so that we can\n> restore fd #2 by another dup2() of the value of this variable when\n> we declare that we are done with the standard error stream\"?\n> \n> But that does not look like what is happening here.\n> \n> >  /* Is the value coming back from term_columns() just a guess? */\n> >  static int term_columns_guessed;\n> > @@ -23,7 +24,8 @@ static void close_pager_fds(void)\n> >  {\n> >  \t/* signal EOF to pager */\n> >  \tclose(1);\n> > -\tclose(2);\n> > +\tif (old_fd2 != -1)\n> > +\t\tclose(2);\n> >  }\n> >  \n> >  static void wait_for_pager_atexit(void)\n> > @@ -141,8 +143,10 @@ void setup_pager(void)\n> >  \n> >  \t/* original process continues, but writes to the pipe */\n> >  \tdup2(pager_process.in, 1);\n> > -\tif (isatty(2))\n> > +\tif (isatty(2)) {\n> > +\t\told_fd2 = 1;\n> \n> Equally unclear magic number \"1\" is used here.\n> \n> This value is different from pager_process.in, and my earlier \"we\n> are saving away\" does not apply, either.\n\nIt applies, in 3/5.\n\n> \n> >  \t\tdup2(pager_process.in, 2);\n> > +\t}\n> >  \tclose(pager_process.in);\n> \n> Puzzled...\n\nThanks for reading the series.\n"},{"id":"495123","messageId":"83071f70-e8a1-41d6-9fb1-108a31602baa@gmail.com","threadId":"61514","inReplyTo":"ec5d73e22a6e4587f3d87314a9c0e422@manjaro.org","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-20T22:35:24Z","receivedAt":"2024-05-20T22:35:27Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Mon, May 20, 2024 at 09:45:51PM +0200, Dragan Simic wrote:\n\n> Thus, if someone wants to have a complete longer-than-one-screen hunk\n> displayed and use the terminal emulator scrollback to inspect the\n> hunk in its entirety, passing such (or all) hunks through the pager\n> would make such inspection impossible.  I'd assume that at least some\n> Git users already do that (I do, for example), and we surely don't want\n> to make that no longer possible.\n\nI'm not sure if I understand the problem... disabling the pager is\nstill an option, no?\n\n    $ git -P add -p\n\n    $ git -c pager.add=false add -p\n"},{"id":"495125","messageId":"dcc9f9bf-3c0f-435f-ba10-35ff31122b7d@gmail.com","threadId":"61514","inReplyTo":"xmqqh6esffh1.fsf@gitster.g","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-20T22:47:21Z","receivedAt":"2024-05-20T22:47:25Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Mon, May 20, 2024 at 12:30:50PM -0700, Junio C Hamano wrote:\n\n> > Invoke the pager when displaying hunks during \"add -p\" sessions, to make\n> > it easier for the user to review hunks longer than one screen height.\n> \n> If the hunk fits on one screen, it is annoying to see a pager\n> invoked and then torn down immediately,\n\nGood point.\n\n> even with \"less -F\"\n> (--quit-if-one-screen).  As we know how much output we are throwing\n> at the user, we'd want to make this conditional to the size of the\n> hunk being shown and the terminal height.\n\nAre you thinking of something like?:\n \ndiff --git a/add-patch.c b/add-patch.c\nindex cefa3941a3..495baad3ac 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1449,11 +1449,18 @@ static int patch_update_file(struct add_p_state *s,\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n \t\t\tif (rendered_hunk_index != hunk_index) {\n-\t\t\t\tsetup_pager();\n+\t\t\t\tint lines = 0;\n \t\t\t\trender_hunk(s, hunk, 0, colored, &s->buf);\n+\t\t\t\tfor(int i = 0; i < s->buf.len; i++) {\n+\t\t\t\t\tif (s->buf.buf[i] == '\\n')\n+\t\t\t\t\t\tlines++;\n+\t\t\t\t}\n+\t\t\t\tif (lines > term_columns())\n+\t\t\t\t\tsetup_pager();\n \t\t\t\tfputs(s->buf.buf, stdout);\n \t\t\t\trendered_hunk_index = hunk_index;\n-\t\t\t\twait_for_pager();\n+\t\t\t\tif (lines > term_columns())\n+\t\t\t\t\twait_for_pager();\n \t\t\t}\n \n \t\t\tstrbuf_reset(&s->buf);\n\nThis would significantly reduce the blast radius.\n\nThanks.\n"},{"id":"495132","messageId":"xmqq5xv8dqd2.fsf@gitster.g","threadId":"61514","inReplyTo":"dcc9f9bf-3c0f-435f-ba10-35ff31122b7d@gmail.com","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-20T23:18:33Z","receivedAt":"2024-05-20T23:18:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n>> even with \"less -F\"\n>> (--quit-if-one-screen).  As we know how much output we are throwing\n>> at the user, we'd want to make this conditional to the size of the\n>> hunk being shown and the terminal height.\n>\n> Are you thinking of something like?:\n\nI don't.  \n\nYour hunk may have overly wide lines in which case counting the\nnumber of lines may be insuffucient to measure the necessary display\nheight.   Besides, comparison with term_columns() is meaningless\nunless your window is square ;-)\n\nAn explicit 'P' might be palatable, though.\n\nThanks.\n\n>  \n> diff --git a/add-patch.c b/add-patch.c\n> index cefa3941a3..495baad3ac 100644\n> --- a/add-patch.c\n> +++ b/add-patch.c\n> @@ -1449,11 +1449,18 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\tstrbuf_reset(&s->buf);\n>  \t\tif (file_diff->hunk_nr) {\n>  \t\t\tif (rendered_hunk_index != hunk_index) {\n> -\t\t\t\tsetup_pager();\n> +\t\t\t\tint lines = 0;\n>  \t\t\t\trender_hunk(s, hunk, 0, colored, &s->buf);\n> +\t\t\t\tfor(int i = 0; i < s->buf.len; i++) {\n> +\t\t\t\t\tif (s->buf.buf[i] == '\\n')\n> +\t\t\t\t\t\tlines++;\n> +\t\t\t\t}\n> +\t\t\t\tif (lines > term_columns())\n> +\t\t\t\t\tsetup_pager();\n>  \t\t\t\tfputs(s->buf.buf, stdout);\n>  \t\t\t\trendered_hunk_index = hunk_index;\n> -\t\t\t\twait_for_pager();\n> +\t\t\t\tif (lines > term_columns())\n> +\t\t\t\t\twait_for_pager();\n>  \t\t\t}\n>  \n>  \t\t\tstrbuf_reset(&s->buf);\n>\n> This would significantly reduce the blast radius.\n>\n> Thanks.\n"},{"id":"495133","messageId":"bfa694d4-7ee4-4e13-9fc9-8631a95b1d73@gmail.com","threadId":"61514","inReplyTo":"xmqq5xv8dqd2.fsf@gitster.g","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-20T23:27:41Z","receivedAt":"2024-05-20T23:27:44Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Mon, May 20, 2024 at 04:18:33PM -0700, Junio C Hamano wrote:\n\n> Besides, comparison with term_columns() is meaningless\n> unless your window is square ;-)\n\nXD\n\n> \n> An explicit 'P' might be palatable, though.\n\nOK.  Thanks.\n"},{"id":"495134","messageId":"516ffe6bafb71f5645e93e6c01f721a7@manjaro.org","threadId":"61514","inReplyTo":"83071f70-e8a1-41d6-9fb1-108a31602baa@gmail.com","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-05-20T23:54:22Z","receivedAt":"2024-05-20T23:54:31Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-05-21 00:35, Rubén Justo wrote:\n> On Mon, May 20, 2024 at 09:45:51PM +0200, Dragan Simic wrote:\n> \n>> Thus, if someone wants to have a complete longer-than-one-screen hunk\n>> displayed and use the terminal emulator scrollback to inspect the\n>> hunk in its entirety, passing such (or all) hunks through the pager\n>> would make such inspection impossible.  I'd assume that at least some\n>> Git users already do that (I do, for example), and we surely don't \n>> want\n>> to make that no longer possible.\n> \n> I'm not sure if I understand the problem... disabling the pager is\n> still an option, no?\n> \n>     $ git -P add -p\n> \n>     $ git -c pager.add=false add -p\n\nPlease read the thread [1] that I linked in my previous response \ncarefully.\nI know, there's _a lot_ of text, but I already tried to sum it all up in \nmy\nprevious response.  There's even a video clip [2] in that thread that \nshows\nthe issue with the corrupted scrollback in a terminal emulator.\n\nFrankly, the \"-c pager.add=false\" approach is a bit cumbersome.  \nBasically,\nthe new \"-P\" option would be like \"-c pager.add=true\", while the already\nexisting \"-p\" option would be like \"-c pager.add=false\".  Though, I \nthink\nthat we don't want to add \"pager.add\" as a new configuration option, \nbecause\nthrowing it into the mix would make the \"-p\" and \"-P\" options for \"git \nadd\"\nquite confusing.\n\nAs another idea, we might also add \"p\" as another option in the \n\"y/n/q/a/d...\"\nmenu when the user decides about each hunk in \"git add -p\" or \"git add \n-P\".\nWhen running \"git add -p\", pressing \"p\" would display the current hunk \nusing\nthe pager (which would be opposite to the \"git add -p\"'s behavior of not\nusing the pager), and when running \"git add -P\", pressing \"p\" would \ndisplay\nthe current hunk by bypassing the pager (again, opposite to the \"add \n-P\"'s\nbehavior).  That would allow greatest level of flexibility.\n\n[1] \nhttps://lore.kernel.org/git/8289ef15266172cbfa10bb146afe9797@manjaro.org/T/#u\n[2] https://youtu.be/MsxtQgrKM50\n"},{"id":"495148","messageId":"20240521070752.GA616202@coredump.intra.peff.net","threadId":"61514","inReplyTo":"ec5d73e22a6e4587f3d87314a9c0e422@manjaro.org","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-05-21T07:07:52Z","receivedAt":"2024-05-21T07:08:01Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, May 20, 2024 at 09:45:51PM +0200, Dragan Simic wrote:\n\n> > Another, ulteriour, motive here behind this suggestion is to\n> > encourage users to work with smaller hunks.  Being able to scroll\n> > around and view lines on demand (i.e. use of pager) is one thing.\n> > Being able to view all relevant lines at once (i.e. not wasting\n> > vertical screen real estate and making things fit on one screen) is\n> > very different and much nicer.\n> \n> There's another thing to consider, which makes the introduction of\n> \"P\" as the new option even more desirable.  Let me explain.\n> \n> With the upcoming changes to the way less(1) as the pager works,\n> which was already discussed at length and even required new features\n> to be implemented in less(1), [1] displaying anything through less(1)\n> will not leave an accessible scrollback in the terminal emulator.\n> Only one screen worth of text will be displayed, even after quitting\n> less(1).  That's what we have to do, to fix age-old issues with the\n> pager-generated scrollback that easily gets corrupted and actually\n> becomes misleading.\n\nThis feature can be annoying even with current versions of less,\ndepending on your $LESS variable. If you don't set \"F\" you'll get a\npager for short inputs, and if you don't set \"X\" then even small hunks\nare cleared from the screen while we ask about them.\n\nSo this definitely needs to be configurable, and I'd be tempted to say\nit should be off by default, just because we don't know how the user's\npager will behave when invoked for multiple short snippets like this (it\nmight not even be \"less\", after all).\n\nI don't think setting pager.add is enough here. You'd also need to set\npager.checkout, pager.reset, and so on, since their interactive modes\nall invoke the same code. We'd presumably want a single config option\n(and possibly even one that could be set to a distinct pager command for\nthis context, rather than the usual one).\n\n-Peff\n"},{"id":"495210","messageId":"87fa38a1-d40b-42d1-be31-aba7ce21881a@gmail.com","threadId":"61514","inReplyTo":"516ffe6bafb71f5645e93e6c01f721a7@manjaro.org","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-21T19:56:41Z","receivedAt":"2024-05-21T19:56:44Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Tue, May 21, 2024 at 01:54:22AM +0200, Dragan Simic wrote:\n\n> Though, I think that we don't want to add \"pager.add\" as a new\n> configuration option\n\nI have no plans to add a new git-add(1) option or a new configuration\noption.  Only a new interactive option 'P'.\n\nI do not see the need for them, but maybe I'm missing some use case.\n\nI'm going send a new iteration, v2;  please, take a look at it.\n\nThanks.\n"},{"id":"495224","messageId":"5f6f3ce7-a590-4109-ab8a-1d6a31d50f3c@gmail.com","threadId":"61514","inReplyTo":"20240521070752.GA616202@coredump.intra.peff.net","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-21T19:59:55Z","receivedAt":"2024-05-21T19:59:58Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Tue, May 21, 2024 at 03:07:52AM -0400, Jeff King wrote:\n> On Mon, May 20, 2024 at 09:45:51PM +0200, Dragan Simic wrote:\n> \n> > > Another, ulteriour, motive here behind this suggestion is to\n> > > encourage users to work with smaller hunks.  Being able to scroll\n> > > around and view lines on demand (i.e. use of pager) is one thing.\n> > > Being able to view all relevant lines at once (i.e. not wasting\n> > > vertical screen real estate and making things fit on one screen) is\n> > > very different and much nicer.\n> > \n> > There's another thing to consider, which makes the introduction of\n> > \"P\" as the new option even more desirable.  Let me explain.\n> > \n> > With the upcoming changes to the way less(1) as the pager works,\n> > which was already discussed at length and even required new features\n> > to be implemented in less(1), [1] displaying anything through less(1)\n> > will not leave an accessible scrollback in the terminal emulator.\n> > Only one screen worth of text will be displayed, even after quitting\n> > less(1).  That's what we have to do, to fix age-old issues with the\n> > pager-generated scrollback that easily gets corrupted and actually\n> > becomes misleading.\n> \n> This feature can be annoying even with current versions of less,\n\nHopefully, reducing the blast radius to a new 'P' option, will make it\npalatable.\n\n> depending on your $LESS variable. If you don't set \"F\" you'll get a\n> pager for short inputs, and if you don't set \"X\" then even small hunks\n> are cleared from the screen while we ask about them.\n> \n> So this definitely needs to be configurable, and I'd be tempted to say\n> it should be off by default, just because we don't know how the user's\n> pager will behave when invoked for multiple short snippets like this (it\n> might not even be \"less\", after all).\n> \n> I don't think setting pager.add is enough here. You'd also need to set\n> pager.checkout, pager.reset, and so on, since their interactive modes\n> all invoke the same code. We'd presumably want a single config option\n> (and possibly even one that could be set to a distinct pager command for\n> this context, rather than the usual one).\n> \n> -Peff\n"},{"id":"495229","messageId":"199072a9-a3fb-4c8d-b867-b0717a10bacc@gmail.com","threadId":"61514","inReplyTo":"1d0cb55c-5f32-419a-b593-d5f0969a51fd@gmail.com","subject":"[PATCH v2 0/5] use the pager in 'add -p'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-21T20:49:44Z","receivedAt":"2024-05-21T20:49:47Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Invoke the pager when displaying hunks during \"add -p\" sessions, to make                                                                     \nit easier for the user to review hunks longer than one screen height.                                                                        \n                                                                                                                                             \nThis iteration, v2, reduces the use of the pager to the 'print' command\nwhen invoked using a capital 'P'.                 \n\nRubén Justo (5):\n  add-patch: test for 'p' command\n  pager: do not close fd 2 unnecessarily\n  pager: introduce wait_for_pager\n  test-terminal: introduce --no-stdin-pty\n  add-patch: render hunks through the pager\n\n add-patch.c                | 14 ++++++++++---\n pager.c                    | 41 ++++++++++++++++++++++++++++++++------\n pager.h                    |  1 +\n t/t3701-add-interactive.sh | 37 ++++++++++++++++++++++++++++++++++\n t/test-terminal.perl       | 32 ++++++++++++++++-------------\n 5 files changed, 102 insertions(+), 23 deletions(-)\n\nRange-diff against v1:\n1:  340b090e64 = 1:  05edb72326 add-patch: test for 'p' command\n2:  7cb16742ef = 2:  8fe915a820 pager: do not close fd 2 unnecessarily\n3:  207faad1b1 = 3:  fc629a7334 pager: introduce wait_for_pager\n4:  a3815e20c3 = 4:  d2bd591e65 test-terminal: introduce --no-stdin-pty\n5:  d5886bf9cd ! 5:  d3c11dbb1d add-patch: render hunks through the pager\n    @@ Metadata\n      ## Commit message ##\n         add-patch: render hunks through the pager\n     \n    -    Invoke the pager when displaying hunks during \"add -p\" sessions, to make\n    -    it easier for the user to review hunks longer than one screen height.\n    +    Make the print command to trigger the pager when invoked using a capital\n    +    'P', to make it easier for the user to review long hunks.\n     \n         Signed-off-by: Rubén Justo <rjusto@gmail.com>\n     \n    @@ add-patch.c\n      #include \"read-cache-ll.h\"\n      #include \"repository.h\"\n      #include \"strbuf.h\"\n    +@@ add-patch.c: N_(\"j - leave this hunk undecided, see next undecided hunk\\n\"\n    +    \"/ - search for a hunk matching the given regex\\n\"\n    +    \"s - split the current hunk into smaller hunks\\n\"\n    +    \"e - manually edit the current hunk\\n\"\n    +-   \"p - print the current hunk\\n\"\n    ++   \"p - print the current hunk, 'P' to use the pager\\n\"\n    +    \"? - print help\\n\");\n    + \n    + static int patch_update_file(struct add_p_state *s,\n    +@@ add-patch.c: static int patch_update_file(struct add_p_state *s,\n    + \tstruct hunk *hunk;\n    + \tchar ch;\n    + \tstruct child_process cp = CHILD_PROCESS_INIT;\n    +-\tint colored = !!s->colored.len, quit = 0;\n    ++\tint colored = !!s->colored.len, quit = 0, use_pager = 0;\n    + \tenum prompt_mode_type prompt_mode_type;\n    + \tenum {\n    + \t\tALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,\n     @@ add-patch.c: static int patch_update_file(struct add_p_state *s,\n      \t\tstrbuf_reset(&s->buf);\n      \t\tif (file_diff->hunk_nr) {\n      \t\t\tif (rendered_hunk_index != hunk_index) {\n    -+\t\t\t\tsetup_pager();\n    ++\t\t\t\tif (use_pager)\n    ++\t\t\t\t\tsetup_pager();\n      \t\t\t\trender_hunk(s, hunk, 0, colored, &s->buf);\n      \t\t\t\tfputs(s->buf.buf, stdout);\n      \t\t\t\trendered_hunk_index = hunk_index;\n    -+\t\t\t\twait_for_pager();\n    ++\t\t\t\tif (use_pager) {\n    ++\t\t\t\t\twait_for_pager();\n    ++\t\t\t\t\tuse_pager = 0;\n    ++\t\t\t\t}\n      \t\t\t}\n      \n      \t\t\tstrbuf_reset(&s->buf);\n    +@@ add-patch.c: static int patch_update_file(struct add_p_state *s,\n    + \t\t\t\thunk->use = USE_HUNK;\n    + \t\t\t\tgoto soft_increment;\n    + \t\t\t}\n    +-\t\t} else if (s->answer.buf[0] == 'p') {\n    ++\t\t} else if (ch == 'p') {\n    + \t\t\trendered_hunk_index = -1;\n    ++\t\t\tuse_pager = (s->answer.buf[0] == 'P') ? 1 : 0;\n    + \t\t} else if (s->answer.buf[0] == '?') {\n    + \t\t\tconst char *p = _(help_patch_remainder), *eol = p;\n    + \n     \n      ## t/t3701-add-interactive.sh ##\n     @@ t/t3701-add-interactive.sh: test_expect_success 'print again the hunk' '\n    @@ t/t3701-add-interactive.sh: test_expect_success 'print again the hunk' '\n     +test_expect_success TTY 'print again the hunk (PAGER)' '\n     +\ttest_when_finished \"git reset\" &&\n     +\tcat >expect <<-EOF &&\n    -+\tPAGER <GREEN>+<RESET><GREEN>15<RESET>\n    -+\tPAGER  20<RESET>\n    ++\t<GREEN>+<RESET><GREEN>15<RESET>\n    ++\t 20<RESET>\n     +\t<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>\n     +\tPAGER  10<RESET>\n     +\tPAGER <GREEN>+<RESET><GREEN>15<RESET>\n     +\tPAGER  20<RESET>\n     +\t<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>\n     +\tEOF\n    -+\ttest_write_lines s y g 1 p |\n    ++\ttest_write_lines s y g 1 P |\n     +\t(\n     +\t\tGIT_PAGER=\"sed s/^/PAGER\\ /\" &&\n     +\t\texport GIT_PAGER &&\n-- \n2.45.1.221.gd3c11dbb1d\n"},{"id":"495230","messageId":"89511733-fe62-44ec-bb27-106c3d8b798a@gmail.com","threadId":"61514","inReplyTo":"199072a9-a3fb-4c8d-b867-b0717a10bacc@gmail.com","subject":"[PATCH v2 1/5] add-patch: test for 'p' command","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-21T20:51:57Z","receivedAt":"2024-05-21T20:52:00Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Add a test for the 'p' command, which was introduced in 66c14ab592\n(add-patch: introduce 'p' in interactive-patch, 2024-03-29).\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n t/t3701-add-interactive.sh | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 28a95a775d..52d7830de2 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -542,6 +542,22 @@ test_expect_success 'goto hunk' '\n \ttest_cmp expect actual.trimmed\n '\n \n+test_expect_success 'print again the hunk' '\n+\ttest_when_finished \"git reset\" &&\n+\ttr _ \" \" >expect <<-EOF &&\n+\t+15\n+\t 20\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@\n+\t 10\n+\t+15\n+\t 20\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\tEOF\n+\ttest_write_lines s y g 1 p | git add -p >actual &&\n+\ttail -n 7 <actual >actual.trimmed &&\n+\ttest_cmp expect actual.trimmed\n+'\n+\n test_expect_success 'navigate to hunk via regex' '\n \ttest_when_finished \"git reset\" &&\n \ttr _ \" \" >expect <<-EOF &&\n-- \n2.45.1.221.gd3c11dbb1d\n"},{"id":"495231","messageId":"0a6c38fe-8feb-47c3-804c-44d8535a278d@gmail.com","threadId":"61514","inReplyTo":"199072a9-a3fb-4c8d-b867-b0717a10bacc@gmail.com","subject":"[PATCH v2 2/5] pager: do not close fd 2 unnecessarily","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-21T20:52:07Z","receivedAt":"2024-05-21T20:52:10Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"We send errors to the pager since 61b80509e3 (sending errors to stdout\nunder $PAGER, 2008-02-16).\n\nIn a8335024c2 (pager: do not dup2 stderr if it is already redirected,\n2008-12-15) an exception was introduced to avoid redirecting stderr if\nit is not connected to a terminal.\n\nIn such exceptional cases, the close(STDERR_FILENO) we're doing in\nclose_pager_fds, is unnecessary.\n\nFurthermore, in a subsequent commit we're going to introduce changes\nthat might call close_pager_fds multiple times.  With this in mind,\nunconditionally closing stderr will become undesirable.\n\nLet's close(STDERR_FILENO) only when necessary, and pave the way for the\ncomming changes.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n pager.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex b8822a9381..3ef6798f7e 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -14,6 +14,7 @@ int pager_use_color = 1;\n \n static struct child_process pager_process;\n static const char *pager_program;\n+static int old_fd2 = -1;\n \n /* Is the value coming back from term_columns() just a guess? */\n static int term_columns_guessed;\n@@ -23,7 +24,8 @@ static void close_pager_fds(void)\n {\n \t/* signal EOF to pager */\n \tclose(1);\n-\tclose(2);\n+\tif (old_fd2 != -1)\n+\t\tclose(2);\n }\n \n static void wait_for_pager_atexit(void)\n@@ -141,8 +143,10 @@ void setup_pager(void)\n \n \t/* original process continues, but writes to the pipe */\n \tdup2(pager_process.in, 1);\n-\tif (isatty(2))\n+\tif (isatty(2)) {\n+\t\told_fd2 = 1;\n \t\tdup2(pager_process.in, 2);\n+\t}\n \tclose(pager_process.in);\n \n \t/* this makes sure that the parent terminates after the pager */\n-- \n2.45.1.221.gd3c11dbb1d\n"},{"id":"495232","messageId":"217c246b-2c28-4acf-8614-ce66ad345437@gmail.com","threadId":"61514","inReplyTo":"199072a9-a3fb-4c8d-b867-b0717a10bacc@gmail.com","subject":"[PATCH v2 3/5] pager: introduce wait_for_pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-21T20:52:18Z","receivedAt":"2024-05-21T20:52:20Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Since f67b45f862 (Introduce trivial new pager.c helper infrastructure,\n2006-02-28) we have the machinery to send our output to a pager.\n\nThat machinery, once set up, does not allow us to regain the original\nstdio streams.\n\nIn the interactive commands (i.e.: add -p) we want to use the pager for\nsome output, while maintaining the interaction with the user.\n\nModify the pager machinery so that we can use setup_pager and, once\nwe've finished sending the desired output for the pager, wait for the\npager termination using a new function wait_for_pager.   Make this\nfunction reset the pager machinery before returning.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n pager.c | 37 +++++++++++++++++++++++++++++++------\n pager.h |  1 +\n 2 files changed, 32 insertions(+), 6 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex 3ef6798f7e..2fa06c43c4 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -14,12 +14,11 @@ int pager_use_color = 1;\n \n static struct child_process pager_process;\n static const char *pager_program;\n-static int old_fd2 = -1;\n+static int old_fd1 = -1, old_fd2 = -1;\n \n /* Is the value coming back from term_columns() just a guess? */\n static int term_columns_guessed;\n \n-\n static void close_pager_fds(void)\n {\n \t/* signal EOF to pager */\n@@ -30,14 +29,35 @@ static void close_pager_fds(void)\n \n static void wait_for_pager_atexit(void)\n {\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n \tfflush(stdout);\n \tfflush(stderr);\n \tclose_pager_fds();\n \tfinish_command(&pager_process);\n }\n \n+void wait_for_pager(void)\n+{\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n+\twait_for_pager_atexit();\n+\tunsetenv(\"GIT_PAGER_IN_USE\");\n+\tdup2(old_fd1, 1);\n+\told_fd1 = -1;\n+\tif (old_fd2 != -1) {\n+\t\tdup2(old_fd2, 2);\n+\t\told_fd2 = -1;\n+\t}\n+}\n+\n static void wait_for_pager_signal(int signo)\n {\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n \tclose_pager_fds();\n \tfinish_command_in_signal(&pager_process);\n \tsigchain_pop(signo);\n@@ -113,11 +133,14 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)\n \n void setup_pager(void)\n {\n+\tstatic int once = 0;\n \tconst char *pager = git_pager(isatty(1));\n \n \tif (!pager)\n \t\treturn;\n \n+\tassert(old_fd1 == -1);\n+\n \t/*\n \t * After we redirect standard output, we won't be able to use an ioctl\n \t * to get the terminal size. Let's grab it now, and then set $COLUMNS\n@@ -142,16 +165,18 @@ void setup_pager(void)\n \t\treturn;\n \n \t/* original process continues, but writes to the pipe */\n+\told_fd1 = dup(1);\n \tdup2(pager_process.in, 1);\n \tif (isatty(2)) {\n-\t\told_fd2 = 1;\n+\t\told_fd2 = dup(2);\n \t\tdup2(pager_process.in, 2);\n \t}\n \tclose(pager_process.in);\n \n-\t/* this makes sure that the parent terminates after the pager */\n-\tsigchain_push_common(wait_for_pager_signal);\n-\tatexit(wait_for_pager_atexit);\n+\tif (!once++) {\n+\t\tsigchain_push_common(wait_for_pager_signal);\n+\t\tatexit(wait_for_pager_atexit);\n+\t}\n }\n \n int pager_in_use(void)\ndiff --git a/pager.h b/pager.h\nindex b77433026d..103ecac476 100644\n--- a/pager.h\n+++ b/pager.h\n@@ -5,6 +5,7 @@ struct child_process;\n \n const char *git_pager(int stdout_is_tty);\n void setup_pager(void);\n+void wait_for_pager(void);\n int pager_in_use(void);\n int term_columns(void);\n void term_clear_line(void);\n-- \n2.45.1.221.gd3c11dbb1d\n"},{"id":"495233","messageId":"dabf978d-1e3d-4446-9f62-32081f393371@gmail.com","threadId":"61514","inReplyTo":"199072a9-a3fb-4c8d-b867-b0717a10bacc@gmail.com","subject":"[PATCH v2 4/5] test-terminal: introduce --no-stdin-pty","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-21T20:52:26Z","receivedAt":"2024-05-21T20:52:29Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"In 18d8c26930 (test_terminal: redirect child process' stdin to a pty,\n2015-08-04), t/test-terminal.perl learned to connect the child process'\nstdin to a pty.  It works well for what was intended: satisfying an\n`isatty(STDIN_FILENO)` check.\n\nHowever, the fork introduced, that copies the stdin to the child\nprocess, does not always manage to send all the information.\n\nTo illustrate this behaviour, we can use a function like this:\n\n    f ()\n    {\n    \tdd if=/dev/zero bs=1 count=10000 status=none |\n    \tt/test-terminal.perl cat - 2>/dev/null |\n    \twc -c;\n    }\n\nWe do not obtain the expected results when executing this function\n100 times:\n\n    $ for i in $(seq 100); do f; done | sort | uniq -c\n         36 0\n          4 1\n         53 4095\n          7 4159\n\nIf we do the same with a version that does not redirect stdin, a version\nprior to 18d8c26930, the expected result is obtained:\n\n    $ git checkout 18d8c26930~1\n    $ for i in $(seq 100); do f; done | sort | uniq -c\n        100 10000\n\nIn a subsequent commit, a new test is going to rely on test-terminate,\nand it does not require stdin to be connected to a terminal, but all\npiped data needs to be successfully transmitted to the child process.\n\nTo make this possible, add a new parameter \"--no-stdin-pty\" to allow\ndisabling the stdin redirection though a pty.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n t/test-terminal.perl | 32 ++++++++++++++++++--------------\n 1 file changed, 18 insertions(+), 14 deletions(-)\n\ndiff --git a/t/test-terminal.perl b/t/test-terminal.perl\nindex 3810e9bb43..85edc9e8b9 100755\n--- a/t/test-terminal.perl\n+++ b/t/test-terminal.perl\n@@ -12,10 +12,10 @@ sub start_child {\n \tif (not defined $pid) {\n \t\tdie \"fork failed: $!\"\n \t} elsif ($pid == 0) {\n-\t\topen STDIN, \"<&\", $in;\n+\t\topen STDIN, \"<&\", $in if $in;\n \t\topen STDOUT, \">&\", $out;\n \t\topen STDERR, \">&\", $err;\n-\t\tclose $in;\n+\t\tclose $in if $in;\n \t\tclose $out;\n \t\texec(@$argv) or die \"cannot exec '$argv->[0]': $!\"\n \t}\n@@ -78,28 +78,32 @@ sub copy_stdio {\n }\n \n if ($#ARGV < 1) {\n-\tdie \"usage: test-terminal program args\";\n+\tdie \"usage: test-terminal [--no-stdin-pty] program args\";\n }\n+my $no_stdin_pty = $ARGV[0] eq '--no-stdin-pty';\n+shift @ARGV if $no_stdin_pty;\n $ENV{TERM} = 'vt100';\n-my $parent_in = new IO::Pty;\n+my $parent_in = $no_stdin_pty ? undef : IO::Pty->new;\n my $parent_out = new IO::Pty;\n my $parent_err = new IO::Pty;\n-$parent_in->set_raw();\n+$parent_in->set_raw() if $parent_in;\n $parent_out->set_raw();\n $parent_err->set_raw();\n-$parent_in->slave->set_raw();\n+$parent_in->slave->set_raw() if $parent_in;\n $parent_out->slave->set_raw();\n $parent_err->slave->set_raw();\n-my $pid = start_child(\\@ARGV, $parent_in->slave, $parent_out->slave, $parent_err->slave);\n-close $parent_in->slave;\n+my $pid = start_child(\\@ARGV,$parent_in ? $parent_in->slave : undef, $parent_out->slave, $parent_err->slave);\n+close $parent_in->slave if $parent_in;\n close $parent_out->slave;\n close $parent_err->slave;\n-my $in_pid = copy_stdin($parent_in);\n+my $in_pid = $no_stdin_pty ? 0 : copy_stdin($parent_in);\n copy_stdio($parent_out, $parent_err);\n my $ret = finish_child($pid);\n-# If the child process terminates before our copy_stdin() process is able to\n-# write all of its data to $parent_in, the copy_stdin() process could stall.\n-# Send SIGTERM to it to ensure it terminates.\n-kill 'TERM', $in_pid;\n-finish_child($in_pid);\n+if ($in_pid) {\n+\t# If the child process terminates before our copy_stdin() process is able to\n+\t# write all of its data to $parent_in, the copy_stdin() process could stall.\n+\t# Send SIGTERM to it to ensure it terminates.\n+\tkill 'TERM', $in_pid;\n+\tfinish_child($in_pid);\n+}\n exit($ret);\n-- \n2.45.1.221.gd3c11dbb1d\n"},{"id":"495234","messageId":"310a2904-681a-4bee-96b9-90a2dc107975@gmail.com","threadId":"61514","inReplyTo":"199072a9-a3fb-4c8d-b867-b0717a10bacc@gmail.com","subject":"[PATCH v2 5/5] add-patch: render hunks through the pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-21T20:52:37Z","receivedAt":"2024-05-21T20:52:40Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Make the print command to trigger the pager when invoked using a capital\n'P', to make it easier for the user to review long hunks.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n add-patch.c                | 14 +++++++++++---\n t/t3701-add-interactive.sh | 21 +++++++++++++++++++++\n 2 files changed, 32 insertions(+), 3 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 2252895c28..d614536cb2 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -5,6 +5,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"object-name.h\"\n+#include \"pager.h\"\n #include \"read-cache-ll.h\"\n #include \"repository.h\"\n #include \"strbuf.h\"\n@@ -1387,7 +1388,7 @@ N_(\"j - leave this hunk undecided, see next undecided hunk\\n\"\n    \"/ - search for a hunk matching the given regex\\n\"\n    \"s - split the current hunk into smaller hunks\\n\"\n    \"e - manually edit the current hunk\\n\"\n-   \"p - print the current hunk\\n\"\n+   \"p - print the current hunk, 'P' to use the pager\\n\"\n    \"? - print help\\n\");\n \n static int patch_update_file(struct add_p_state *s,\n@@ -1398,7 +1399,7 @@ static int patch_update_file(struct add_p_state *s,\n \tstruct hunk *hunk;\n \tchar ch;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tint colored = !!s->colored.len, quit = 0;\n+\tint colored = !!s->colored.len, quit = 0, use_pager = 0;\n \tenum prompt_mode_type prompt_mode_type;\n \tenum {\n \t\tALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,\n@@ -1448,9 +1449,15 @@ static int patch_update_file(struct add_p_state *s,\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n \t\t\tif (rendered_hunk_index != hunk_index) {\n+\t\t\t\tif (use_pager)\n+\t\t\t\t\tsetup_pager();\n \t\t\t\trender_hunk(s, hunk, 0, colored, &s->buf);\n \t\t\t\tfputs(s->buf.buf, stdout);\n \t\t\t\trendered_hunk_index = hunk_index;\n+\t\t\t\tif (use_pager) {\n+\t\t\t\t\twait_for_pager();\n+\t\t\t\t\tuse_pager = 0;\n+\t\t\t\t}\n \t\t\t}\n \n \t\t\tstrbuf_reset(&s->buf);\n@@ -1665,8 +1672,9 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\thunk->use = USE_HUNK;\n \t\t\t\tgoto soft_increment;\n \t\t\t}\n-\t\t} else if (s->answer.buf[0] == 'p') {\n+\t\t} else if (ch == 'p') {\n \t\t\trendered_hunk_index = -1;\n+\t\t\tuse_pager = (s->answer.buf[0] == 'P') ? 1 : 0;\n \t\t} else if (s->answer.buf[0] == '?') {\n \t\t\tconst char *p = _(help_patch_remainder), *eol = p;\n \ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 52d7830de2..4be7a14419 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -558,6 +558,27 @@ test_expect_success 'print again the hunk' '\n \ttest_cmp expect actual.trimmed\n '\n \n+test_expect_success TTY 'print again the hunk (PAGER)' '\n+\ttest_when_finished \"git reset\" &&\n+\tcat >expect <<-EOF &&\n+\t<GREEN>+<RESET><GREEN>15<RESET>\n+\t 20<RESET>\n+\t<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>\n+\tPAGER  10<RESET>\n+\tPAGER <GREEN>+<RESET><GREEN>15<RESET>\n+\tPAGER  20<RESET>\n+\t<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>\n+\tEOF\n+\ttest_write_lines s y g 1 P |\n+\t(\n+\t\tGIT_PAGER=\"sed s/^/PAGER\\ /\" &&\n+\t\texport GIT_PAGER &&\n+\t\ttest_terminal --no-stdin-pty git add -p >actual\n+\t) &&\n+\ttail -n 7 <actual | test_decode_color >actual.trimmed &&\n+\ttest_cmp expect actual.trimmed\n+'\n+\n test_expect_success 'navigate to hunk via regex' '\n \ttest_when_finished \"git reset\" &&\n \ttr _ \" \" >expect <<-EOF &&\n-- \n2.45.1.221.gd3c11dbb1d\n"},{"id":"495235","messageId":"xmqqwmnm993k.fsf@gitster.g","threadId":"61514","inReplyTo":"a9f199d8-bb06-479f-88c2-63d80338a4e9@gmail.com","subject":"Re: [PATCH 2/5] pager: do not close fd 2 unnecessarily","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-21T20:57:19Z","receivedAt":"2024-05-21T20:57:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n>> >  static struct child_process pager_process;\n>> >  static const char *pager_program;\n>> > +static int old_fd2 = -1;\n>> \n>> What does the magic number \"-1\" mean?\n>\n> Invalid fd.\n>\n>> We often use it to signal\n>> \"uninitialized\", but then what are concrete \"initialized\" values\n>> mean?  \"We dup2()'ed something else to stderr/fd #2 but before doing\n>> so we saved the original fd #2 away to this variable, so that we can\n>> restore fd #2 by another dup2() of the value of this variable when\n>> we declare that we are done with the standard error stream\"?\n>> \n>> But that does not look like what is happening here.\n>>  ....\n>> Equally unclear magic number \"1\" is used here.\n>> \n>> This value is different from pager_process.in, and my earlier \"we\n>> are saving away\" does not apply, either.\n>\n> It applies, in 3/5.\n\nWe need to be prepared to see a series chomped at an early stage and\nit should still make sense.  If the series does not make sense when\nyou stop before applying patch 3, it is a strong sign that this step\nand the next step can be separated and structured better.\n\nOr perhaps if they are made into a single patch it makes more sense\nand becomes easier to explain?\n\n"},{"id":"495239","messageId":"0574914d-8088-434d-8db2-013c1abe27c3@gmail.com","threadId":"61514","inReplyTo":"xmqqwmnm993k.fsf@gitster.g","subject":"Re: [PATCH 2/5] pager: do not close fd 2 unnecessarily","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-21T21:35:17Z","receivedAt":"2024-05-21T21:35:20Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Tue, May 21, 2024 at 01:57:19PM -0700, Junio C Hamano wrote:\n> Rubén Justo <rjusto@gmail.com> writes:\n> \n> >> >  static struct child_process pager_process;\n> >> >  static const char *pager_program;\n> >> > +static int old_fd2 = -1;\n> >> \n> >> What does the magic number \"-1\" mean?\n> >\n> > Invalid fd.\n> >\n> >> We often use it to signal\n> >> \"uninitialized\", but then what are concrete \"initialized\" values\n> >> mean?  \"We dup2()'ed something else to stderr/fd #2 but before doing\n> >> so we saved the original fd #2 away to this variable, so that we can\n> >> restore fd #2 by another dup2() of the value of this variable when\n> >> we declare that we are done with the standard error stream\"?\n> >> \n> >> But that does not look like what is happening here.\n> >>  ....\n> >> Equally unclear magic number \"1\" is used here.\n> >> \n> >> This value is different from pager_process.in, and my earlier \"we\n> >> are saving away\" does not apply, either.\n> >\n> > It applies, in 3/5.\n> \n> We need to be prepared to see a series chomped at an early stage and\n> it should still make sense.  If the series does not make sense when\n> you stop before applying patch 3, it is a strong sign that this step\n> and the next step can be separated and structured better.\n> \n> Or perhaps if they are made into a single patch it makes more sense\n> and becomes easier to explain?\n> \n\nAdding logic to adjust when we close(stderr) in close_pager_fds() makes\nsense on its own, I think.\n\nAnd, the values for the flag \"do-we-want-to-close-stderr-at-exit\", too,\nto me.\n\nI am happy with the series;  the 'P' command introduced in v2 is a good\nimprovement.  Combining 2/5 and 3/5, I think it is not a good idea.\n\nTherefore, I'm not sure how to alleviate the puzzling.\n"},{"id":"495241","messageId":"xmqqikz6966t.fsf@gitster.g","threadId":"61514","inReplyTo":"0574914d-8088-434d-8db2-013c1abe27c3@gmail.com","subject":"Re: [PATCH 2/5] pager: do not close fd 2 unnecessarily","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-21T22:00:10Z","receivedAt":"2024-05-21T22:00:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n> Adding logic to adjust when we close(stderr) in close_pager_fds() makes\n> sense on its own, I think.\n\nThe feature may be.\n\n> And, the values for the flag \"do-we-want-to-close-stderr-at-exit\", too,\n> to me.\n\nBut the thing is, the flag is *NOT* named as such, and an\nundocumented \"value -1 means X, while value 1 means Y\", do not make\nany sense, either.\n"},{"id":"495278","messageId":"1accd0163c96811b7b7f146e477acf89@manjaro.org","threadId":"61514","inReplyTo":"310a2904-681a-4bee-96b9-90a2dc107975@gmail.com","subject":"Re: [PATCH v2 5/5] add-patch: render hunks through the pager","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-05-22T08:09:25Z","receivedAt":"2024-05-22T08:09:27Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"Hello Ruben,\n\nOn 2024-05-21 22:52, Rubén Justo wrote:\n> Make the print command to trigger the pager when invoked using a \n> capital\n> 'P', to make it easier for the user to review long hunks.\n> \n> ...\n> \n> @@ -1387,7 +1388,7 @@ N_(\"j - leave this hunk undecided, see next\n> undecided hunk\\n\"\n>     \"/ - search for a hunk matching the given regex\\n\"\n>     \"s - split the current hunk into smaller hunks\\n\"\n>     \"e - manually edit the current hunk\\n\"\n> -   \"p - print the current hunk\\n\"\n> +   \"p - print the current hunk, 'P' to use the pager\\n\"\n\nI think it would be better to move the description of \"P\" into\na separate line after the \"p\" line, perhaps like this:\n\n   \"P - use the pager to print the current hunk\\n\"\n\nI know, we'd sacrifice one line of the valuable vertical space\nthis way, but I find it more consistent and much harder to miss\nthe new \"P\" option.\n\nOverall, I find the introduction of \"P\" as the new single-character\nmenu option fine.  Maybe we can later add \"-P\" as the new command-\nline option, if there will be some demand to do that.\n"},{"id":"495306","messageId":"501a610c-550f-45da-a311-d4c941ae4870@gmail.com","threadId":"61514","inReplyTo":"xmqqikz6966t.fsf@gitster.g","subject":"Re: [PATCH 2/5] pager: do not close fd 2 unnecessarily","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-22T17:19:08Z","receivedAt":"2024-05-22T17:19:27Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Tue, May 21, 2024 at 03:00:10PM -0700, Junio C Hamano wrote:\n> Rubén Justo <rjusto@gmail.com> writes:\n> \n> > Adding logic to adjust when we close(stderr) in close_pager_fds() makes\n> > sense on its own, I think.\n> \n> The feature may be.\n> \n> > And, the values for the flag \"do-we-want-to-close-stderr-at-exit\", too,\n> > to me.\n> \n> But the thing is, the flag is *NOT* named as such, and an\n> undocumented \"value -1 means X, while value 1 means Y\", do not make\n> any sense, either\n\nPerhaps this makes more sense?:\n\n1:  8fe915a820 ! 1:  70cc34efc4 pager: do not close fd 2 unnecessarily\n    @@ pager.c: int pager_use_color = 1;\n      \n      static struct child_process pager_process;\n      static const char *pager_program;\n    -+static int old_fd2 = -1;\n    ++static int old_fd2;\n      \n      /* Is the value coming back from term_columns() just a guess? */\n      static int term_columns_guessed;\n    @@ pager.c: static void close_pager_fds(void)\n      \t/* signal EOF to pager */\n      \tclose(1);\n     -\tclose(2);\n    -+\tif (old_fd2 != -1)\n    ++\tif (old_fd2)\n     +\t\tclose(2);\n      }\n"},{"id":"495310","messageId":"xmqqa5kh68zh.fsf@gitster.g","threadId":"61514","inReplyTo":"501a610c-550f-45da-a311-d4c941ae4870@gmail.com","subject":"Re: [PATCH 2/5] pager: do not close fd 2 unnecessarily","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-22T17:40:18Z","receivedAt":"2024-05-22T17:40:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n> Perhaps this makes more sense?:\n>\n> 1:  8fe915a820 ! 1:  70cc34efc4 pager: do not close fd 2 unnecessarily\n>     @@ pager.c: int pager_use_color = 1;\n>       \n>       static struct child_process pager_process;\n>       static const char *pager_program;\n>     -+static int old_fd2 = -1;\n>     ++static int old_fd2;\n>       \n>       /* Is the value coming back from term_columns() just a guess? */\n>       static int term_columns_guessed;\n>     @@ pager.c: static void close_pager_fds(void)\n>       \t/* signal EOF to pager */\n>       \tclose(1);\n>      -\tclose(2);\n>     -+\tif (old_fd2 != -1)\n>     ++\tif (old_fd2)\n>      +\t\tclose(2);\n>       }\n\nNot really.  The name \"old_fd2\" strongly implies \"where did fd#2\ncome from?\" and it did not come from fd#0, did it?  Until [3/5]\nthis variable used to mean something different from \"this was the\nsaved fd#2 we can use to restore it later\", which is the name\n\"old_fd2\" clearly wants to stand for.\n\nIf you really want to have them as two separate patches, I would\nexpect the proposed log message for the [3/5] step to say something\nlike\n\n    ... we added variable X to signal if we should close fd#2 in\n    function F in the previous step.  As store away the original\n    fd#2 with dup(2) to be restored later after we close() it, the\n    question the previous step asked, \"should we be the one closing\n    fd#2?\" becomes equivalent to \"have we stored away the original\n    fd#2 (in which case we close() fd#2 when we are done with the\n    pager and restore the original one)?\"  Rename X to old_fd2 and\n    have it serve both purposes.\n\nor somesuch.\n"},{"id":"495313","messageId":"xmqqle414rae.fsf@gitster.g","threadId":"61514","inReplyTo":"1accd0163c96811b7b7f146e477acf89@manjaro.org","subject":"Re: [PATCH v2 5/5] add-patch: render hunks through the pager","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-22T18:47:53Z","receivedAt":"2024-05-22T18:47:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dragan Simic <dsimic@manjaro.org> writes:\n\n>> @@ -1387,7 +1388,7 @@ N_(\"j - leave this hunk undecided, see next\n>> undecided hunk\\n\"\n>>     \"/ - search for a hunk matching the given regex\\n\"\n>>     \"s - split the current hunk into smaller hunks\\n\"\n>>     \"e - manually edit the current hunk\\n\"\n>> -   \"p - print the current hunk\\n\"\n>> +   \"p - print the current hunk, 'P' to use the pager\\n\"\n>\n> I think it would be better to move the description of \"P\" into\n> a separate line after the \"p\" line, perhaps like this:\n>\n>   \"P - use the pager to print the current hunk\\n\"\n\nSounds good to me, too.\n"},{"id":"495329","messageId":"ff8efadb-4c1a-43ce-9b12-7688d6062c18@gmail.com","threadId":"61514","inReplyTo":"1accd0163c96811b7b7f146e477acf89@manjaro.org","subject":"Re: [PATCH v2 5/5] add-patch: render hunks through the pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-22T21:23:41Z","receivedAt":"2024-05-22T21:23:44Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Wed, May 22, 2024 at 10:09:25AM +0200, Dragan Simic wrote:\n\n> > @@ -1387,7 +1388,7 @@ N_(\"j - leave this hunk undecided, see next\n> > undecided hunk\\n\"\n> >     \"/ - search for a hunk matching the given regex\\n\"\n> >     \"s - split the current hunk into smaller hunks\\n\"\n> >     \"e - manually edit the current hunk\\n\"\n> > -   \"p - print the current hunk\\n\"\n> > +   \"p - print the current hunk, 'P' to use the pager\\n\"\n> \n> I think it would be better to move the description of \"P\" into\n> a separate line after the \"p\" line, perhaps like this:\n> \n>   \"P - use the pager to print the current hunk\\n\"\n> \n> I know, we'd sacrifice one line of the valuable vertical space\n> this way, but I find it more consistent and much harder to miss\n> the new \"P\" option.\n\nMaking 'P' a /version/ of 'p' allows us to skip adding 'P' to the list\nof available options:\n\n    (1/1) Stage this hunk [y,n,q,a,d,j,J,k,K,g,/,s,e,p,P,?]\n\nThis is what I though and this long list is what worries me.\n\nBut I see your point.  I am not opposed to adding a new line.\n"},{"id":"495331","messageId":"424c7de34b5a42680a07e7f362a47f89@manjaro.org","threadId":"61514","inReplyTo":"ff8efadb-4c1a-43ce-9b12-7688d6062c18@gmail.com","subject":"Re: [PATCH v2 5/5] add-patch: render hunks through the pager","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-05-22T21:27:55Z","receivedAt":"2024-05-22T21:27:57Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-05-22 23:23, Rubén Justo wrote:\n> On Wed, May 22, 2024 at 10:09:25AM +0200, Dragan Simic wrote:\n> \n>> > @@ -1387,7 +1388,7 @@ N_(\"j - leave this hunk undecided, see next\n>> > undecided hunk\\n\"\n>> >     \"/ - search for a hunk matching the given regex\\n\"\n>> >     \"s - split the current hunk into smaller hunks\\n\"\n>> >     \"e - manually edit the current hunk\\n\"\n>> > -   \"p - print the current hunk\\n\"\n>> > +   \"p - print the current hunk, 'P' to use the pager\\n\"\n>> \n>> I think it would be better to move the description of \"P\" into\n>> a separate line after the \"p\" line, perhaps like this:\n>> \n>>   \"P - use the pager to print the current hunk\\n\"\n>> \n>> I know, we'd sacrifice one line of the valuable vertical space\n>> this way, but I find it more consistent and much harder to miss\n>> the new \"P\" option.\n> \n> Making 'P' a /version/ of 'p' allows us to skip adding 'P' to the list\n> of available options:\n> \n>     (1/1) Stage this hunk [y,n,q,a,d,j,J,k,K,g,/,s,e,p,P,?]\n> \n> This is what I though and this long list is what worries me.\n\nOh, I wouldn't be worried too much about the length of the list of\nthe options.  It's feature-rich, so the list has to be a bit long. :)\n\n> But I see your point.  I am not opposed to adding a new line.\n\nThanks.\n"},{"id":"495367","messageId":"20240523090601.GC1306938@coredump.intra.peff.net","threadId":"61514","inReplyTo":"5f6f3ce7-a590-4109-ab8a-1d6a31d50f3c@gmail.com","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-05-23T09:06:01Z","receivedAt":"2024-05-23T09:06:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, May 21, 2024 at 09:59:55PM +0200, Rubén Justo wrote:\n\n> > This feature can be annoying even with current versions of less,\n> \n> Hopefully, reducing the blast radius to a new 'P' option, will make it\n> palatable.\n\nYeah, that would be perfect. I might even use it, then, for the rare\ncases when I want to look at a really big hunk. I do still think it\nwould be useful to be able to configure its pager separately (in my\ncase, I'd use \"less -FX\" rather than my default setup, which doesn't use\neither of those options). But I'm also OK to leave that for now and let\nit be somebody else's itch to scratch later.\n\n-Peff\n"},{"id":"495405","messageId":"xmqqjzjky6eo.fsf@gitster.g","threadId":"61514","inReplyTo":"20240523090601.GC1306938@coredump.intra.peff.net","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-23T14:00:47Z","receivedAt":"2024-05-23T14:00:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I do still think it\n> would be useful to be able to configure its pager separately (in my\n> case, I'd use \"less -FX\" rather than my default setup, which doesn't use\n> either of those options).\n\nEven better.  Allow to optionally have the command after the option,\ne.g.,\n\n    (1/1) Use this hunk [y,n,q,j,k,e,p,P] P<RET>\n    (1/1) Use this hunk [y,n,q,j,k,e,p,P] Pless -FX<RET>\n    (1/1) Use this hunk [y,n,q,j,k,e,p,P] Pcat<RET>\n\nThe first one feeds the default program with the hunk via pipe, the\nsecond one instead invokes command you specifed, \"less -FX\", and\nfeeds the hunk to it via a pipe.  The last one emulates a plain 'p'\nbehaviour.\n\nAnd for usability, perhaps giving a specific command would change\nthe default program a bare 'P' invokes for the rest of the session\nuntil another specific command overrides.  Another usability hack\nmay be \"[interactive] pipecommand = less -FX\" configuration variable\ngives the initial default for each session.\n\nAt that point, we can explain it as\n\n   p - print the current hunk\n   P[<program>] - pipe the current hunk to a program\n\nor even use '|' instead of 'P'.\n\n"},{"id":"495406","messageId":"261636d461e58ac8a16180c4cd6e0460@manjaro.org","threadId":"61514","inReplyTo":"xmqqjzjky6eo.fsf@gitster.g","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-05-23T14:18:03Z","receivedAt":"2024-05-23T14:18:05Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-05-23 16:00, Junio C Hamano wrote:\n> Jeff King <peff@peff.net> writes:\n> \n>> I do still think it\n>> would be useful to be able to configure its pager separately (in my\n>> case, I'd use \"less -FX\" rather than my default setup, which doesn't \n>> use\n>> either of those options).\n> \n> Even better.  Allow to optionally have the command after the option,\n> e.g.,\n> \n>     (1/1) Use this hunk [y,n,q,j,k,e,p,P] P<RET>\n>     (1/1) Use this hunk [y,n,q,j,k,e,p,P] Pless -FX<RET>\n>     (1/1) Use this hunk [y,n,q,j,k,e,p,P] Pcat<RET>\n\nPlease note that \"-X\" will no longer be used as one of the options\npassed to less(1) as the pager, in the upcoming resolution of the\nage-old pager issues. [1]\n\nIn more detail, \"-X\" is actually an ugly hack that was nothing more\nthan a stopgap measure, but it has never been resolved properly.  That\nis, until recently, when I started to collaborate with the author of\nless(1) towards finding and implementing the real solution.\n\n[1] \nhttps://lore.kernel.org/git/8289ef15266172cbfa10bb146afe9797@manjaro.org/T/#u\n\n> The first one feeds the default program with the hunk via pipe, the\n> second one instead invokes command you specifed, \"less -FX\", and\n> feeds the hunk to it via a pipe.  The last one emulates a plain 'p'\n> behaviour.\n\nFrankly, that would be a lot of typing, and may even open a path for\nsome unforeseen security issues.\n\n> And for usability, perhaps giving a specific command would change\n> the default program a bare 'P' invokes for the rest of the session\n> until another specific command overrides.  Another usability hack\n> may be \"[interactive] pipecommand = less -FX\" configuration variable\n> gives the initial default for each session.\n\nI think that would be way too complicated.\n\n> At that point, we can explain it as\n> \n>    p - print the current hunk\n>    P[<program>] - pipe the current hunk to a program\n> \n> or even use '|' instead of 'P'.\n"},{"id":"495484","messageId":"9d25b8af-a865-4535-b8fb-d518768e00b4@gmail.com","threadId":"61514","inReplyTo":"20240523090601.GC1306938@coredump.intra.peff.net","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-23T22:25:42Z","receivedAt":"2024-05-23T22:25:46Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Thu, May 23, 2024 at 05:06:01AM -0400, Jeff King wrote:\n> On Tue, May 21, 2024 at 09:59:55PM +0200, Rubén Justo wrote:\n> \n> > > This feature can be annoying even with current versions of less,\n> > \n> > Hopefully, reducing the blast radius to a new 'P' option, will make it\n> > palatable.\n> \n> Yeah, that would be perfect. I might even use it, then, for the rare\n> cases when I want to look at a really big hunk.\n\nIn addition to that, I have two use-cases that make sense to me:\n\n  - avoiding a huuge but split-able hunk to go all through my terminal\n    before I can say: split, 's'.  For this, perhaps the '-P' suggested\n    by Dragan is the way to go.\n\n  - a lot of mid-sized hunks that I only need to see, to decide, the\n    result of \"head -3\".  Here, the pager would be acting as a 'filter'.\n\nPerhaps I am stretching the meaning of 'pager' too far...\n\n> I do still think it would be useful to be able to configure its pager\n> separately (in my case, I'd use \"less -FX\" rather than my default\n> setup, which doesn't use either of those options).\n\nA new \"interactive.pager\" setting?  Perhaps with higher preference than\n\"add.pager\"?  Just questioning, do not take this as an intention of\nscratching that itch :-)\n"},{"id":"495490","messageId":"0b43d5df031aabec48c5ab4fe6436c36@manjaro.org","threadId":"61514","inReplyTo":"9d25b8af-a865-4535-b8fb-d518768e00b4@gmail.com","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-05-23T23:03:03Z","receivedAt":"2024-05-23T23:03:05Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-05-24 00:25, Rubén Justo wrote:\n> On Thu, May 23, 2024 at 05:06:01AM -0400, Jeff King wrote:\n\n[...]\n\n> In addition to that, I have two use-cases that make sense to me:\n> \n>   - avoiding a huuge but split-able hunk to go all through my terminal\n>     before I can say: split, 's'.  For this, perhaps the '-P' suggested\n>     by Dragan is the way to go.\n\nA possible UX issue with the \"-P\" option is that the menu wouldn't\nbe displayed right after executing \"git add -P\" if the first displayed\nhunk is longer than one screen, leaving the users wondering what\nactually happened.  Though, that perhaps could be addressed in the\ndocumentation.\n\n>   - a lot of mid-sized hunks that I only need to see, to decide, the\n>     result of \"head -3\".  Here, the pager would be acting as a \n> 'filter'.\n> \n> Perhaps I am stretching the meaning of 'pager' too far...\n> \n>> I do still think it would be useful to be able to configure its pager\n>> separately (in my case, I'd use \"less -FX\" rather than my default\n>> setup, which doesn't use either of those options).\n> \n> A new \"interactive.pager\" setting?  Perhaps with higher preference than\n> \"add.pager\"?  Just questioning, do not take this as an intention of\n> scratching that itch :-)\n\nHuh, I see some possible issues with separate pager configurations,\nresulting from the upcoming rework of the default less(1)-as-pager\noptions, so perhaps it would, in addition, be better to wait until\nthose changes settle first.\n\nI intend to get into that rather soon, not only for Git, but also\nfor a few other projects that use less(1) as their pager by default,\nsuch as util-linux.\n"},{"id":"495492","messageId":"xmqq7cfkqger.fsf@gitster.g","threadId":"61514","inReplyTo":"261636d461e58ac8a16180c4cd6e0460@manjaro.org","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-23T23:04:12Z","receivedAt":"2024-05-23T23:04:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dragan Simic <dsimic@manjaro.org> writes:\n\n>> And for usability, perhaps giving a specific command would change\n>> the default program a bare 'P' invokes for the rest of the session\n>> until another specific command overrides.  Another usability hack\n>> may be \"[interactive] pipecommand = less -FX\" configuration variable\n>> gives the initial default for each session.\n>\n> I think that would be way too complicated.\n\nIt is modelled after how \"less\" and \"vi\" remembers the last pattern\nfed to their \"/\" command.  You once give, say, \"/test_<ENTER>\" to\nfind one instance of \"test_\", then \"/<ENTER>\" takes to the next\ninstance.\n\nAs I expect our target audiences are used to such a behaviour, I do\nnot think I agree with your \"way too complicated\".\n"},{"id":"495497","messageId":"ba1c6fbac4424a4e2c68cb439f9918eb@manjaro.org","threadId":"61514","inReplyTo":"xmqq7cfkqger.fsf@gitster.g","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-05-23T23:28:30Z","receivedAt":"2024-05-23T23:28:32Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-05-24 01:04, Junio C Hamano wrote:\n> Dragan Simic <dsimic@manjaro.org> writes:\n> \n>>> And for usability, perhaps giving a specific command would change\n>>> the default program a bare 'P' invokes for the rest of the session\n>>> until another specific command overrides.  Another usability hack\n>>> may be \"[interactive] pipecommand = less -FX\" configuration variable\n>>> gives the initial default for each session.\n>> \n>> I think that would be way too complicated.\n> \n> It is modelled after how \"less\" and \"vi\" remembers the last pattern\n> fed to their \"/\" command.  You once give, say, \"/test_<ENTER>\" to\n> find one instance of \"test_\", then \"/<ENTER>\" takes to the next\n> instance.\n\nHuh, less(1) actually remembers nothing when the secure mode is\nturned on.  That's another thing I've collaborated with the author\nof less(1), to make it possible to remember the last search term\nwhen running less(1) in secure mode.\n\n> As I expect our target audiences are used to such a behaviour, I do\n> not think I agree with your \"way too complicated\".\n\nHmm.  Where would that state be stored?\n"},{"id":"495501","messageId":"3391a15907a055c281106c4995fb8272@manjaro.org","threadId":"61514","inReplyTo":"ba1c6fbac4424a4e2c68cb439f9918eb@manjaro.org","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-05-23T23:43:57Z","receivedAt":"2024-05-23T23:43:59Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-05-24 01:28, Dragan Simic wrote:\n> On 2024-05-24 01:04, Junio C Hamano wrote:\n>> Dragan Simic <dsimic@manjaro.org> writes:\n>> \n>>>> And for usability, perhaps giving a specific command would change\n>>>> the default program a bare 'P' invokes for the rest of the session\n>>>> until another specific command overrides.  Another usability hack\n>>>> may be \"[interactive] pipecommand = less -FX\" configuration variable\n>>>> gives the initial default for each session.\n>>> \n>>> I think that would be way too complicated.\n>> \n>> It is modelled after how \"less\" and \"vi\" remembers the last pattern\n>> fed to their \"/\" command.  You once give, say, \"/test_<ENTER>\" to\n>> find one instance of \"test_\", then \"/<ENTER>\" takes to the next\n>> instance.\n> \n> Huh, less(1) actually remembers nothing when the secure mode is\n> turned on.  That's another thing I've collaborated with the author\n> of less(1), to make it possible to remember the last search term\n> when running less(1) in secure mode.\n> \n>> As I expect our target audiences are used to such a behaviour, I do\n>> not think I agree with your \"way too complicated\".\n> \n> Hmm.  Where would that state be stored?\n\nAh, sorry, I misread your description a bit.  There's obviously no need\nto store any state, because this additional feature doesn't break the\nboundaries of a single session.\n\nHowever, I'd suggest that we leave this additional feature aside for \nnow,\nuntil the upcoming pager-related changes and fixes settle down.\n"},{"id":"495502","messageId":"xmqqjzjkozhv.fsf@gitster.g","threadId":"61514","inReplyTo":"ba1c6fbac4424a4e2c68cb439f9918eb@manjaro.org","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-23T23:54:52Z","receivedAt":"2024-05-23T23:54:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dragan Simic <dsimic@manjaro.org> writes:\n\n> On 2024-05-24 01:04, Junio C Hamano wrote:\n>> Dragan Simic <dsimic@manjaro.org> writes:\n>> \n>>>> And for usability, perhaps giving a specific command would change\n>>>> the default program a bare 'P' invokes for the rest of the session\n>>>> until another specific command overrides.  Another usability hack\n>>>> may be \"[interactive] pipecommand = less -FX\" configuration variable\n>>>> gives the initial default for each session.\n>>> I think that would be way too complicated.\n>> It is modelled after how \"less\" and \"vi\" remembers the last pattern\n>> fed to their \"/\" command.  You once give, say, \"/test_<ENTER>\" to\n>> find one instance of \"test_\", then \"/<ENTER>\" takes to the next\n>> instance.\n>\n> Huh, less(1) actually remembers nothing when the secure mode is\n> turned on.\n\nAre you sure you read me right?  I wasn't talking about storing\nanything on disk for the \"usability hack\", and made it explicitly\nclear with \"for the rest of the session\".\n"},{"id":"495503","messageId":"928234dea3d6d7a5ed1e39e9897ef4cf@manjaro.org","threadId":"61514","inReplyTo":"xmqqjzjkozhv.fsf@gitster.g","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-05-23T23:57:12Z","receivedAt":"2024-05-23T23:57:14Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-05-24 01:54, Junio C Hamano wrote:\n> Dragan Simic <dsimic@manjaro.org> writes:\n> \n>> On 2024-05-24 01:04, Junio C Hamano wrote:\n>>> Dragan Simic <dsimic@manjaro.org> writes:\n>>> \n>>>>> And for usability, perhaps giving a specific command would change\n>>>>> the default program a bare 'P' invokes for the rest of the session\n>>>>> until another specific command overrides.  Another usability hack\n>>>>> may be \"[interactive] pipecommand = less -FX\" configuration \n>>>>> variable\n>>>>> gives the initial default for each session.\n>>>> I think that would be way too complicated.\n>>> \n>>> It is modelled after how \"less\" and \"vi\" remembers the last pattern\n>>> fed to their \"/\" command.  You once give, say, \"/test_<ENTER>\" to\n>>> find one instance of \"test_\", then \"/<ENTER>\" takes to the next\n>>> instance.\n>> \n>> Huh, less(1) actually remembers nothing when the secure mode is\n>> turned on.\n> \n> Are you sure you read me right?  I wasn't talking about storing\n> anything on disk for the \"usability hack\", and made it explicitly\n> clear with \"for the rest of the session\".\n\nI misread it at first, but I corrected myself. [1]\n\n[1] \nhttps://lore.kernel.org/git/3391a15907a055c281106c4995fb8272@manjaro.org/\n"},{"id":"495626","messageId":"20240525045434.GC1895047@coredump.intra.peff.net","threadId":"61514","inReplyTo":"xmqqjzjky6eo.fsf@gitster.g","subject":"Re: [PATCH 5/5] add-patch: render hunks through the pager","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-05-25T04:54:34Z","receivedAt":"2024-05-25T04:54:35Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, May 23, 2024 at 07:00:47AM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > I do still think it\n> > would be useful to be able to configure its pager separately (in my\n> > case, I'd use \"less -FX\" rather than my default setup, which doesn't use\n> > either of those options).\n> \n> Even better.  Allow to optionally have the command after the option,\n> e.g.,\n> \n>     (1/1) Use this hunk [y,n,q,j,k,e,p,P] P<RET>\n>     (1/1) Use this hunk [y,n,q,j,k,e,p,P] Pless -FX<RET>\n>     (1/1) Use this hunk [y,n,q,j,k,e,p,P] Pcat<RET>\n> \n> The first one feeds the default program with the hunk via pipe, the\n> second one instead invokes command you specifed, \"less -FX\", and\n> feeds the hunk to it via a pipe.  The last one emulates a plain 'p'\n> behaviour.\n> \n> And for usability, perhaps giving a specific command would change\n> the default program a bare 'P' invokes for the rest of the session\n> until another specific command overrides.  Another usability hack\n> may be \"[interactive] pipecommand = less -FX\" configuration variable\n> gives the initial default for each session.\n> \n> At that point, we can explain it as\n> \n>    p - print the current hunk\n>    P[<program>] - pipe the current hunk to a program\n> \n> or even use '|' instead of 'P'.\n\nOoh, I like all of that (including \"|\", which is what triggers the\nsimilar feature in mutt). If interactive.pipeCommand defaults to the\nusual pager, then a bare \"|\" would do what most people want.\n\n-Peff\n"},{"id":"495647","messageId":"e6c4dc8f-ffbf-4749-8086-22c29da768a7@gmail.com","threadId":"61514","inReplyTo":"xmqqa5kh68zh.fsf@gitster.g","subject":"Re: [PATCH 2/5] pager: do not close fd 2 unnecessarily","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-05-26T06:48:55Z","receivedAt":"2024-05-26T06:49:05Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Wed, May 22, 2024 at 10:40:18AM -0700, Junio C Hamano wrote:\n\n> Not really.  The name \"old_fd2\" strongly implies \"where did fd#2\n> come from?\" and it did not come from fd#0, did it?\n\nPerhaps \"close_fd2\" is a better name?:\n\n@@ pager.c: int pager_use_color = 1;\n  \n  static struct child_process pager_process;\n  static const char *pager_program;\n-+static int old_fd2 = -1;\n++static int close_fd2;\n  \n  /* Is the value coming back from term_columns() just a guess? */\n  static int term_columns_guessed;\n\n@@ pager.c: static void close_pager_fds(void)\n  \t/* signal EOF to pager */\n  \tclose(1);\n -\tclose(2);\n-+\tif (old_fd2 != -1)\n++\tif (close_fd2)\n +\t\tclose(2);\n  }\n"},{"id":"495656","messageId":"xmqqfru4e02s.fsf@gitster.g","threadId":"61514","inReplyTo":"e6c4dc8f-ffbf-4749-8086-22c29da768a7@gmail.com","subject":"Re: [PATCH 2/5] pager: do not close fd 2 unnecessarily","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-05-26T21:26:51Z","receivedAt":"2024-05-26T21:27:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n> On Wed, May 22, 2024 at 10:40:18AM -0700, Junio C Hamano wrote:\n>\n>> Not really.  The name \"old_fd2\" strongly implies \"where did fd#2\n>> come from?\" and it did not come from fd#0, did it?\n>\n> Perhaps \"close_fd2\" is a better name?:\n> @@ pager.c: static void close_pager_fds(void)\n>   \t/* signal EOF to pager */\n>   \tclose(1);\n>  -\tclose(2);\n> -+\tif (old_fd2 != -1)\n> ++\tif (close_fd2)\n>  +\t\tclose(2);\n\nThat's a very straight-forward name that says what effect anybody\nwho assigns to the variable wants to see.\n"},{"id":"496045","messageId":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","threadId":"61514","inReplyTo":"199072a9-a3fb-4c8d-b867-b0717a10bacc@gmail.com","subject":"[PATCH v3 0/6] use the pager in 'add -p'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-02T15:38:42Z","receivedAt":"2024-06-02T15:38:45Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"The main goal in this series is to allow using the pager when displaying\nhunks during \"add -p\" sessions, to make easier for users to review hunks\nlonger than one screen height.\n\nThis iteration, v3, introduces a new command: '|', suggested by Junio,\ninstead of the 'P' command proposed in the previous iteration.\n\nThis allows us to use the pager:\n\n  (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,p,|,?]? |\n\nBut also to use other programs, like:\n\n  (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,p,|,?]? | head\n\nOr:\n\n  (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,p,|,?]? | grep term\n\nHopefully, we'll find a way to avoid sending ANSI codes, on demand,\nwithout disabling it entirely with color.ui=never or any other global\noption.  To make this usable:\n\n  (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,p,|,?]? | vim -\n\nHowever, the current functionality meets my current needs, so I'm happy\nwith it.\n\nThis, a new 'interactive.pipeCommand' setting, or a new switch: 'add -P',\nare left for discussing in, hopefully, a future series.\n\nOne final note;  I preferred to model the help text this way:\n\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    j - leave this hunk undecided, see next undecided hunk\n    J - leave this hunk undecided, see next hunk\n    g - select a hunk to go to \n    / - search for a hunk matching the given regex\n    s - split the current hunk into smaller hunks\n    e - manually edit the current hunk\n    p - print the current hunk\n    | - pipe the current hunk to the pager, or |<program> to use a program'\n    ? - print help\n\nInstead of:\n\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    j - leave this hunk undecided, see next undecided hunk\n    J - leave this hunk undecided, see next hunk\n    g - select a hunk to go to\n    / - search for a hunk matching the given regex\n    s - split the current hunk into smaller hunks\n    e - manually edit the current hunk\n    p - print the current hunk\n    |[program] - pipe the current hunk to a program, the pager if none...\n    ? - print help\n\nBecause I believe it reads better by maintaining a single character\nbefore the dash.  But I am not opposed to the latter.\n\nThe series is now based on jc/add-patch-enforce-single-letter-input,\nwhich has been recently merged to master.\n\nThanks.\n\nRubén Justo (6):\n  add-patch: test for 'p' command\n  pager: do not close fd 2 unnecessarily\n  pager: introduce wait_for_pager\n  pager: introduce setup_custom_pager\n  test-terminal: introduce --no-stdin-pty\n  add-patch: introduce the command '|'\n\n add-patch.c                | 17 ++++++++--\n pager.c                    | 55 ++++++++++++++++++++++++-------\n pager.h                    |  7 +++-\n t/t3701-add-interactive.sh | 67 +++++++++++++++++++++++++++++---------\n t/test-terminal.perl       | 32 ++++++++++--------\n 5 files changed, 135 insertions(+), 43 deletions(-)\n\n-- \n2.45.0.97.g9fa538478d\n"},{"id":"496046","messageId":"de0a68d0-245f-4641-a467-8678d5c7ae05@gmail.com","threadId":"61514","inReplyTo":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","subject":"[PATCH v3 1/6] add-patch: test for 'p' command","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-02T15:42:38Z","receivedAt":"2024-06-02T15:42:41Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Add a test for the 'p' command, which was introduced in 66c14ab592\n(add-patch: introduce 'p' in interactive-patch, 2024-03-29).\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n t/t3701-add-interactive.sh | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 6624a4f7c0..6f6d174687 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -590,6 +590,22 @@ test_expect_success 'navigate to hunk via regex / pattern' '\n \ttest_cmp expect actual.trimmed\n '\n \n+test_expect_success 'print again the hunk' '\n+\ttest_when_finished \"git reset\" &&\n+\ttr _ \" \" >expect <<-EOF &&\n+\t+15\n+\t 20\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@\n+\t 10\n+\t+15\n+\t 20\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\tEOF\n+\ttest_write_lines s y g 1 p | git add -p >actual &&\n+\ttail -n 7 <actual >actual.trimmed &&\n+\ttest_cmp expect actual.trimmed\n+'\n+\n test_expect_success 'split hunk \"add -p (edit)\"' '\n \t# Split, say Edit and do nothing.  Then:\n \t#\n-- \n2.45.0.97.g9fa538478d\n"},{"id":"496047","messageId":"5aca7ccd-d119-4a4f-8234-2c6ba3387435@gmail.com","threadId":"61514","inReplyTo":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","subject":"[PATCH v3 2/6] pager: do not close fd 2 unnecessarily","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-02T15:42:52Z","receivedAt":"2024-06-02T15:42:55Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"We send errors to the pager since 61b80509e3 (sending errors to stdout\nunder $PAGER, 2008-02-16).\n\nIn a8335024c2 (pager: do not dup2 stderr if it is already redirected,\n2008-12-15) an exception was introduced to avoid redirecting stderr if\nit is not connected to a terminal.\n\nIn such exceptional cases, the close(STDERR_FILENO) we're doing in\nclose_pager_fds, is unnecessary.\n\nFurthermore, in a subsequent commit we're going to introduce changes\nthat will involve using close_pager_fds multiple times.\n\nWith this in mind, controlling when we want to close stderr, become\nsensible.\n\nLet's close(STDERR_FILENO) only when necessary, and pave the way for the\nupcoming changes.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n pager.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex b8822a9381..b786601074 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -14,6 +14,7 @@ int pager_use_color = 1;\n \n static struct child_process pager_process;\n static const char *pager_program;\n+static int close_fd2;\n \n /* Is the value coming back from term_columns() just a guess? */\n static int term_columns_guessed;\n@@ -23,7 +24,8 @@ static void close_pager_fds(void)\n {\n \t/* signal EOF to pager */\n \tclose(1);\n-\tclose(2);\n+\tif (close_fd2)\n+\t\tclose(2);\n }\n \n static void wait_for_pager_atexit(void)\n@@ -141,8 +143,10 @@ void setup_pager(void)\n \n \t/* original process continues, but writes to the pipe */\n \tdup2(pager_process.in, 1);\n-\tif (isatty(2))\n+\tif (isatty(2)) {\n+\t\tclose_fd2 = 1;\n \t\tdup2(pager_process.in, 2);\n+\t}\n \tclose(pager_process.in);\n \n \t/* this makes sure that the parent terminates after the pager */\n-- \n2.45.0.97.g9fa538478d\n"},{"id":"496048","messageId":"6176a476-6afc-4513-abfb-280b0dee5be7@gmail.com","threadId":"61514","inReplyTo":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","subject":"[PATCH v3 3/6] pager: introduce wait_for_pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-02T15:43:13Z","receivedAt":"2024-06-02T15:43:15Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Since f67b45f862 (Introduce trivial new pager.c helper infrastructure,\n2006-02-28) we have the machinery to send our output to a pager.\n\nThat machinery, once set up, does not allow us to regain the original\nstdio streams.\n\nIn the interactive commands (i.e.: add -p) we want to use the pager for\nsome output, while maintaining the interaction with the user.\n\nModify the pager machinery so that we can use setup_pager and, once\nwe've finished sending the desired output for the pager, wait for the\npager termination using a new function wait_for_pager.   Make this\nfunction reset the pager machinery before returning.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n pager.c | 36 ++++++++++++++++++++++++++++++------\n pager.h |  1 +\n 2 files changed, 31 insertions(+), 6 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex b786601074..925f860335 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -14,7 +14,7 @@ int pager_use_color = 1;\n \n static struct child_process pager_process;\n static const char *pager_program;\n-static int close_fd2;\n+static int old_fd1 = -1, old_fd2 = -1;\n \n /* Is the value coming back from term_columns() just a guess? */\n static int term_columns_guessed;\n@@ -24,20 +24,41 @@ static void close_pager_fds(void)\n {\n \t/* signal EOF to pager */\n \tclose(1);\n-\tif (close_fd2)\n+\tif (old_fd2 != -1)\n \t\tclose(2);\n }\n \n static void wait_for_pager_atexit(void)\n {\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n \tfflush(stdout);\n \tfflush(stderr);\n \tclose_pager_fds();\n \tfinish_command(&pager_process);\n }\n \n+void wait_for_pager(void)\n+{\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n+\twait_for_pager_atexit();\n+\tunsetenv(\"GIT_PAGER_IN_USE\");\n+\tdup2(old_fd1, 1);\n+\told_fd1 = -1;\n+\tif (old_fd2 != -1) {\n+\t\tdup2(old_fd2, 2);\n+\t\told_fd2 = -1;\n+\t}\n+}\n+\n static void wait_for_pager_signal(int signo)\n {\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n \tclose_pager_fds();\n \tfinish_command_in_signal(&pager_process);\n \tsigchain_pop(signo);\n@@ -113,6 +134,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)\n \n void setup_pager(void)\n {\n+\tstatic int once = 0;\n \tconst char *pager = git_pager(isatty(1));\n \n \tif (!pager)\n@@ -142,16 +164,18 @@ void setup_pager(void)\n \t\treturn;\n \n \t/* original process continues, but writes to the pipe */\n+\told_fd1 = dup(1);\n \tdup2(pager_process.in, 1);\n \tif (isatty(2)) {\n-\t\tclose_fd2 = 1;\n+\t\told_fd2 = dup(2);\n \t\tdup2(pager_process.in, 2);\n \t}\n \tclose(pager_process.in);\n \n-\t/* this makes sure that the parent terminates after the pager */\n-\tsigchain_push_common(wait_for_pager_signal);\n-\tatexit(wait_for_pager_atexit);\n+\tif (!once++) {\n+\t\tsigchain_push_common(wait_for_pager_signal);\n+\t\tatexit(wait_for_pager_atexit);\n+\t}\n }\n \n int pager_in_use(void)\ndiff --git a/pager.h b/pager.h\nindex b77433026d..103ecac476 100644\n--- a/pager.h\n+++ b/pager.h\n@@ -5,6 +5,7 @@ struct child_process;\n \n const char *git_pager(int stdout_is_tty);\n void setup_pager(void);\n+void wait_for_pager(void);\n int pager_in_use(void);\n int term_columns(void);\n void term_clear_line(void);\n-- \n2.45.0.97.g9fa538478d\n"},{"id":"496049","messageId":"a852d45a-e148-4326-9662-14469abbfceb@gmail.com","threadId":"61514","inReplyTo":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","subject":"[PATCH v3 4/6] pager: introduce setup_custom_pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-02T15:43:30Z","receivedAt":"2024-06-02T15:43:33Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Introduce a new function setup_custom_pager() to allow setting up our\npager mechanism using a custom pager.  If the custom pager specified is\nNULL or an empty string, use the normal pager as setup_pager() currently\ndoes.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n pager.c | 17 +++++++++++------\n pager.h |  6 +++++-\n 2 files changed, 16 insertions(+), 7 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex 925f860335..21a7d9cd60 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -74,14 +74,13 @@ static int core_pager_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-const char *git_pager(int stdout_is_tty)\n+static const char *git_pager_custom(int stdout_is_tty, const char* pager)\n {\n-\tconst char *pager;\n-\n \tif (!stdout_is_tty)\n \t\treturn NULL;\n \n-\tpager = getenv(\"GIT_PAGER\");\n+\tif (!pager || !*pager)\n+\t\tpager = getenv(\"GIT_PAGER\");\n \tif (!pager) {\n \t\tif (!pager_program)\n \t\t\tread_early_config(core_pager_config, NULL);\n@@ -97,6 +96,11 @@ const char *git_pager(int stdout_is_tty)\n \treturn pager;\n }\n \n+const char *git_pager(int stdout_is_tty)\n+{\n+\treturn git_pager_custom(stdout_is_tty, NULL);\n+}\n+\n static void setup_pager_env(struct strvec *env)\n {\n \tconst char **argv;\n@@ -132,10 +136,11 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)\n \tpager_process->trace2_child_class = \"pager\";\n }\n \n-void setup_pager(void)\n+void setup_custom_pager(const char* pager)\n {\n \tstatic int once = 0;\n-\tconst char *pager = git_pager(isatty(1));\n+\n+\tpager = git_pager_custom(isatty(1), pager);\n \n \tif (!pager)\n \t\treturn;\ndiff --git a/pager.h b/pager.h\nindex 103ecac476..2166662361 100644\n--- a/pager.h\n+++ b/pager.h\n@@ -4,7 +4,11 @@\n struct child_process;\n \n const char *git_pager(int stdout_is_tty);\n-void setup_pager(void);\n+void setup_custom_pager(const char*);\n+static inline void setup_pager(void)\n+{\n+\tsetup_custom_pager(NULL);\n+}\n void wait_for_pager(void);\n int pager_in_use(void);\n int term_columns(void);\n-- \n2.45.0.97.g9fa538478d\n"},{"id":"496050","messageId":"4542d34a-8387-4c7e-aeda-52ef28f8ed8b@gmail.com","threadId":"61514","inReplyTo":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","subject":"[PATCH v3 5/6] test-terminal: introduce --no-stdin-pty","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-02T15:43:59Z","receivedAt":"2024-06-02T15:44:03Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"In 18d8c26930 (test_terminal: redirect child process' stdin to a pty,\n2015-08-04), t/test-terminal.perl learned to connect the child process'\nstdin to a pty.  It works well for what was intended: satisfying an\n`isatty(STDIN_FILENO)` check.\n\nHowever, the fork introduced in 18d8c26930, that copies the stdin to the\nchild process, does not always manage to transmit all the input data.\n\nTo illustrate this behavior, we can use a function like this:\n\n    send_data ()\n    {\n    \tdd if=/dev/zero bs=1 count=10000 status=none |\n    \tt/test-terminal.perl cat - 2>/dev/null |\n    \twc -c;\n    }\n\nWe do not obtain the expected results when executing this function\n100 times:\n\n    $ for i in $(seq 100); do send_data; done | sort | uniq -c\n         36 0\n          4 1\n         53 4095\n          7 4159\n\nNone of the executions have successfully transmitted the full data.\n\nIf we do the same with a version of t/test-terminal.perl that does not\nredirect stdin, a version prior to 18d8c26930, the expected result is\nobtained:\n\n    $ git checkout 18d8c26930~1\n    $ for i in $(seq 100); do send_data; done | sort | uniq -c\n        100 10000\n\nIn a subsequent commit, we'll introduce a new test that depends on\nt/test-terminate.perl.  This test does not require stdin to be connected\nto a terminal; however, all data piped into the process must be\nsuccessfully transmitted to the child process.\n\nTo make this possible, add a new parameter \"--no-stdin-pty\" to allow\ndisabling the stdin redirection though a pty.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n t/test-terminal.perl | 32 ++++++++++++++++++--------------\n 1 file changed, 18 insertions(+), 14 deletions(-)\n\ndiff --git a/t/test-terminal.perl b/t/test-terminal.perl\nindex 3810e9bb43..85edc9e8b9 100755\n--- a/t/test-terminal.perl\n+++ b/t/test-terminal.perl\n@@ -12,10 +12,10 @@ sub start_child {\n \tif (not defined $pid) {\n \t\tdie \"fork failed: $!\"\n \t} elsif ($pid == 0) {\n-\t\topen STDIN, \"<&\", $in;\n+\t\topen STDIN, \"<&\", $in if $in;\n \t\topen STDOUT, \">&\", $out;\n \t\topen STDERR, \">&\", $err;\n-\t\tclose $in;\n+\t\tclose $in if $in;\n \t\tclose $out;\n \t\texec(@$argv) or die \"cannot exec '$argv->[0]': $!\"\n \t}\n@@ -78,28 +78,32 @@ sub copy_stdio {\n }\n \n if ($#ARGV < 1) {\n-\tdie \"usage: test-terminal program args\";\n+\tdie \"usage: test-terminal [--no-stdin-pty] program args\";\n }\n+my $no_stdin_pty = $ARGV[0] eq '--no-stdin-pty';\n+shift @ARGV if $no_stdin_pty;\n $ENV{TERM} = 'vt100';\n-my $parent_in = new IO::Pty;\n+my $parent_in = $no_stdin_pty ? undef : IO::Pty->new;\n my $parent_out = new IO::Pty;\n my $parent_err = new IO::Pty;\n-$parent_in->set_raw();\n+$parent_in->set_raw() if $parent_in;\n $parent_out->set_raw();\n $parent_err->set_raw();\n-$parent_in->slave->set_raw();\n+$parent_in->slave->set_raw() if $parent_in;\n $parent_out->slave->set_raw();\n $parent_err->slave->set_raw();\n-my $pid = start_child(\\@ARGV, $parent_in->slave, $parent_out->slave, $parent_err->slave);\n-close $parent_in->slave;\n+my $pid = start_child(\\@ARGV,$parent_in ? $parent_in->slave : undef, $parent_out->slave, $parent_err->slave);\n+close $parent_in->slave if $parent_in;\n close $parent_out->slave;\n close $parent_err->slave;\n-my $in_pid = copy_stdin($parent_in);\n+my $in_pid = $no_stdin_pty ? 0 : copy_stdin($parent_in);\n copy_stdio($parent_out, $parent_err);\n my $ret = finish_child($pid);\n-# If the child process terminates before our copy_stdin() process is able to\n-# write all of its data to $parent_in, the copy_stdin() process could stall.\n-# Send SIGTERM to it to ensure it terminates.\n-kill 'TERM', $in_pid;\n-finish_child($in_pid);\n+if ($in_pid) {\n+\t# If the child process terminates before our copy_stdin() process is able to\n+\t# write all of its data to $parent_in, the copy_stdin() process could stall.\n+\t# Send SIGTERM to it to ensure it terminates.\n+\tkill 'TERM', $in_pid;\n+\tfinish_child($in_pid);\n+}\n exit($ret);\n-- \n2.45.0.97.g9fa538478d\n"},{"id":"496051","messageId":"8dbbfd1d-0e74-4423-9c8b-3a40df3b78c1@gmail.com","threadId":"61514","inReplyTo":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","subject":"[PATCH v3 6/6] add-patch: introduce the command '|'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-02T15:44:35Z","receivedAt":"2024-06-02T15:44:37Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Introduce a new command: '|', to send the current hunk to a program.\n\nIf no program is specified, default to the pager.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n add-patch.c                | 17 ++++++++++--\n t/t3701-add-interactive.sh | 55 ++++++++++++++++++++++++++------------\n 2 files changed, 53 insertions(+), 19 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 814de57c4a..5a586d1b9b 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -5,6 +5,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"object-name.h\"\n+#include \"pager.h\"\n #include \"read-cache-ll.h\"\n #include \"repository.h\"\n #include \"strbuf.h\"\n@@ -1389,6 +1390,7 @@ N_(\"j - leave this hunk undecided, see next undecided hunk\\n\"\n    \"s - split the current hunk into smaller hunks\\n\"\n    \"e - manually edit the current hunk\\n\"\n    \"p - print the current hunk\\n\"\n+   \"| - pipe the current hunk to the pager, or |<program> to use a program'\\n\"\n    \"? - print help\\n\");\n \n static int patch_update_file(struct add_p_state *s,\n@@ -1401,6 +1403,7 @@ static int patch_update_file(struct add_p_state *s,\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint colored = !!s->colored.len, quit = 0;\n \tenum prompt_mode_type prompt_mode_type;\n+\tconst char* pager = NULL;\n \tenum {\n \t\tALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,\n \t\tALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,\n@@ -1449,9 +1452,15 @@ static int patch_update_file(struct add_p_state *s,\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n \t\t\tif (rendered_hunk_index != hunk_index) {\n+\t\t\t\tif (pager)\n+\t\t\t\t\tsetup_custom_pager(pager);\n \t\t\t\trender_hunk(s, hunk, 0, colored, &s->buf);\n \t\t\t\tfputs(s->buf.buf, stdout);\n \t\t\t\trendered_hunk_index = hunk_index;\n+\t\t\t\tif (pager) {\n+\t\t\t\t\twait_for_pager();\n+\t\t\t\t\tpager = NULL;\n+\t\t\t\t}\n \t\t\t}\n \n \t\t\tstrbuf_reset(&s->buf);\n@@ -1485,6 +1494,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\tstrbuf_addstr(&s->buf, \",e\");\n \t\t\t}\n \t\t\tstrbuf_addstr(&s->buf, \",p\");\n+\t\t\tstrbuf_addstr(&s->buf, \",|\");\n \t\t}\n \t\tif (file_diff->deleted)\n \t\t\tprompt_mode_type = PROMPT_DELETION;\n@@ -1512,8 +1522,8 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\tcontinue;\n \t\tch = tolower(s->answer.buf[0]);\n \n-\t\t/* 'g' takes a hunk number and '/' takes a regexp */\n-\t\tif (s->answer.len != 1 && (ch != 'g' && ch != '/')) {\n+\t\t/* 'g' takes a hunk number, '/' takes a regexp and '|' takes a program */\n+\t\tif (s->answer.len != 1 && (ch != 'g' && ch != '/' && ch != '|')) {\n \t\t\terr(s, _(\"Only one letter is expected, got '%s'\"), s->answer.buf);\n \t\t\tcontinue;\n \t\t}\n@@ -1674,6 +1684,9 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t}\n \t\t} else if (s->answer.buf[0] == 'p') {\n \t\t\trendered_hunk_index = -1;\n+\t\t} else if (ch == '|') {\n+\t\t\trendered_hunk_index = -1;\n+\t\t\tpager = s->answer.buf + 1;\n \t\t} else if (s->answer.buf[0] == '?') {\n \t\t\tconst char *p = _(help_patch_remainder), *eol = p;\n \ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 6f6d174687..7b3ebb671d 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -64,8 +64,8 @@ test_expect_success 'unknown command' '\n \tgit add -N command &&\n \tgit diff command >expect &&\n \tcat >>expect <<-EOF &&\n-\t(1/1) Stage addition [y,n,q,a,d,e,p,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help)\n-\t(1/1) Stage addition [y,n,q,a,d,e,p,?]?$SP\n+\t(1/1) Stage addition [y,n,q,a,d,e,p,|,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help)\n+\t(1/1) Stage addition [y,n,q,a,d,e,p,|,?]?$SP\n \tEOF\n \tgit add -p -- command <command >actual 2>&1 &&\n \ttest_cmp expect actual\n@@ -348,9 +348,9 @@ test_expect_success 'different prompts for mode change/deleted' '\n \tgit -c core.filemode=true add -p >actual &&\n \tsed -n \"s/^\\(([0-9/]*) Stage .*?\\).*/\\1/p\" actual >actual.filtered &&\n \tcat >expect <<-\\EOF &&\n-\t(1/1) Stage deletion [y,n,q,a,d,p,?]?\n-\t(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,?]?\n-\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]?\n+\t(1/1) Stage deletion [y,n,q,a,d,p,|,?]?\n+\t(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,|,?]?\n+\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]?\n \tEOF\n \ttest_cmp expect actual.filtered\n '\n@@ -537,13 +537,13 @@ test_expect_success 'split hunk setup' '\n test_expect_success 'goto hunk 1 with \"g 1\"' '\n \ttest_when_finished \"git reset\" &&\n \ttr _ \" \" >expect <<-EOF &&\n-\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1:  -1,2 +1,3          +15\n+\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]? + 1:  -1,2 +1,3          +15\n \t_ 2:  -2,4 +3,8          +21\n \tgo to which hunk? @@ -1,2 +1,3 @@\n \t_10\n \t+15\n \t_20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n \tEOF\n \ttest_write_lines s y g 1 | git add -p >actual &&\n \ttail -n 7 <actual >actual.trimmed &&\n@@ -556,7 +556,7 @@ test_expect_success 'goto hunk 1 with \"g1\"' '\n \t_10\n \t+15\n \t_20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n \tEOF\n \ttest_write_lines s y g1 | git add -p >actual &&\n \ttail -n 4 <actual >actual.trimmed &&\n@@ -566,11 +566,11 @@ test_expect_success 'goto hunk 1 with \"g1\"' '\n test_expect_success 'navigate to hunk via regex /pattern' '\n \ttest_when_finished \"git reset\" &&\n \ttr _ \" \" >expect <<-EOF &&\n-\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@\n+\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]? @@ -1,2 +1,3 @@\n \t_10\n \t+15\n \t_20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n \tEOF\n \ttest_write_lines s y /1,2 | git add -p >actual &&\n \ttail -n 5 <actual >actual.trimmed &&\n@@ -583,7 +583,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '\n \t_10\n \t+15\n \t_20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n \tEOF\n \ttest_write_lines s y / 1,2 | git add -p >actual &&\n \ttail -n 4 <actual >actual.trimmed &&\n@@ -595,17 +595,38 @@ test_expect_success 'print again the hunk' '\n \ttr _ \" \" >expect <<-EOF &&\n \t+15\n \t 20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? @@ -1,2 +1,3 @@\n \t 10\n \t+15\n \t 20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n \tEOF\n \ttest_write_lines s y g 1 p | git add -p >actual &&\n \ttail -n 7 <actual >actual.trimmed &&\n \ttest_cmp expect actual.trimmed\n '\n \n+test_expect_success TTY 'print again the hunk (PAGER)' '\n+\ttest_when_finished \"git reset\" &&\n+\tcat >expect <<-EOF &&\n+\t<GREEN>+<RESET><GREEN>15<RESET>\n+\t 20<RESET>\n+\t<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>\n+\tPAGER  10<RESET>\n+\tPAGER <GREEN>+<RESET><GREEN>15<RESET>\n+\tPAGER  20<RESET>\n+\t<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? <RESET>\n+\tEOF\n+\ttest_write_lines s y g 1 \\| |\n+\t(\n+\t\tGIT_PAGER=\"sed s/^/PAGER\\ /\" &&\n+\t\texport GIT_PAGER &&\n+\t\ttest_terminal --no-stdin-pty git add -p >actual\n+\t) &&\n+\ttail -n 7 <actual | test_decode_color >actual.trimmed &&\n+\ttest_cmp expect actual.trimmed\n+'\n+\n test_expect_success 'split hunk \"add -p (edit)\"' '\n \t# Split, say Edit and do nothing.  Then:\n \t#\n@@ -780,21 +801,21 @@ test_expect_success 'colors can be overridden' '\n \t<BLUE>+<RESET><BLUE>new<RESET>\n \t<CYAN> more-context<RESET>\n \t<BLUE>+<RESET><BLUE>another-one<RESET>\n-\t<YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,?]? <RESET><BOLD>Split into 2 hunks.<RESET>\n+\t<YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,|,?]? <RESET><BOLD>Split into 2 hunks.<RESET>\n \t<MAGENTA>@@ -1,3 +1,3 @@<RESET>\n \t<CYAN> context<RESET>\n \t<BOLD>-old<RESET>\n \t<BLUE>+<RESET><BLUE>new<RESET>\n \t<CYAN> more-context<RESET>\n-\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>\n+\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>\n \t<CYAN> more-context<RESET>\n \t<BLUE>+<RESET><BLUE>another-one<RESET>\n-\t<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>\n+\t<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>\n \t<CYAN> context<RESET>\n \t<BOLD>-old<RESET>\n \t<BLUE>+new<RESET>\n \t<CYAN> more-context<RESET>\n-\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>\n+\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? <RESET>\n \tEOF\n \ttest_cmp expect actual\n '\n-- \n2.45.0.97.g9fa538478d\n"},{"id":"496052","messageId":"xmqqwmn79u98.fsf@gitster.g","threadId":"61514","inReplyTo":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-02T16:36:35Z","receivedAt":"2024-06-02T16:36:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n> Hopefully, we'll find a way to avoid sending ANSI codes, on demand,\n> without disabling it entirely with color.ui=never or any other global\n> option.  To make this usable:\n>\n>   (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,p,|,?]? | vim -\n>\n> However, the current functionality meets my current needs, so I'm happy\n> with it.\n\nYup, if it really is needed we could do || or anything \"unusual\" to\nsignal the unusual nature of the command.\n\nOr \">\" command can send the output to specified file without\ncoloring, and the user can do whatever they want to it.  \n\nIn any case, unlike \"Let's not just do pager, but have a facility to\npipe to anything and make the pager a default destination\" that was\na natural match to the originally proposed behaviour, these two are\nquite different and can be left totally outside the scope of the\ntopic.\n\n> One final note;  I preferred to model the help text this way:\n>\n>     y - stage this hunk\n>     n - do not stage this hunk\n> ...\n>     g - select a hunk to go to \n>     / - search for a hunk matching the given regex\n> ...\n>     | - pipe the current hunk to the pager, or |<program> to use a program'\n>     ? - print help\n\nThat's fine.\n\nThe 'g' and '/' commands take _mandatory_ arguments, but we do not\neven mention it in the help text.  But we need to say something for\nthis new thing, because it is _optional_ and if you do not give a\nprogram, it does not ask.\n\nA possibility is to phrase it like so:\n\n    | - pipe the current hunk to the program (or \"%s\" by default)\n\nand fill %s with the program you'd use if not given, i.e. initially\nthe value of the GIT_PAGER but updated to the last used program\nafter the user uses \"|<program>\" form to specify one.\n"},{"id":"496053","messageId":"xmqqttib8e22.fsf@gitster.g","threadId":"61514","inReplyTo":"xmqqwmn79u98.fsf@gitster.g","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-02T17:11:49Z","receivedAt":"2024-06-02T17:11:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> The 'g' and '/' commands take _mandatory_ arguments, but we do not\n> even mention it in the help text.  But we need to say something for\n> this new thing, because it is _optional_ and if you do not give a\n> program, it does not ask.\n\nBy the way, although I personally do not have much sympathy to those\nwho set it, in the presence of interactive.singleKey configuration\nvariable, a command that takes optional argument may turn out to be\na mistake, as the user cannot give the argument even if they wanted\nto, when the configuration variable is set to true.  To help them,\nwe'd probably need something like the following to allow them to\noptionally set their own program, like the following:\n\n 1. read the command, notice that it begins with '|'.\n\n 2. if it has <program> after it, call it <program> and jump to 5.\n\n 3. if it does not have <program> after it, but if single key\n    operation is in effect, give the user a chance to give <program>\n    by prompting.  Call the answer to the prompt <program>.  If it\n    is not an empty string, jump to 5.\n\n 4. at this point, we have <program> that is empty as given by the\n    user.  Replace the <program> with the value we remembered from\n    step 6. during the last use of the '|' command.\n\n 5. if <program> is all whitespace, replace it with an empty string.\n\n 6. remember the value of <program> (so that you can reuse it next\n    time the user says '|' and without <program>), and call the\n    \"set-custom-pager\" thing with <program> (this design assumes\n    that \"set-custom-pager\" thing uses the GIT_PAGER when fed an\n    empty string).\n\n 7. spawn the program set by \"set-custom-pager\", and feed our output\n    to it.\n\nSo the end-user observable behaviour would become\n\n * There is the \"default\" program, initially their pager, but after\n   they use the '|' command, we remember the last program they used\n   during the session and reuse it when they tell us to do so.\n\n * For singlekey folks, typing '|' will give them a prompt.  They\n   can give an empty string and spawn the \"default\" thing.  They can\n   give \" \" plus <RET> to reset the \"default\" to GIT_PAGER and use\n   it.  Or they can give <program> plus <RET> to use it and update\n   the \"default\".\n\n * For the rest of us, typing \"|\" plus <RET> will spawn the\n   \"default\" thing.  Typing \"|<program>\" plus <RET> will use the\n   <program> and update the \"default\".  Typing \"| \" plus <RET> will\n   reset the \"default\" to GIT_PAGER and use it.\n\nwhich is quite straight-forward and consistent between the two\ncamps.\n\n"},{"id":"496054","messageId":"1ccc6c1a-0a1e-46ff-8311-abdbbdb4a60d@gmail.com","threadId":"61514","inReplyTo":"xmqqwmn79u98.fsf@gitster.g","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-02T17:13:36Z","receivedAt":"2024-06-02T17:13:39Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Sun, Jun 02, 2024 at 09:36:35AM -0700, Junio C Hamano wrote:\n> Rubén Justo <rjusto@gmail.com> writes:\n> \n> > Hopefully, we'll find a way to avoid sending ANSI codes, on demand,\n> > without disabling it entirely with color.ui=never or any other global\n> > option.  To make this usable:\n> >\n> >   (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,p,|,?]? | vim -\n> >\n> > However, the current functionality meets my current needs, so I'm happy\n> > with it.\n> \n> Yup, if it really is needed we could do || or anything \"unusual\" to\n> signal the unusual nature of the command.\n> \n> Or \">\" command can send the output to specified file without\n> coloring, and the user can do whatever they want to it.  \n\nInteresting;  perhaps intuitive and in line with other 'isatty()'\nconditions we have.\n\n> \n> In any case, unlike \"Let's not just do pager, but have a facility to\n> pipe to anything and make the pager a default destination\" that was\n> a natural match to the originally proposed behaviour, these two are\n> quite different and can be left totally outside the scope of the\n> topic.\n> \n> > One final note;  I preferred to model the help text this way:\n> >\n> >     y - stage this hunk\n> >     n - do not stage this hunk\n> > ...\n> >     g - select a hunk to go to \n> >     / - search for a hunk matching the given regex\n> > ...\n> >     | - pipe the current hunk to the pager, or |<program> to use a program'\n> >     ? - print help\n> \n> That's fine.\n> \n> The 'g' and '/' commands take _mandatory_ arguments, but we do not\n> even mention it in the help text.  But we need to say something for\n> this new thing, because it is _optional_ and if you do not give a\n> program, it does not ask.\n> \n> A possibility is to phrase it like so:\n> \n>     | - pipe the current hunk to the program (or \"%s\" by default)\n> \n> and fill %s with the program you'd use if not given, i.e. initially\n> the value of the GIT_PAGER but updated to the last used program\n> after the user uses \"|<program>\" form to specify one.\n\nAnd this one too;  a nice way to allow reusing the previous value.\nPerhaps we'd better find a way to introduce some form of CTRL+P, or\narrow-up...  I dunno.\n\nInteresting ideas.  But my preference is to queue this series as it is,\nif no major issue is pointed out.  And leave for future series this\ntopics or the others mentioned in the thread: the 'interactive.'\nsetting suggested by Peff, or the '-P' switch, by Dragan.\n\nLet's see what others say.\n\nThanks.\n"},{"id":"496055","messageId":"7ab01cce-ee08-4ee2-8fcc-65324c340aaa@gmail.com","threadId":"61514","inReplyTo":"xmqqttib8e22.fsf@gitster.g","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-02T17:33:12Z","receivedAt":"2024-06-02T17:33:15Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Sun, Jun 02, 2024 at 10:11:49AM -0700, Junio C Hamano wrote:\n\n> By the way, although I personally do not have much sympathy to those\n> who set it, in the presence of interactive.singleKey configuration\n> variable, a command that takes optional argument may turn out to be\n> a mistake, as the user cannot give the argument even if they wanted\n> to, when the configuration variable is set to true.\n\nWell spotted.\n\nPerhaps we can do something like:\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 5a586d1b9b..01525214f9 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1685,8 +1685,17 @@ static int patch_update_file(struct add_p_state *s,\n \t\t} else if (s->answer.buf[0] == 'p') {\n \t\t\trendered_hunk_index = -1;\n \t\t} else if (ch == '|') {\n+\t\t\tstrbuf_remove(&s->answer, 0, 1);\n+\t\t\tif (s->s.use_single_key && !s->answer.len) {\n+\t\t\t\tprintf(\"%s\", _(\"program? \"));\n+\t\t\t\tfflush(stdout);\n+\t\t\t\tstrbuf_getline(&s->answer, stdin);\n+\t\t\t\tstrbuf_trim_trailing_newline(&s->answer);\n+\t\t\t}\n \t\t\trendered_hunk_index = -1;\n-\t\t\tpager = s->answer.buf + 1;\n+\t\t\tpager = s->answer.buf;\n \t\t} else if (s->answer.buf[0] == '?') {\n \t\t\tconst char *p = _(help_patch_remainder), *eol = p;\n \nI agree this needs to be addressed in this series.\n\nI'd prefer to leave the feature of reusing the command, to another\nseries.\n"},{"id":"496056","messageId":"81d52b31ce4c287765a43d87d94f526b@manjaro.org","threadId":"61514","inReplyTo":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-02T17:36:37Z","receivedAt":"2024-06-02T17:36:40Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"Hello Ruben,\n\nOn 2024-06-02 17:38, Rubén Justo wrote:\n> This iteration, v3, introduces a new command: '|', suggested by Junio,\n> instead of the 'P' command proposed in the previous iteration.\n> \n> This allows us to use the pager:\n> \n>   (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,p,|,?]? |\n> \n> But also to use other programs, like:\n> \n>   (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,p,|,?]? | head\n> \n> Or:\n> \n>   (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,p,|,?]? | grep term\n> \n> Hopefully, we'll find a way to avoid sending ANSI codes, on demand,\n> without disabling it entirely with color.ui=never or any other global\n> option.  To make this usable:\n> \n>   (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,p,|,?]? | vim -\n> \n> However, the current functionality meets my current needs, so I'm happy\n> with it.\n\nThe way I see it, using \"| <program>\" should follow the de facto rules\nalready established by the \"--color=auto\" command-line option in \nmultiple\nutilities.  Thus, when piping to a custom program, the escape codes that\nperform the coloring should be stripped.\n\n> This, a new 'interactive.pipeCommand' setting, or a new switch: 'add \n> -P',\n> are left for discussing in, hopefully, a future series.\n> \n> One final note;  I preferred to model the help text this way:\n> \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>     j - leave this hunk undecided, see next undecided hunk\n>     J - leave this hunk undecided, see next hunk\n>     g - select a hunk to go to\n>     / - search for a hunk matching the given regex\n>     s - split the current hunk into smaller hunks\n>     e - manually edit the current hunk\n>     p - print the current hunk\n>     | - pipe the current hunk to the pager, or |<program> to use a \n> program'\n>     ? - print help\n\nI also like this form better, but I think wording could be improved.\nI'll think a bit more about it, maybe something like this:\n\n       | - use pager to show the current hunk, or use |<program> to \ncustomize\n\nAlso, what's the single quote doing after \"use a program\"?\n\n> Instead of:\n> \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>     j - leave this hunk undecided, see next undecided hunk\n>     J - leave this hunk undecided, see next hunk\n>     g - select a hunk to go to\n>     / - search for a hunk matching the given regex\n>     s - split the current hunk into smaller hunks\n>     e - manually edit the current hunk\n>     p - print the current hunk\n>     |[program] - pipe the current hunk to a program, the pager if \n> none...\n>     ? - print help\n> \n> Because I believe it reads better by maintaining a single character\n> before the dash.  But I am not opposed to the latter.\n"},{"id":"496057","messageId":"ec2ca25486b84615e30dbeb83ec47310@manjaro.org","threadId":"61514","inReplyTo":"xmqqwmn79u98.fsf@gitster.g","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-02T17:46:53Z","receivedAt":"2024-06-02T17:46:55Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"Hello Junio,\n\nOn 2024-06-02 18:36, Junio C Hamano wrote:\n> A possibility is to phrase it like so:\n> \n>     | - pipe the current hunk to the program (or \"%s\" by default)\n> \n> and fill %s with the program you'd use if not given, i.e. initially\n> the value of the GIT_PAGER but updated to the last used program\n> after the user uses \"|<program>\" form to specify one.\n\nThe value of GIT_PAGER (or the <program>) can be a rather long string,\nso it would have to be stripped down to the base command, but it would\nbe rather error-prone and the printed information would become much less\ninformative that way.\n"},{"id":"496066","messageId":"xmqq34pu8kkg.fsf@gitster.g","threadId":"61514","inReplyTo":"ec2ca25486b84615e30dbeb83ec47310@manjaro.org","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-03T09:03:27Z","receivedAt":"2024-06-03T09:03:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dragan Simic <dsimic@manjaro.org> writes:\n\n> Hello Junio,\n>\n> On 2024-06-02 18:36, Junio C Hamano wrote:\n>> A possibility is to phrase it like so:\n>>     | - pipe the current hunk to the program (or \"%s\" by default)\n>> and fill %s with the program you'd use if not given, i.e. initially\n>> the value of the GIT_PAGER but updated to the last used program\n>> after the user uses \"|<program>\" form to specify one.\n>\n> The value of GIT_PAGER (or the <program>) can be a rather long string,\n> so it would have to be stripped down to the base command, but it would\n> be rather error-prone and the printed information would become much less\n> informative that way.\n\nI'd probably just say $GIT_PAGER instead of its expansion if we were\ngo that route.\n"},{"id":"496147","messageId":"fb2a9e98f0b2c3a009b0ad800c05522c@manjaro.org","threadId":"61514","inReplyTo":"xmqq34pu8kkg.fsf@gitster.g","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-03T10:21:05Z","receivedAt":"2024-06-03T10:21:08Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-06-03 11:03, Junio C Hamano wrote:\n> Dragan Simic <dsimic@manjaro.org> writes:\n>> On 2024-06-02 18:36, Junio C Hamano wrote:\n>>> A possibility is to phrase it like so:\n>>>     | - pipe the current hunk to the program (or \"%s\" by default)\n>>> and fill %s with the program you'd use if not given, i.e. initially\n>>> the value of the GIT_PAGER but updated to the last used program\n>>> after the user uses \"|<program>\" form to specify one.\n>> \n>> The value of GIT_PAGER (or the <program>) can be a rather long string,\n>> so it would have to be stripped down to the base command, but it would\n>> be rather error-prone and the printed information would become much \n>> less\n>> informative that way.\n> \n> I'd probably just say $GIT_PAGER instead of its expansion if we were\n> go that route.\n\nMakes sense to me.  More precisely, the environment should be checked\nto see is it \"$GIT_PAGER\" or \"$PAGER\" that needs to be printed literally\nas part of the help message.\n"},{"id":"496154","messageId":"xmqqsexu6o60.fsf@gitster.g","threadId":"61514","inReplyTo":"fb2a9e98f0b2c3a009b0ad800c05522c@manjaro.org","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-03T15:28:39Z","receivedAt":"2024-06-03T15:28:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dragan Simic <dsimic@manjaro.org> writes:\n\n>> I'd probably just say $GIT_PAGER instead of its expansion if we were\n>> go that route.\n>\n> Makes sense to me.  More precisely, the environment should be checked\n> to see is it \"$GIT_PAGER\" or \"$PAGER\" that needs to be printed literally\n> as part of the help message.\n\nI was sure somebody will split a hair like that.  At that point we\nare better off mentioning 'git var' X-<.  Or just 'Your Pager'.\n"},{"id":"496157","messageId":"xmqqcyoy6mnl.fsf@gitster.g","threadId":"61514","inReplyTo":"81d52b31ce4c287765a43d87d94f526b@manjaro.org","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-03T16:01:18Z","receivedAt":"2024-06-03T16:01:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dragan Simic <dsimic@manjaro.org> writes:\n\n> Thus, when piping to a custom program, the escape codes that\n> perform the coloring should be stripped.\n\nI tend to agree that if we do not give a way to toggle between\n\"with\" and \"without\" color when piping to a program, it is safer to\nmake the default uncolored.\n\nThe user's configured pager is expected to deal with colors just\nfine (or the user has globally configured colors to be off).  As we\nare capable of telling if the user is asking to spawn the default\npager (by not giving a custom command or by clearing the previous\ncustom command given in the same session) or a custom one, it should\nbe easily doable to give colored version to the configured pager and\nuncolored version to a custom/one-shot command.  Unlike the existing\nsupport for (e)dit command, we do not read back from what the\ncommand does using the hunk and present it again to the user, it\nshould be a relatively easy and safe thing to do.\n\n\n"},{"id":"496176","messageId":"9d05b41f-c120-4db0-9ee5-e24d20389129@gmail.com","threadId":"61514","inReplyTo":"81d52b31ce4c287765a43d87d94f526b@manjaro.org","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-03T20:19:34Z","receivedAt":"2024-06-03T20:19:37Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Sun, Jun 02, 2024 at 07:36:37PM +0200, Dragan Simic wrote:\n\n> The way I see it, using \"| <program>\" should follow the de facto rules\n> already established by the \"--color=auto\" command-line option in multiple\n> utilities.  Thus, when piping to a custom program, the escape codes that\n> perform the coloring should be stripped.\n\nInteresting.  However, I'd like to find a way to keep the escape codes\nwhen using programs like: '|head';  perhaps with the '>' command,\nsuggested by Junio.\n\nAt any rate, I feel we can leave that, perhaps corner-case scenario, for\na future series.  As this series is mainly about the 'pager' machinery.\n\n> \n> > This, a new 'interactive.pipeCommand' setting, or a new switch: 'add\n> > -P',\n> > are left for discussing in, hopefully, a future series.\n> > \n> > One final note;  I preferred to model the help text this way:\n> > \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> >     j - leave this hunk undecided, see next undecided hunk\n> >     J - leave this hunk undecided, see next hunk\n> >     g - select a hunk to go to\n> >     / - search for a hunk matching the given regex\n> >     s - split the current hunk into smaller hunks\n> >     e - manually edit the current hunk\n> >     p - print the current hunk\n> >     | - pipe the current hunk to the pager, or |<program> to use a\n> > program'\n> >     ? - print help\n> \n> I also like this form better, but I think wording could be improved.\n> I'll think a bit more about it, maybe something like this:\n> \n>       | - use pager to show the current hunk, or use |<program> to customize\n\nCertainly!  It is indeed a sensible idea to improve the wording, avoiding\nthe word \"pipe\" :-).  Thank you.\n\n> \n> Also, what's the single quote doing after \"use a program\"?\n\nJust a typo.  Sorry.\n\n> \n> > Instead of:\n> > \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> >     j - leave this hunk undecided, see next undecided hunk\n> >     J - leave this hunk undecided, see next hunk\n> >     g - select a hunk to go to\n> >     / - search for a hunk matching the given regex\n> >     s - split the current hunk into smaller hunks\n> >     e - manually edit the current hunk\n> >     p - print the current hunk\n> >     |[program] - pipe the current hunk to a program, the pager if\n> > none...\n> >     ? - print help\n> > \n> > Because I believe it reads better by maintaining a single character\n> > before the dash.  But I am not opposed to the latter.\n"},{"id":"496177","messageId":"1ef0ac3a-3be5-4fc2-93f8-46610f3d1880@gmail.com","threadId":"61514","inReplyTo":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","subject":"[PATCH v4 0/6] use the pager in 'add -p'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-03T20:35:36Z","receivedAt":"2024-06-03T20:35:39Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"This iteration is a quick reroll to address the interactive.singleKey\noption in the new '|' interactive command.\n\nI've also used the improved wording for the help text, suggested by\nDragan.\n\nThe rest of the series remains the same.  Here is the range-diff from\nv3:\n\nRange-diff against v3:\n-:  ---------- > 1:  49481da8a7 add-patch: test for 'p' command\n-:  ---------- > 2:  865bb68508 pager: do not close fd 2 unnecessarily\n-:  ---------- > 3:  9fcf244dac pager: introduce wait_for_pager\n-:  ---------- > 4:  1e817a9ec0 pager: introduce setup_custom_pager\n-:  ---------- > 5:  87dd368346 test-terminal: introduce --no-stdin-pty\n1:  9fa538478d ! 6:  b691764a17 add-patch: introduce the command '|'\n    @@ add-patch.c: N_(\"j - leave this hunk undecided, see next undecided hunk\\n\"\n         \"s - split the current hunk into smaller hunks\\n\"\n         \"e - manually edit the current hunk\\n\"\n         \"p - print the current hunk\\n\"\n    -+   \"| - pipe the current hunk to the pager, or |<program> to use a program'\\n\"\n    ++   \"| - use pager to show the current hunk, or use |<program> to customize\\n\"\n         \"? - print help\\n\");\n      \n      static int patch_update_file(struct add_p_state *s,\n    @@ add-patch.c: static int patch_update_file(struct add_p_state *s,\n      \t\t} else if (s->answer.buf[0] == 'p') {\n      \t\t\trendered_hunk_index = -1;\n     +\t\t} else if (ch == '|') {\n    ++\t\t\tstrbuf_remove(&s->answer, 0, 1);\n    ++\t\t\tif (s->s.use_single_key && s->answer.len == 0) {\n    ++\t\t\t\tprintf(\"%s\", _(\"program? \"));\n    ++\t\t\t\tfflush(stdout);\n    ++\t\t\t\tstrbuf_getline(&s->answer, stdin);\n    ++\t\t\t\tstrbuf_trim_trailing_newline(&s->answer);\n    ++\t\t\t}\n    ++\t\t\tstrbuf_trim(&s->answer);\n    ++\t\t\tpager = s->answer.buf;\n     +\t\t\trendered_hunk_index = -1;\n    -+\t\t\tpager = s->answer.buf + 1;\n      \t\t} else if (s->answer.buf[0] == '?') {\n      \t\t\tconst char *p = _(help_patch_remainder), *eol = p;\n      \n\nbase-commit: d3f616a4e56f359d84a9d439aa03dca1fe9ac280\n-- \n2.45.0.97.gb691764a17\n\n"},{"id":"496178","messageId":"28cc803a-1885-49fc-81b2-4ced1fbb8236@gmail.com","threadId":"61514","inReplyTo":"1ef0ac3a-3be5-4fc2-93f8-46610f3d1880@gmail.com","subject":"[PATCH v4 1/6] add-patch: test for 'p' command","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-03T20:38:04Z","receivedAt":"2024-06-03T20:38:06Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Add a test for the 'p' command, which was introduced in 66c14ab592\n(add-patch: introduce 'p' in interactive-patch, 2024-03-29).\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n t/t3701-add-interactive.sh | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 6624a4f7c0..6f6d174687 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -590,6 +590,22 @@ test_expect_success 'navigate to hunk via regex / pattern' '\n \ttest_cmp expect actual.trimmed\n '\n \n+test_expect_success 'print again the hunk' '\n+\ttest_when_finished \"git reset\" &&\n+\ttr _ \" \" >expect <<-EOF &&\n+\t+15\n+\t 20\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@\n+\t 10\n+\t+15\n+\t 20\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\tEOF\n+\ttest_write_lines s y g 1 p | git add -p >actual &&\n+\ttail -n 7 <actual >actual.trimmed &&\n+\ttest_cmp expect actual.trimmed\n+'\n+\n test_expect_success 'split hunk \"add -p (edit)\"' '\n \t# Split, say Edit and do nothing.  Then:\n \t#\n-- \n2.45.0.97.gb691764a17\n"},{"id":"496179","messageId":"e98dc7b1-3c93-41d2-a2ef-7f9f69789886@gmail.com","threadId":"61514","inReplyTo":"1ef0ac3a-3be5-4fc2-93f8-46610f3d1880@gmail.com","subject":"[PATCH v4 2/6] pager: do not close fd 2 unnecessarily","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-03T20:38:12Z","receivedAt":"2024-06-03T20:38:15Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"We send errors to the pager since 61b80509e3 (sending errors to stdout\nunder $PAGER, 2008-02-16).\n\nIn a8335024c2 (pager: do not dup2 stderr if it is already redirected,\n2008-12-15) an exception was introduced to avoid redirecting stderr if\nit is not connected to a terminal.\n\nIn such exceptional cases, the close(STDERR_FILENO) we're doing in\nclose_pager_fds, is unnecessary.\n\nFurthermore, in a subsequent commit we're going to introduce changes\nthat will involve using close_pager_fds multiple times.\n\nWith this in mind, controlling when we want to close stderr, become\nsensible.\n\nLet's close(STDERR_FILENO) only when necessary, and pave the way for the\nupcoming changes.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n pager.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex b8822a9381..b786601074 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -14,6 +14,7 @@ int pager_use_color = 1;\n \n static struct child_process pager_process;\n static const char *pager_program;\n+static int close_fd2;\n \n /* Is the value coming back from term_columns() just a guess? */\n static int term_columns_guessed;\n@@ -23,7 +24,8 @@ static void close_pager_fds(void)\n {\n \t/* signal EOF to pager */\n \tclose(1);\n-\tclose(2);\n+\tif (close_fd2)\n+\t\tclose(2);\n }\n \n static void wait_for_pager_atexit(void)\n@@ -141,8 +143,10 @@ void setup_pager(void)\n \n \t/* original process continues, but writes to the pipe */\n \tdup2(pager_process.in, 1);\n-\tif (isatty(2))\n+\tif (isatty(2)) {\n+\t\tclose_fd2 = 1;\n \t\tdup2(pager_process.in, 2);\n+\t}\n \tclose(pager_process.in);\n \n \t/* this makes sure that the parent terminates after the pager */\n-- \n2.45.0.97.gb691764a17\n"},{"id":"496180","messageId":"76c725b4-1bc4-4916-81d8-98cad8fc4ca0@gmail.com","threadId":"61514","inReplyTo":"1ef0ac3a-3be5-4fc2-93f8-46610f3d1880@gmail.com","subject":"[PATCH v4 3/6] pager: introduce wait_for_pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-03T20:38:19Z","receivedAt":"2024-06-03T20:38:22Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Since f67b45f862 (Introduce trivial new pager.c helper infrastructure,\n2006-02-28) we have the machinery to send our output to a pager.\n\nThat machinery, once set up, does not allow us to regain the original\nstdio streams.\n\nIn the interactive commands (i.e.: add -p) we want to use the pager for\nsome output, while maintaining the interaction with the user.\n\nModify the pager machinery so that we can use setup_pager and, once\nwe've finished sending the desired output for the pager, wait for the\npager termination using a new function wait_for_pager.   Make this\nfunction reset the pager machinery before returning.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n pager.c | 36 ++++++++++++++++++++++++++++++------\n pager.h |  1 +\n 2 files changed, 31 insertions(+), 6 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex b786601074..925f860335 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -14,7 +14,7 @@ int pager_use_color = 1;\n \n static struct child_process pager_process;\n static const char *pager_program;\n-static int close_fd2;\n+static int old_fd1 = -1, old_fd2 = -1;\n \n /* Is the value coming back from term_columns() just a guess? */\n static int term_columns_guessed;\n@@ -24,20 +24,41 @@ static void close_pager_fds(void)\n {\n \t/* signal EOF to pager */\n \tclose(1);\n-\tif (close_fd2)\n+\tif (old_fd2 != -1)\n \t\tclose(2);\n }\n \n static void wait_for_pager_atexit(void)\n {\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n \tfflush(stdout);\n \tfflush(stderr);\n \tclose_pager_fds();\n \tfinish_command(&pager_process);\n }\n \n+void wait_for_pager(void)\n+{\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n+\twait_for_pager_atexit();\n+\tunsetenv(\"GIT_PAGER_IN_USE\");\n+\tdup2(old_fd1, 1);\n+\told_fd1 = -1;\n+\tif (old_fd2 != -1) {\n+\t\tdup2(old_fd2, 2);\n+\t\told_fd2 = -1;\n+\t}\n+}\n+\n static void wait_for_pager_signal(int signo)\n {\n+\tif (old_fd1 == -1)\n+\t\treturn;\n+\n \tclose_pager_fds();\n \tfinish_command_in_signal(&pager_process);\n \tsigchain_pop(signo);\n@@ -113,6 +134,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)\n \n void setup_pager(void)\n {\n+\tstatic int once = 0;\n \tconst char *pager = git_pager(isatty(1));\n \n \tif (!pager)\n@@ -142,16 +164,18 @@ void setup_pager(void)\n \t\treturn;\n \n \t/* original process continues, but writes to the pipe */\n+\told_fd1 = dup(1);\n \tdup2(pager_process.in, 1);\n \tif (isatty(2)) {\n-\t\tclose_fd2 = 1;\n+\t\told_fd2 = dup(2);\n \t\tdup2(pager_process.in, 2);\n \t}\n \tclose(pager_process.in);\n \n-\t/* this makes sure that the parent terminates after the pager */\n-\tsigchain_push_common(wait_for_pager_signal);\n-\tatexit(wait_for_pager_atexit);\n+\tif (!once++) {\n+\t\tsigchain_push_common(wait_for_pager_signal);\n+\t\tatexit(wait_for_pager_atexit);\n+\t}\n }\n \n int pager_in_use(void)\ndiff --git a/pager.h b/pager.h\nindex b77433026d..103ecac476 100644\n--- a/pager.h\n+++ b/pager.h\n@@ -5,6 +5,7 @@ struct child_process;\n \n const char *git_pager(int stdout_is_tty);\n void setup_pager(void);\n+void wait_for_pager(void);\n int pager_in_use(void);\n int term_columns(void);\n void term_clear_line(void);\n-- \n2.45.0.97.gb691764a17\n"},{"id":"496181","messageId":"3fa0ade6-d710-47ce-8bcd-81d7821dfffb@gmail.com","threadId":"61514","inReplyTo":"1ef0ac3a-3be5-4fc2-93f8-46610f3d1880@gmail.com","subject":"[PATCH v4 4/6] pager: introduce setup_custom_pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-03T20:38:27Z","receivedAt":"2024-06-03T20:38:29Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Introduce a new function setup_custom_pager() to allow setting up our\npager mechanism using a custom pager.  If the custom pager specified is\nNULL or an empty string, use the normal pager as setup_pager() currently\ndoes.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n pager.c | 17 +++++++++++------\n pager.h |  6 +++++-\n 2 files changed, 16 insertions(+), 7 deletions(-)\n\ndiff --git a/pager.c b/pager.c\nindex 925f860335..21a7d9cd60 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -74,14 +74,13 @@ static int core_pager_config(const char *var, const char *value,\n \treturn 0;\n }\n \n-const char *git_pager(int stdout_is_tty)\n+static const char *git_pager_custom(int stdout_is_tty, const char* pager)\n {\n-\tconst char *pager;\n-\n \tif (!stdout_is_tty)\n \t\treturn NULL;\n \n-\tpager = getenv(\"GIT_PAGER\");\n+\tif (!pager || !*pager)\n+\t\tpager = getenv(\"GIT_PAGER\");\n \tif (!pager) {\n \t\tif (!pager_program)\n \t\t\tread_early_config(core_pager_config, NULL);\n@@ -97,6 +96,11 @@ const char *git_pager(int stdout_is_tty)\n \treturn pager;\n }\n \n+const char *git_pager(int stdout_is_tty)\n+{\n+\treturn git_pager_custom(stdout_is_tty, NULL);\n+}\n+\n static void setup_pager_env(struct strvec *env)\n {\n \tconst char **argv;\n@@ -132,10 +136,11 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)\n \tpager_process->trace2_child_class = \"pager\";\n }\n \n-void setup_pager(void)\n+void setup_custom_pager(const char* pager)\n {\n \tstatic int once = 0;\n-\tconst char *pager = git_pager(isatty(1));\n+\n+\tpager = git_pager_custom(isatty(1), pager);\n \n \tif (!pager)\n \t\treturn;\ndiff --git a/pager.h b/pager.h\nindex 103ecac476..2166662361 100644\n--- a/pager.h\n+++ b/pager.h\n@@ -4,7 +4,11 @@\n struct child_process;\n \n const char *git_pager(int stdout_is_tty);\n-void setup_pager(void);\n+void setup_custom_pager(const char*);\n+static inline void setup_pager(void)\n+{\n+\tsetup_custom_pager(NULL);\n+}\n void wait_for_pager(void);\n int pager_in_use(void);\n int term_columns(void);\n-- \n2.45.0.97.gb691764a17\n"},{"id":"496182","messageId":"d95180fc-8f8a-4e1d-987d-3aa0811be7de@gmail.com","threadId":"61514","inReplyTo":"1ef0ac3a-3be5-4fc2-93f8-46610f3d1880@gmail.com","subject":"[PATCH v4 5/6] test-terminal: introduce --no-stdin-pty","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-03T20:38:33Z","receivedAt":"2024-06-03T20:38:36Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"In 18d8c26930 (test_terminal: redirect child process' stdin to a pty,\n2015-08-04), t/test-terminal.perl learned to connect the child process'\nstdin to a pty.  It works well for what was intended: satisfying an\n`isatty(STDIN_FILENO)` check.\n\nHowever, the fork introduced, that copies the stdin to the child\nprocess, does not always manage to send all the information.\n\nTo illustrate this behaviour, we can use a function like this:\n\n    f ()\n    {\n    \tdd if=/dev/zero bs=1 count=10000 status=none |\n    \tt/test-terminal.perl cat - 2>/dev/null |\n    \twc -c;\n    }\n\nWe do not obtain the expected results when executing this function\n100 times:\n\n    $ for i in $(seq 100); do f; done | sort | uniq -c\n         36 0\n          4 1\n         53 4095\n          7 4159\n\nIf we do the same with a version that does not redirect stdin, a version\nprior to 18d8c26930, the expected result is obtained:\n\n    $ git checkout 18d8c26930~1\n    $ for i in $(seq 100); do f; done | sort | uniq -c\n        100 10000\n\nIn a subsequent commit, a new test is going to rely on test-terminate,\nand it does not require stdin to be connected to a terminal, but all\npiped data needs to be successfully transmitted to the child process.\n\nTo make this possible, add a new parameter \"--no-stdin-pty\" to allow\ndisabling the stdin redirection though a pty.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n t/test-terminal.perl | 32 ++++++++++++++++++--------------\n 1 file changed, 18 insertions(+), 14 deletions(-)\n\ndiff --git a/t/test-terminal.perl b/t/test-terminal.perl\nindex 3810e9bb43..85edc9e8b9 100755\n--- a/t/test-terminal.perl\n+++ b/t/test-terminal.perl\n@@ -12,10 +12,10 @@ sub start_child {\n \tif (not defined $pid) {\n \t\tdie \"fork failed: $!\"\n \t} elsif ($pid == 0) {\n-\t\topen STDIN, \"<&\", $in;\n+\t\topen STDIN, \"<&\", $in if $in;\n \t\topen STDOUT, \">&\", $out;\n \t\topen STDERR, \">&\", $err;\n-\t\tclose $in;\n+\t\tclose $in if $in;\n \t\tclose $out;\n \t\texec(@$argv) or die \"cannot exec '$argv->[0]': $!\"\n \t}\n@@ -78,28 +78,32 @@ sub copy_stdio {\n }\n \n if ($#ARGV < 1) {\n-\tdie \"usage: test-terminal program args\";\n+\tdie \"usage: test-terminal [--no-stdin-pty] program args\";\n }\n+my $no_stdin_pty = $ARGV[0] eq '--no-stdin-pty';\n+shift @ARGV if $no_stdin_pty;\n $ENV{TERM} = 'vt100';\n-my $parent_in = new IO::Pty;\n+my $parent_in = $no_stdin_pty ? undef : IO::Pty->new;\n my $parent_out = new IO::Pty;\n my $parent_err = new IO::Pty;\n-$parent_in->set_raw();\n+$parent_in->set_raw() if $parent_in;\n $parent_out->set_raw();\n $parent_err->set_raw();\n-$parent_in->slave->set_raw();\n+$parent_in->slave->set_raw() if $parent_in;\n $parent_out->slave->set_raw();\n $parent_err->slave->set_raw();\n-my $pid = start_child(\\@ARGV, $parent_in->slave, $parent_out->slave, $parent_err->slave);\n-close $parent_in->slave;\n+my $pid = start_child(\\@ARGV,$parent_in ? $parent_in->slave : undef, $parent_out->slave, $parent_err->slave);\n+close $parent_in->slave if $parent_in;\n close $parent_out->slave;\n close $parent_err->slave;\n-my $in_pid = copy_stdin($parent_in);\n+my $in_pid = $no_stdin_pty ? 0 : copy_stdin($parent_in);\n copy_stdio($parent_out, $parent_err);\n my $ret = finish_child($pid);\n-# If the child process terminates before our copy_stdin() process is able to\n-# write all of its data to $parent_in, the copy_stdin() process could stall.\n-# Send SIGTERM to it to ensure it terminates.\n-kill 'TERM', $in_pid;\n-finish_child($in_pid);\n+if ($in_pid) {\n+\t# If the child process terminates before our copy_stdin() process is able to\n+\t# write all of its data to $parent_in, the copy_stdin() process could stall.\n+\t# Send SIGTERM to it to ensure it terminates.\n+\tkill 'TERM', $in_pid;\n+\tfinish_child($in_pid);\n+}\n exit($ret);\n-- \n2.45.0.97.gb691764a17\n"},{"id":"496183","messageId":"75a3cc89-4d23-4eae-b0ad-e52e2c8ba550@gmail.com","threadId":"61514","inReplyTo":"1ef0ac3a-3be5-4fc2-93f8-46610f3d1880@gmail.com","subject":"[PATCH v4 6/6] add-patch: introduce the command '|'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-03T20:38:41Z","receivedAt":"2024-06-03T20:38:44Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"Introduce a new command '|' to send the current hunk to a program.  If\nno program is specified, use the pager.\n\nSigned-off-by: Rubén Justo <rjusto@gmail.com>\n---\n add-patch.c                | 25 +++++++++++++++--\n t/t3701-add-interactive.sh | 55 ++++++++++++++++++++++++++------------\n 2 files changed, 61 insertions(+), 19 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 814de57c4a..5d8a2f97f9 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -5,6 +5,7 @@\n #include \"environment.h\"\n #include \"gettext.h\"\n #include \"object-name.h\"\n+#include \"pager.h\"\n #include \"read-cache-ll.h\"\n #include \"repository.h\"\n #include \"strbuf.h\"\n@@ -1389,6 +1390,7 @@ N_(\"j - leave this hunk undecided, see next undecided hunk\\n\"\n    \"s - split the current hunk into smaller hunks\\n\"\n    \"e - manually edit the current hunk\\n\"\n    \"p - print the current hunk\\n\"\n+   \"| - use pager to show the current hunk, or use |<program> to customize\\n\"\n    \"? - print help\\n\");\n \n static int patch_update_file(struct add_p_state *s,\n@@ -1401,6 +1403,7 @@ static int patch_update_file(struct add_p_state *s,\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint colored = !!s->colored.len, quit = 0;\n \tenum prompt_mode_type prompt_mode_type;\n+\tconst char* pager = NULL;\n \tenum {\n \t\tALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,\n \t\tALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,\n@@ -1449,9 +1452,15 @@ static int patch_update_file(struct add_p_state *s,\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n \t\t\tif (rendered_hunk_index != hunk_index) {\n+\t\t\t\tif (pager)\n+\t\t\t\t\tsetup_custom_pager(pager);\n \t\t\t\trender_hunk(s, hunk, 0, colored, &s->buf);\n \t\t\t\tfputs(s->buf.buf, stdout);\n \t\t\t\trendered_hunk_index = hunk_index;\n+\t\t\t\tif (pager) {\n+\t\t\t\t\twait_for_pager();\n+\t\t\t\t\tpager = NULL;\n+\t\t\t\t}\n \t\t\t}\n \n \t\t\tstrbuf_reset(&s->buf);\n@@ -1485,6 +1494,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\tstrbuf_addstr(&s->buf, \",e\");\n \t\t\t}\n \t\t\tstrbuf_addstr(&s->buf, \",p\");\n+\t\t\tstrbuf_addstr(&s->buf, \",|\");\n \t\t}\n \t\tif (file_diff->deleted)\n \t\t\tprompt_mode_type = PROMPT_DELETION;\n@@ -1512,8 +1522,8 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\tcontinue;\n \t\tch = tolower(s->answer.buf[0]);\n \n-\t\t/* 'g' takes a hunk number and '/' takes a regexp */\n-\t\tif (s->answer.len != 1 && (ch != 'g' && ch != '/')) {\n+\t\t/* 'g' takes a hunk number, '/' takes a regexp and '|' takes a program */\n+\t\tif (s->answer.len != 1 && (ch != 'g' && ch != '/' && ch != '|')) {\n \t\t\terr(s, _(\"Only one letter is expected, got '%s'\"), s->answer.buf);\n \t\t\tcontinue;\n \t\t}\n@@ -1674,6 +1684,17 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t}\n \t\t} else if (s->answer.buf[0] == 'p') {\n \t\t\trendered_hunk_index = -1;\n+\t\t} else if (ch == '|') {\n+\t\t\tstrbuf_remove(&s->answer, 0, 1);\n+\t\t\tif (s->s.use_single_key && s->answer.len == 0) {\n+\t\t\t\tprintf(\"%s\", _(\"program? \"));\n+\t\t\t\tfflush(stdout);\n+\t\t\t\tstrbuf_getline(&s->answer, stdin);\n+\t\t\t\tstrbuf_trim_trailing_newline(&s->answer);\n+\t\t\t}\n+\t\t\tstrbuf_trim(&s->answer);\n+\t\t\tpager = s->answer.buf;\n+\t\t\trendered_hunk_index = -1;\n \t\t} else if (s->answer.buf[0] == '?') {\n \t\t\tconst char *p = _(help_patch_remainder), *eol = p;\n \ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 6f6d174687..7b3ebb671d 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -64,8 +64,8 @@ test_expect_success 'unknown command' '\n \tgit add -N command &&\n \tgit diff command >expect &&\n \tcat >>expect <<-EOF &&\n-\t(1/1) Stage addition [y,n,q,a,d,e,p,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help)\n-\t(1/1) Stage addition [y,n,q,a,d,e,p,?]?$SP\n+\t(1/1) Stage addition [y,n,q,a,d,e,p,|,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help)\n+\t(1/1) Stage addition [y,n,q,a,d,e,p,|,?]?$SP\n \tEOF\n \tgit add -p -- command <command >actual 2>&1 &&\n \ttest_cmp expect actual\n@@ -348,9 +348,9 @@ test_expect_success 'different prompts for mode change/deleted' '\n \tgit -c core.filemode=true add -p >actual &&\n \tsed -n \"s/^\\(([0-9/]*) Stage .*?\\).*/\\1/p\" actual >actual.filtered &&\n \tcat >expect <<-\\EOF &&\n-\t(1/1) Stage deletion [y,n,q,a,d,p,?]?\n-\t(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,?]?\n-\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]?\n+\t(1/1) Stage deletion [y,n,q,a,d,p,|,?]?\n+\t(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,|,?]?\n+\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]?\n \tEOF\n \ttest_cmp expect actual.filtered\n '\n@@ -537,13 +537,13 @@ test_expect_success 'split hunk setup' '\n test_expect_success 'goto hunk 1 with \"g 1\"' '\n \ttest_when_finished \"git reset\" &&\n \ttr _ \" \" >expect <<-EOF &&\n-\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1:  -1,2 +1,3          +15\n+\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]? + 1:  -1,2 +1,3          +15\n \t_ 2:  -2,4 +3,8          +21\n \tgo to which hunk? @@ -1,2 +1,3 @@\n \t_10\n \t+15\n \t_20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n \tEOF\n \ttest_write_lines s y g 1 | git add -p >actual &&\n \ttail -n 7 <actual >actual.trimmed &&\n@@ -556,7 +556,7 @@ test_expect_success 'goto hunk 1 with \"g1\"' '\n \t_10\n \t+15\n \t_20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n \tEOF\n \ttest_write_lines s y g1 | git add -p >actual &&\n \ttail -n 4 <actual >actual.trimmed &&\n@@ -566,11 +566,11 @@ test_expect_success 'goto hunk 1 with \"g1\"' '\n test_expect_success 'navigate to hunk via regex /pattern' '\n \ttest_when_finished \"git reset\" &&\n \ttr _ \" \" >expect <<-EOF &&\n-\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@\n+\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]? @@ -1,2 +1,3 @@\n \t_10\n \t+15\n \t_20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n \tEOF\n \ttest_write_lines s y /1,2 | git add -p >actual &&\n \ttail -n 5 <actual >actual.trimmed &&\n@@ -583,7 +583,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '\n \t_10\n \t+15\n \t_20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n \tEOF\n \ttest_write_lines s y / 1,2 | git add -p >actual &&\n \ttail -n 4 <actual >actual.trimmed &&\n@@ -595,17 +595,38 @@ test_expect_success 'print again the hunk' '\n \ttr _ \" \" >expect <<-EOF &&\n \t+15\n \t 20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? @@ -1,2 +1,3 @@\n \t 10\n \t+15\n \t 20\n-\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n+\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n \tEOF\n \ttest_write_lines s y g 1 p | git add -p >actual &&\n \ttail -n 7 <actual >actual.trimmed &&\n \ttest_cmp expect actual.trimmed\n '\n \n+test_expect_success TTY 'print again the hunk (PAGER)' '\n+\ttest_when_finished \"git reset\" &&\n+\tcat >expect <<-EOF &&\n+\t<GREEN>+<RESET><GREEN>15<RESET>\n+\t 20<RESET>\n+\t<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>\n+\tPAGER  10<RESET>\n+\tPAGER <GREEN>+<RESET><GREEN>15<RESET>\n+\tPAGER  20<RESET>\n+\t<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? <RESET>\n+\tEOF\n+\ttest_write_lines s y g 1 \\| |\n+\t(\n+\t\tGIT_PAGER=\"sed s/^/PAGER\\ /\" &&\n+\t\texport GIT_PAGER &&\n+\t\ttest_terminal --no-stdin-pty git add -p >actual\n+\t) &&\n+\ttail -n 7 <actual | test_decode_color >actual.trimmed &&\n+\ttest_cmp expect actual.trimmed\n+'\n+\n test_expect_success 'split hunk \"add -p (edit)\"' '\n \t# Split, say Edit and do nothing.  Then:\n \t#\n@@ -780,21 +801,21 @@ test_expect_success 'colors can be overridden' '\n \t<BLUE>+<RESET><BLUE>new<RESET>\n \t<CYAN> more-context<RESET>\n \t<BLUE>+<RESET><BLUE>another-one<RESET>\n-\t<YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,?]? <RESET><BOLD>Split into 2 hunks.<RESET>\n+\t<YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,|,?]? <RESET><BOLD>Split into 2 hunks.<RESET>\n \t<MAGENTA>@@ -1,3 +1,3 @@<RESET>\n \t<CYAN> context<RESET>\n \t<BOLD>-old<RESET>\n \t<BLUE>+<RESET><BLUE>new<RESET>\n \t<CYAN> more-context<RESET>\n-\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>\n+\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>\n \t<CYAN> more-context<RESET>\n \t<BLUE>+<RESET><BLUE>another-one<RESET>\n-\t<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>\n+\t<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>\n \t<CYAN> context<RESET>\n \t<BOLD>-old<RESET>\n \t<BLUE>+new<RESET>\n \t<CYAN> more-context<RESET>\n-\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>\n+\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? <RESET>\n \tEOF\n \ttest_cmp expect actual\n '\n-- \n2.45.0.97.gb691764a17\n"},{"id":"496220","messageId":"3f085795-79bd-4a56-9df8-659e32179925@gmail.com","threadId":"61514","inReplyTo":"76c725b4-1bc4-4916-81d8-98cad8fc4ca0@gmail.com","subject":"Re: [PATCH v4 3/6] pager: introduce wait_for_pager","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-06-04T10:00:37Z","receivedAt":"2024-06-04T10:00:39Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Rubén\n\nOn 03/06/2024 21:38, Rubén Justo wrote:\n> Since f67b45f862 (Introduce trivial new pager.c helper infrastructure,\n> 2006-02-28) we have the machinery to send our output to a pager.\n> \n> That machinery, once set up, does not allow us to regain the original\n> stdio streams.\n> \n> In the interactive commands (i.e.: add -p) we want to use the pager for\n> some output, while maintaining the interaction with the user.\n> \n> Modify the pager machinery so that we can use setup_pager and, once\n> we've finished sending the desired output for the pager, wait for the\n> pager termination using a new function wait_for_pager.   Make this\n> function reset the pager machinery before returning.\n\nThis makes sense, I've left a few comments below\n\n> Signed-off-by: Rubén Justo <rjusto@gmail.com>\n> ---\n\n>   static void wait_for_pager_atexit(void)\n>   {\n> +\tif (old_fd1 == -1)\n> +\t\treturn;\n> +\n\nThis is good - we'll return early if we've already cleaned up the pager.\n\n>   \tfflush(stdout);\n>   \tfflush(stderr);\n>   \tclose_pager_fds();\n>   \tfinish_command(&pager_process);\n>   }\n>   \n> +void wait_for_pager(void)\n> +{\n> +\tif (old_fd1 == -1)\n> +\t\treturn;\n\nIsn't it a bug to call this with old_fd1 == -1 or have I missed something?\n\n> +\twait_for_pager_atexit();\n> +\tunsetenv(\"GIT_PAGER_IN_USE\");\n> +\tdup2(old_fd1, 1);\n> +\told_fd1 = -1;\n> +\tif (old_fd2 != -1) {\n> +\t\tdup2(old_fd2, 2);\n> +\t\told_fd2 = -1;\n\nWe're leaking old_fd1 and old_fd2 here. wait_for_pager_atexit() flushes \nstdout and stderr so this switching of fds should play nicely with code \nthat uses stdio.\n\n> @@ -113,6 +134,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)\n>   \n>   void setup_pager(void)\n>   {\n> +\tstatic int once = 0;\n>   \tconst char *pager = git_pager(isatty(1));\n>   \n>   \tif (!pager)\n> @@ -142,16 +164,18 @@ void setup_pager(void)\n>   \t\treturn;\n>   \n>   \t/* original process continues, but writes to the pipe */\n> +\told_fd1 = dup(1);\n>   \tdup2(pager_process.in, 1);\n>   \tif (isatty(2)) {\n> -\t\tclose_fd2 = 1;\n> +\t\told_fd2 = dup(2);\n>   \t\tdup2(pager_process.in, 2);\n>   \t}\n>   \tclose(pager_process.in);\n>   \n> -\t/* this makes sure that the parent terminates after the pager */\n> -\tsigchain_push_common(wait_for_pager_signal);\n> -\tatexit(wait_for_pager_atexit);\n> +\tif (!once++) {\n\nWe only need to increment \"once\" when we enter this block, not every \ntime the code is run.\n\n> +\t\tsigchain_push_common(wait_for_pager_signal);\n\nI think we should be calling this each time we setup the pager and pop \nit in wait_for_pager(). Imagine a caller sets up a signal handler before \ncalling setup_pager() and wants to pop it after the pager has finished\n\n\tsigchain_push(...)\n\tsetup_pager(...)\n\tdo_something()\n\twait_for_pager()\n\tsigchain_pop(...)\n\nWith the changes here it will pop the signal handler added by \nsetup_pager() rather than the one it is expecting.\n\n> +\t\tatexit(wait_for_pager_atexit);\n\nIt is a bit of a shame we have to leave this function active when the \npager has finished. We could add a wrapper around atexit() that allows \nus to pop functions we no-longer want to call but I don't think it is \nworth the effort here. wait_for_pager_atexit() is careful to return \nearly if it is not needed.\n\n\nBest Wishes\n\nPhillip\n\n"},{"id":"496222","messageId":"600d27c1-f9e2-4a03-af24-4de8f66526d6@gmail.com","threadId":"61514","inReplyTo":"d95180fc-8f8a-4e1d-987d-3aa0811be7de@gmail.com","subject":"Re: [PATCH v4 5/6] test-terminal: introduce --no-stdin-pty","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-06-04T10:05:15Z","receivedAt":"2024-06-04T10:05:18Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Rubén\n\nOn 03/06/2024 21:38, Rubén Justo wrote:\n> In 18d8c26930 (test_terminal: redirect child process' stdin to a pty,\n> 2015-08-04), t/test-terminal.perl learned to connect the child process'\n> stdin to a pty.  It works well for what was intended: satisfying an\n> `isatty(STDIN_FILENO)` check.\n> \n> However, the fork introduced, that copies the stdin to the child\n> process, does not always manage to send all the information.\n\nI think the problem maybe to do with the use of File::Copy, not with the \nfork. The man page for the copy function says\n\n     Note  that  passing  in  files  as  handles  instead  of  names may\n     lead to loss of information on some operating systems; it is\n     recommended that you use file names whenever possible.\n\nRather than adding a new flag to work around a bug in our script it \nmight be better to try and fix the bug by using a loop that reads blocks \nof data from the source and writes them to the destination instead of \ncalling copy.\n\nBest Wishes\n\nPhillip\n\n> To illustrate this behaviour, we can use a function like this:\n> \n>      f ()\n>      {\n>      \tdd if=/dev/zero bs=1 count=10000 status=none |\n>      \tt/test-terminal.perl cat - 2>/dev/null |\n>      \twc -c;\n>      }\n> \n> We do not obtain the expected results when executing this function\n> 100 times:\n> \n>      $ for i in $(seq 100); do f; done | sort | uniq -c\n>           36 0\n>            4 1\n>           53 4095\n>            7 4159\n> \n> If we do the same with a version that does not redirect stdin, a version\n> prior to 18d8c26930, the expected result is obtained:\n> \n>      $ git checkout 18d8c26930~1\n>      $ for i in $(seq 100); do f; done | sort | uniq -c\n>          100 10000\n> \n> In a subsequent commit, a new test is going to rely on test-terminate,\n> and it does not require stdin to be connected to a terminal, but all\n> piped data needs to be successfully transmitted to the child process.\n> \n> To make this possible, add a new parameter \"--no-stdin-pty\" to allow\n> disabling the stdin redirection though a pty.\n> \n> Signed-off-by: Rubén Justo <rjusto@gmail.com>\n> ---\n>   t/test-terminal.perl | 32 ++++++++++++++++++--------------\n>   1 file changed, 18 insertions(+), 14 deletions(-)\n> \n> diff --git a/t/test-terminal.perl b/t/test-terminal.perl\n> index 3810e9bb43..85edc9e8b9 100755\n> --- a/t/test-terminal.perl\n> +++ b/t/test-terminal.perl\n> @@ -12,10 +12,10 @@ sub start_child {\n>   \tif (not defined $pid) {\n>   \t\tdie \"fork failed: $!\"\n>   \t} elsif ($pid == 0) {\n> -\t\topen STDIN, \"<&\", $in;\n> +\t\topen STDIN, \"<&\", $in if $in;\n>   \t\topen STDOUT, \">&\", $out;\n>   \t\topen STDERR, \">&\", $err;\n> -\t\tclose $in;\n> +\t\tclose $in if $in;\n>   \t\tclose $out;\n>   \t\texec(@$argv) or die \"cannot exec '$argv->[0]': $!\"\n>   \t}\n> @@ -78,28 +78,32 @@ sub copy_stdio {\n>   }\n>   \n>   if ($#ARGV < 1) {\n> -\tdie \"usage: test-terminal program args\";\n> +\tdie \"usage: test-terminal [--no-stdin-pty] program args\";\n>   }\n> +my $no_stdin_pty = $ARGV[0] eq '--no-stdin-pty';\n> +shift @ARGV if $no_stdin_pty;\n>   $ENV{TERM} = 'vt100';\n> -my $parent_in = new IO::Pty;\n> +my $parent_in = $no_stdin_pty ? undef : IO::Pty->new;\n>   my $parent_out = new IO::Pty;\n>   my $parent_err = new IO::Pty;\n> -$parent_in->set_raw();\n> +$parent_in->set_raw() if $parent_in;\n>   $parent_out->set_raw();\n>   $parent_err->set_raw();\n> -$parent_in->slave->set_raw();\n> +$parent_in->slave->set_raw() if $parent_in;\n>   $parent_out->slave->set_raw();\n>   $parent_err->slave->set_raw();\n> -my $pid = start_child(\\@ARGV, $parent_in->slave, $parent_out->slave, $parent_err->slave);\n> -close $parent_in->slave;\n> +my $pid = start_child(\\@ARGV,$parent_in ? $parent_in->slave : undef, $parent_out->slave, $parent_err->slave);\n> +close $parent_in->slave if $parent_in;\n>   close $parent_out->slave;\n>   close $parent_err->slave;\n> -my $in_pid = copy_stdin($parent_in);\n> +my $in_pid = $no_stdin_pty ? 0 : copy_stdin($parent_in);\n>   copy_stdio($parent_out, $parent_err);\n>   my $ret = finish_child($pid);\n> -# If the child process terminates before our copy_stdin() process is able to\n> -# write all of its data to $parent_in, the copy_stdin() process could stall.\n> -# Send SIGTERM to it to ensure it terminates.\n> -kill 'TERM', $in_pid;\n> -finish_child($in_pid);\n> +if ($in_pid) {\n> +\t# If the child process terminates before our copy_stdin() process is able to\n> +\t# write all of its data to $parent_in, the copy_stdin() process could stall.\n> +\t# Send SIGTERM to it to ensure it terminates.\n> +\tkill 'TERM', $in_pid;\n> +\tfinish_child($in_pid);\n> +}\n>   exit($ret);\n\n"},{"id":"496237","messageId":"20240604101700.GA1781455@coredump.intra.peff.net","threadId":"61514","inReplyTo":"b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-06-04T10:17:00Z","receivedAt":"2024-06-04T10:17:01Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Jun 02, 2024 at 05:38:42PM +0200, Rubén Justo wrote:\n\n> However, the current functionality meets my current needs, so I'm happy\n> with it.\n> \n> This, a new 'interactive.pipeCommand' setting, or a new switch: 'add -P',\n> are left for discussing in, hopefully, a future series.\n\nEarlier I suggested that I'd set such a config variable to something\nlike:\n\n  diff-highlight | less -FX\n\nBut after playing with your patch, I realized that:\n\n  - there's no need to pipe through diff-highlight; it already happened\n    as part of interactive.diffFilter!\n\n  - since it's triggered manually now, there's no need to add in -FX\n\nSo I am perfectly happy for you to stop where you did. Possibly\ninteractive.pipeCommand could be useful in a more general sense, but we\ncan wait until somebody encounters that itch.\n\n-Peff\n"},{"id":"496238","messageId":"20240604103305.GB1781455@coredump.intra.peff.net","threadId":"61514","inReplyTo":"600d27c1-f9e2-4a03-af24-4de8f66526d6@gmail.com","subject":"Re: [PATCH v4 5/6] test-terminal: introduce --no-stdin-pty","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-06-04T10:33:05Z","receivedAt":"2024-06-04T10:33:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jun 04, 2024 at 11:05:15AM +0100, Phillip Wood wrote:\n\n> Hi Rubén\n> \n> On 03/06/2024 21:38, Rubén Justo wrote:\n> > In 18d8c26930 (test_terminal: redirect child process' stdin to a pty,\n> > 2015-08-04), t/test-terminal.perl learned to connect the child process'\n> > stdin to a pty.  It works well for what was intended: satisfying an\n> > `isatty(STDIN_FILENO)` check.\n> > \n> > However, the fork introduced, that copies the stdin to the child\n> > process, does not always manage to send all the information.\n> \n> I think the problem maybe to do with the use of File::Copy, not with the\n> fork. The man page for the copy function says\n> \n>     Note  that  passing  in  files  as  handles  instead  of  names may\n>     lead to loss of information on some operating systems; it is\n>     recommended that you use file names whenever possible.\n> \n> Rather than adding a new flag to work around a bug in our script it might be\n> better to try and fix the bug by using a loop that reads blocks of data from\n> the source and writes them to the destination instead of calling copy.\n\nI don't think I've seen missing data. But do note that the test_terminal\nstdin handling is racy:\n\n  https://lore.kernel.org/git/20190520125016.GA13474@sigill.intra.peff.net/\n\nIMHO we should consider getting rid of it entirely. I think the only\nthing that uses it is t4153 (AFAICT it is luckily not racy because it\ndoes not actually read stdin, but only checks isatty).\n\n-Peff\n"},{"id":"496285","messageId":"xmqqikyo207f.fsf@gitster.g","threadId":"61514","inReplyTo":"20240604101700.GA1781455@coredump.intra.peff.net","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-04T15:32:04Z","receivedAt":"2024-06-04T15:32:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>   diff-highlight | less -FX\n>\n> But after playing with your patch, I realized that:\n>\n>   - there's no need to pipe through diff-highlight; it already happened\n>     as part of interactive.diffFilter!\n\n;-)\n\n>   - since it's triggered manually now, there's no need to add in -FX\n\nI do not know about -X, but yeah, -F is of dubious value in this\nparticular context.  You explicitly told us that you want to page,\nsomehow knowing that the hunk needs paging.\n\n> So I am perfectly happy for you to stop where you did. Possibly\n> interactive.pipeCommand could be useful in a more general sense, but we\n> can wait until somebody encounters that itch.\n\nIt makes it sound like if somebody has a use case already we\nshouldn't stop here ;-)\n\nThe default that colors the output is something we might later\nregret.  Those who want colored output can always use the\ninteractive.diffFilter configuration, but I am not sure if going\nthe other direction to strip coloring is just as easy.  But other\nthan that, I think we are at an OK place to stop.\n\nThanks.\n\n\n"},{"id":"496286","messageId":"xmqqcyow1zcj.fsf@gitster.g","threadId":"61514","inReplyTo":"e98dc7b1-3c93-41d2-a2ef-7f9f69789886@gmail.com","subject":"Re: [PATCH v4 2/6] pager: do not close fd 2 unnecessarily","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-04T15:50:36Z","receivedAt":"2024-06-04T15:50:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n> We send errors to the pager since 61b80509e3 (sending errors to stdout\n> under $PAGER, 2008-02-16).\n>\n> In a8335024c2 (pager: do not dup2 stderr if it is already redirected,\n> 2008-12-15) an exception was introduced to avoid redirecting stderr if\n> it is not connected to a terminal.\n>\n> In such exceptional cases, the close(STDERR_FILENO) we're doing in\n> close_pager_fds, is unnecessary.\n\nI was wondering how we can test this.\n\n> diff --git a/pager.c b/pager.c\n> index b8822a9381..b786601074 100644\n> --- a/pager.c\n> +++ b/pager.c\n> @@ -14,6 +14,7 @@ int pager_use_color = 1;\n>  \n>  static struct child_process pager_process;\n>  static const char *pager_program;\n> +static int close_fd2;\n>  \n>  /* Is the value coming back from term_columns() just a guess? */\n>  static int term_columns_guessed;\n> @@ -23,7 +24,8 @@ static void close_pager_fds(void)\n>  {\n>  \t/* signal EOF to pager */\n>  \tclose(1);\n> -\tclose(2);\n> +\tif (close_fd2)\n> +\t\tclose(2);\n>  }\n>  \n>  static void wait_for_pager_atexit(void)\n> @@ -141,8 +143,10 @@ void setup_pager(void)\n>  \n>  \t/* original process continues, but writes to the pipe */\n>  \tdup2(pager_process.in, 1);\n> -\tif (isatty(2))\n> +\tif (isatty(2)) {\n> +\t\tclose_fd2 = 1;\n>  \t\tdup2(pager_process.in, 2);\n> +\t}\n>  \tclose(pager_process.in);\n\nAt this step, we are assuming that we would start the pager only\nonce during the whole process, so relying on the 0-initialization of\nclose_fd2 in the BSS and setting it to 1 as needed when we dup2() is\nsufficient, but presumably we would want to explicitly set close_fd2\nto 0 when we are not calling dup2() here for completeness, with an\neye to the future where we run the pager multiple time.\n\nOther than that, this looks reasonable to me.\n\nPerhaps we should be checking the return value of our close() system\ncalls?  We would be getting scolded for closing an invalid file\ndescriptor, if we are closing something we shouldn't be closing,\nright?\n\nThanks.\n\n"},{"id":"496287","messageId":"xmqqsexszncx.fsf@gitster.g","threadId":"61514","inReplyTo":"76c725b4-1bc4-4916-81d8-98cad8fc4ca0@gmail.com","subject":"Re: [PATCH v4 3/6] pager: introduce wait_for_pager","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-04T16:25:34Z","receivedAt":"2024-06-04T16:25:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n>  static struct child_process pager_process;\n>  static const char *pager_program;\n> -static int close_fd2;\n> +static int old_fd1 = -1, old_fd2 = -1;\n\n;-)\n\nPresumably when old_fd2 does not have a valid value (i.e. -1) it\nmeans we did not do dup2() to save it away, and we refrain from\nclosing #2 in that case?\n\nIt is curious that #1 did not have similar problem 2/6 addressed,\nas we never redirected it with dup() to save it away.  But now we\ndo for some reason that the proposed log message did not explain.\nWe should say something like\n\n    We need to take back the standard output and the standard error\n    stream after we are done with an invocation of the pager.  For\n    that, save away the original file descriptors 1 and 2 when\n    spawning the pager, and restore them once the pager exits.  The\n    presence of saved fd#2 can be used to replace the \"close_fd2\"\n    flag introduced in the previous patch.\n\nperhaps.\n\n>  /* Is the value coming back from term_columns() just a guess? */\n>  static int term_columns_guessed;\n> @@ -24,20 +24,41 @@ static void close_pager_fds(void)\n>  {\n>  \t/* signal EOF to pager */\n>  \tclose(1);\n> -\tif (close_fd2)\n> +\tif (old_fd2 != -1)\n>  \t\tclose(2);\n>  }\n\nOK.\n\n>  static void wait_for_pager_atexit(void)\n>  {\n> +\tif (old_fd1 == -1)\n> +\t\treturn;\n> +\n>  \tfflush(stdout);\n>  \tfflush(stderr);\n>  \tclose_pager_fds();\n>  \tfinish_command(&pager_process);\n>  }\n>  \n> +void wait_for_pager(void)\n> +{\n> +\tif (old_fd1 == -1)\n> +\t\treturn;\n> +\n> +\twait_for_pager_atexit();\n> +\tunsetenv(\"GIT_PAGER_IN_USE\");\n> +\tdup2(old_fd1, 1);\n> +\told_fd1 = -1;\n> +\tif (old_fd2 != -1) {\n> +\t\tdup2(old_fd2, 2);\n> +\t\told_fd2 = -1;\n> +\t}\n> +}\n\nPresumably these use old_fd1's validity as a signal to see if have\npager running that need to be cleaned up?  It feels a bit unnatural\nwhy we do not ask about such a process the structure that is set up\nto control it, namely, the pager_process structure, but this is OK\nfor now.\n\nIt is just a naming issue, but it smells strange that the normal\ncode path (wait_for_pager()) calls a function that is an atexit\nhandler, which is more specific and only to be called atexit.\n\nI would have made\n\n\tstatic void finish_pager(void)\n\t{\n\t\tfflush(stdout);\n\t\tfflush(stderr);\n\t\tclose_pager_fds();\n\t\tfinish_command(&pager_process);\n\t}\n\nand then called it from the atexit handler and wait_for_pager().\n\n> @@ -113,6 +134,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)\n>  \n>  void setup_pager(void)\n>  {\n> +\tstatic int once = 0;\n>  \tconst char *pager = git_pager(isatty(1));\n>  \n>  \tif (!pager)\n> @@ -142,16 +164,18 @@ void setup_pager(void)\n>  \t\treturn;\n>  \n>  \t/* original process continues, but writes to the pipe */\n> +\told_fd1 = dup(1);\n>  \tdup2(pager_process.in, 1);\n>  \tif (isatty(2)) {\n> -\t\tclose_fd2 = 1;\n> +\t\told_fd2 = dup(2);\n>  \t\tdup2(pager_process.in, 2);\n>  \t}\n>  \tclose(pager_process.in);\n\nCan dup(2) fail and return -1?\n\n>  \n> -\t/* this makes sure that the parent terminates after the pager */\n> -\tsigchain_push_common(wait_for_pager_signal);\n> -\tatexit(wait_for_pager_atexit);\n> +\tif (!once++) {\n> +\t\tsigchain_push_common(wait_for_pager_signal);\n> +\t\tatexit(wait_for_pager_atexit);\n> +\t}\n>  }\n\nCan we give a name better than \"once\" to this thing?  \n\n>  int pager_in_use(void)\n> diff --git a/pager.h b/pager.h\n> index b77433026d..103ecac476 100644\n> --- a/pager.h\n> +++ b/pager.h\n> @@ -5,6 +5,7 @@ struct child_process;\n>  \n>  const char *git_pager(int stdout_is_tty);\n>  void setup_pager(void);\n> +void wait_for_pager(void);\n>  int pager_in_use(void);\n>  int term_columns(void);\n>  void term_clear_line(void);\n\nOther than that, overall it looks good.\n\nThanks, will queue.\n"},{"id":"496288","messageId":"xmqqo78gzn6d.fsf@gitster.g","threadId":"61514","inReplyTo":"3f085795-79bd-4a56-9df8-659e32179925@gmail.com","subject":"Re: [PATCH v4 3/6] pager: introduce wait_for_pager","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-04T16:29:30Z","receivedAt":"2024-06-04T16:29:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n>>   +void wait_for_pager(void)\n>> +{\n>> +\tif (old_fd1 == -1)\n>> +\t\treturn;\n>\n> Isn't it a bug to call this with old_fd1 == -1 or have I missed something?\n\nGood point.\n\n>> +\twait_for_pager_atexit();\n>> +\tunsetenv(\"GIT_PAGER_IN_USE\");\n>> +\tdup2(old_fd1, 1);\n>> +\told_fd1 = -1;\n>> +\tif (old_fd2 != -1) {\n>> +\t\tdup2(old_fd2, 2);\n>> +\t\told_fd2 = -1;\n>\n> We're leaking old_fd1 and old_fd2 here. wait_for_pager_atexit()\n\nYeah, that needs fixing.\n\n>> +\tif (!once++) {\n>\n> We only need to increment \"once\" when we enter this block, not every\n> time the code is run.\n\nRunning this 4 billion times and we'll be in a trouble ;-).\n\n>> +\t\tsigchain_push_common(wait_for_pager_signal);\n>\n> I think we should be calling this each time we setup the pager and pop\n> it in wait_for_pager().\n\nGood point.\n\n"},{"id":"496289","messageId":"xmqq8qzkzmjr.fsf@gitster.g","threadId":"61514","inReplyTo":"3fa0ade6-d710-47ce-8bcd-81d7821dfffb@gmail.com","subject":"Re: [PATCH v4 4/6] pager: introduce setup_custom_pager","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-04T16:43:04Z","receivedAt":"2024-06-04T16:43:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n> Introduce a new function setup_custom_pager() to allow setting up our\n> pager mechanism using a custom pager.  If the custom pager specified is\n> NULL or an empty string, use the normal pager as setup_pager() currently\n> does.\n\nWe often see \"if the pointer is NULL or points at an empty string\"\nin code that were originally ported from the scripted Porcelain, but\nI doubt we would want to follow that pattern in new code paths.\n\n>\n> Signed-off-by: Rubén Justo <rjusto@gmail.com>\n> ---\n>  pager.c | 17 +++++++++++------\n>  pager.h |  6 +++++-\n>  2 files changed, 16 insertions(+), 7 deletions(-)\n>\n> diff --git a/pager.c b/pager.c\n> index 925f860335..21a7d9cd60 100644\n> --- a/pager.c\n> +++ b/pager.c\n> @@ -74,14 +74,13 @@ static int core_pager_config(const char *var, const char *value,\n>  \treturn 0;\n>  }\n>  \n> -const char *git_pager(int stdout_is_tty)\n> +static const char *git_pager_custom(int stdout_is_tty, const char* pager)\n\nCall it git_custom_pager(), to be more grammatical and also match\nthe other one, setup_custom_pager().\n\nThe asterisk should stick to \"pager\", not its type.\n\n>  {\n> -\tconst char *pager;\n> -\n>  \tif (!stdout_is_tty)\n>  \t\treturn NULL;\n>  \n> -\tpager = getenv(\"GIT_PAGER\");\n> +\tif (!pager || !*pager)\n> +\t\tpager = getenv(\"GIT_PAGER\");\n\nWe often see \"if the pointer is NULL or points at an empty string\"\nin code that were originally ported from the scripted Porcelain, but\nI doubt we would want to follow that pattern in new code paths.\n\n> @@ -97,6 +96,11 @@ const char *git_pager(int stdout_is_tty)\n>  \treturn pager;\n>  }\n>  \n> +const char *git_pager(int stdout_is_tty)\n> +{\n> +\treturn git_pager_custom(stdout_is_tty, NULL);\n> +}\n\nOK.\n\n> @@ -132,10 +136,11 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)\n>  \tpager_process->trace2_child_class = \"pager\";\n>  }\n>  \n> -void setup_pager(void)\n> +void setup_custom_pager(const char* pager)\n\nThe asterisk should stick to \"pager\", not its type.\n\n>  {\n>  \tstatic int once = 0;\n> -\tconst char *pager = git_pager(isatty(1));\n> +\n> +\tpager = git_pager_custom(isatty(1), pager);\n\nThis feels a bit too convoluted.  This thing already knows if it got\na custom one from its caller because \"*pager\" is _its_ parameter.\nPerhaps rip out all the changes before and including the hunk\n\"@@ -132,10 +136,11 @@\" and start it like so, perhaps?\n\n\tvoid setup_custom_pager(const char *pager)\n\t{\n\t\tif (!pager)\n\t\t\tpager = git_pager(isatty(1));\n\t\t...\n\n> diff --git a/pager.h b/pager.h\n> index 103ecac476..2166662361 100644\n> --- a/pager.h\n> +++ b/pager.h\n> @@ -4,7 +4,11 @@\n>  struct child_process;\n>  \n>  const char *git_pager(int stdout_is_tty);\n> -void setup_pager(void);\n> +void setup_custom_pager(const char*);\n> +static inline void setup_pager(void)\n> +{\n> +\tsetup_custom_pager(NULL);\n> +}\n\nA good approach to help existing callers---there are more than half\na dozen existing callers to setup_pager() in the code base.  We\ncould migrate them all away to pass NULL but it would be totally\noutside the scope of this topic.\n"},{"id":"496290","messageId":"xmqqy17kws2k.fsf@gitster.g","threadId":"61514","inReplyTo":"75a3cc89-4d23-4eae-b0ad-e52e2c8ba550@gmail.com","subject":"Re: [PATCH v4 6/6] add-patch: introduce the command '|'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-04T17:12:03Z","receivedAt":"2024-06-04T17:12:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n> @@ -1389,6 +1390,7 @@ N_(\"j - leave this hunk undecided, see next undecided hunk\\n\"\n>     \"s - split the current hunk into smaller hunks\\n\"\n>     \"e - manually edit the current hunk\\n\"\n>     \"p - print the current hunk\\n\"\n> +   \"| - use pager to show the current hunk, or use |<program> to customize\\n\"\n>     \"? - print help\\n\");\n\n\"to customize\" strongly hints that the customization will stick, at\nleast during this session.  Is that what actually happens?\n\n> @@ -1401,6 +1403,7 @@ static int patch_update_file(struct add_p_state *s,\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n>  \tint colored = !!s->colored.len, quit = 0;\n>  \tenum prompt_mode_type prompt_mode_type;\n> +\tconst char* pager = NULL;\n\nThe asterisk sticks to \"pager\", not its type.\n\n>  \tenum {\n>  \t\tALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,\n>  \t\tALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,\n> @@ -1449,9 +1452,15 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\tstrbuf_reset(&s->buf);\n>  \t\tif (file_diff->hunk_nr) {\n>  \t\t\tif (rendered_hunk_index != hunk_index) {\n> +\t\t\t\tif (pager)\n> +\t\t\t\t\tsetup_custom_pager(pager);\n>  \t\t\t\trender_hunk(s, hunk, 0, colored, &s->buf);\n>  \t\t\t\tfputs(s->buf.buf, stdout);\n>  \t\t\t\trendered_hunk_index = hunk_index;\n> +\t\t\t\tif (pager) {\n> +\t\t\t\t\twait_for_pager();\n> +\t\t\t\t\tpager = NULL;\n> +\t\t\t\t}\n>  \t\t\t}\n>  \n>  \t\t\tstrbuf_reset(&s->buf);\n> @@ -1485,6 +1494,7 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\t\tstrbuf_addstr(&s->buf, \",e\");\n>  \t\t\t}\n>  \t\t\tstrbuf_addstr(&s->buf, \",p\");\n> +\t\t\tstrbuf_addstr(&s->buf, \",|\");\n>  \t\t}\n>  \t\tif (file_diff->deleted)\n>  \t\t\tprompt_mode_type = PROMPT_DELETION;\n> @@ -1512,8 +1522,8 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\tcontinue;\n>  \t\tch = tolower(s->answer.buf[0]);\n>  \n> -\t\t/* 'g' takes a hunk number and '/' takes a regexp */\n> -\t\tif (s->answer.len != 1 && (ch != 'g' && ch != '/')) {\n> +\t\t/* 'g' takes a hunk number, '/' takes a regexp and '|' takes a program */\n> +\t\tif (s->answer.len != 1 && (ch != 'g' && ch != '/' && ch != '|')) {\n\nNot limited to this instance, but a good discipline is to stop and\nthink twice before adding the third thing to already existing two.\n\nPerhaps\n\n\t\t/*\n\t\t * 'g' takes a hunk number to go to.\n\t\t * '/' takes a regexp to match.\n\t\t * '|' takes a program to pipe to.\n\t\t */\n\t\tif (s->answer.len != 1 && !strchr(\"g/|\", ch))\n\n> @@ -1674,6 +1684,17 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\t}\n>  \t\t} else if (s->answer.buf[0] == 'p') {\n>  \t\t\trendered_hunk_index = -1;\n> +\t\t} else if (ch == '|') {\n> +\t\t\tstrbuf_remove(&s->answer, 0, 1);\n> +\t\t\tif (s->s.use_single_key && s->answer.len == 0) {\n\nIf you check .use_single_key, you do not need to check answer.len,\ndo you?  Can it ever be anything other than 0 here in the single-key\nmode?\n\n> +\t\t\t\tprintf(\"%s\", _(\"program? \"));\n> +\t\t\t\tfflush(stdout);\n> +\t\t\t\tstrbuf_getline(&s->answer, stdin);\n> +\t\t\t\tstrbuf_trim_trailing_newline(&s->answer);\n> +\t\t\t}\n> +\t\t\tstrbuf_trim(&s->answer);\n> +\t\t\tpager = s->answer.buf;\n\nIs it safe to peek into s->answer.buf and expect it to be live until\nwe have to use the pager like this?\n\nBy the way, it should be trivial to make the \"custom\" pager more sticky.\n\n\t\t} else if (ch == '|') {\n\t\t\tif (s->s.use_single_key) {\n\t\t\t\t... read into s->answer ...\n\t\t\t} else {\n\t\t\t\tstrbuf_remove(&s->answer, 0, 1);\n\t\t\t}\n\t\t\tstrbuf_trim_trailing_newline(&s->answer);\n\n\t\t\t/*\n                         * If it is completely empty, use the last\n                         * one, if it is semi-empty, reset to the default.\n                         */\n\t\t\tif (!s->answer.len) {\n\t\t\t\t;\n\t\t\t} else {\n\t\t\t\tFREE_AND_NULL(pager);\n\t\t\t\tstrbuf_trim(&s->answer);\n                                if (!s->answer.len)\n                                \tpager = xstrdup(s->answer.buf);\n\t\t\t}\n\nEven better, we can lift the scope of \"pager\" one level up, define\nit as an on-stack variable in run_add_p(), add a new parameter of\ntype \"const char **\" to patch_update_file(), and use it throughout\npatch_update_file(), so that a \"custom\" pager set here will be\ncarried across file boundaries.\n\n> +\t\t\trendered_hunk_index = -1;\n>  \t\t} else if (s->answer.buf[0] == '?') {\n>  \t\t\tconst char *p = _(help_patch_remainder), *eol = p;\n>  \n> diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\n> index 6f6d174687..7b3ebb671d 100755\n> --- a/t/t3701-add-interactive.sh\n> +++ b/t/t3701-add-interactive.sh\n> @@ -64,8 +64,8 @@ test_expect_success 'unknown command' '\n>  \tgit add -N command &&\n>  \tgit diff command >expect &&\n>  \tcat >>expect <<-EOF &&\n> -\t(1/1) Stage addition [y,n,q,a,d,e,p,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help)\n> -\t(1/1) Stage addition [y,n,q,a,d,e,p,?]?$SP\n> +\t(1/1) Stage addition [y,n,q,a,d,e,p,|,?]? Unknown command ${SQ}W${SQ} (use ${SQ}?${SQ} for help)\n> +\t(1/1) Stage addition [y,n,q,a,d,e,p,|,?]?$SP\n>  \tEOF\n>  \tgit add -p -- command <command >actual 2>&1 &&\n>  \ttest_cmp expect actual\n> @@ -348,9 +348,9 @@ test_expect_success 'different prompts for mode change/deleted' '\n>  \tgit -c core.filemode=true add -p >actual &&\n>  \tsed -n \"s/^\\(([0-9/]*) Stage .*?\\).*/\\1/p\" actual >actual.filtered &&\n>  \tcat >expect <<-\\EOF &&\n> -\t(1/1) Stage deletion [y,n,q,a,d,p,?]?\n> -\t(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,?]?\n> -\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]?\n> +\t(1/1) Stage deletion [y,n,q,a,d,p,|,?]?\n> +\t(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,|,?]?\n> +\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]?\n>  \tEOF\n>  \ttest_cmp expect actual.filtered\n>  '\n> @@ -537,13 +537,13 @@ test_expect_success 'split hunk setup' '\n>  test_expect_success 'goto hunk 1 with \"g 1\"' '\n>  \ttest_when_finished \"git reset\" &&\n>  \ttr _ \" \" >expect <<-EOF &&\n> -\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1:  -1,2 +1,3          +15\n> +\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]? + 1:  -1,2 +1,3          +15\n>  \t_ 2:  -2,4 +3,8          +21\n>  \tgo to which hunk? @@ -1,2 +1,3 @@\n>  \t_10\n>  \t+15\n>  \t_20\n> -\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n> +\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n>  \tEOF\n>  \ttest_write_lines s y g 1 | git add -p >actual &&\n>  \ttail -n 7 <actual >actual.trimmed &&\n> @@ -556,7 +556,7 @@ test_expect_success 'goto hunk 1 with \"g1\"' '\n>  \t_10\n>  \t+15\n>  \t_20\n> -\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n> +\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n>  \tEOF\n>  \ttest_write_lines s y g1 | git add -p >actual &&\n>  \ttail -n 4 <actual >actual.trimmed &&\n> @@ -566,11 +566,11 @@ test_expect_success 'goto hunk 1 with \"g1\"' '\n>  test_expect_success 'navigate to hunk via regex /pattern' '\n>  \ttest_when_finished \"git reset\" &&\n>  \ttr _ \" \" >expect <<-EOF &&\n> -\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@\n> +\t(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]? @@ -1,2 +1,3 @@\n>  \t_10\n>  \t+15\n>  \t_20\n> -\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n> +\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n>  \tEOF\n>  \ttest_write_lines s y /1,2 | git add -p >actual &&\n>  \ttail -n 5 <actual >actual.trimmed &&\n> @@ -583,7 +583,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '\n>  \t_10\n>  \t+15\n>  \t_20\n> -\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n> +\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n>  \tEOF\n>  \ttest_write_lines s y / 1,2 | git add -p >actual &&\n>  \ttail -n 4 <actual >actual.trimmed &&\n> @@ -595,17 +595,38 @@ test_expect_success 'print again the hunk' '\n>  \ttr _ \" \" >expect <<-EOF &&\n>  \t+15\n>  \t 20\n> -\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@\n> +\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? @@ -1,2 +1,3 @@\n>  \t 10\n>  \t+15\n>  \t 20\n> -\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_\n> +\t(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]?_\n>  \tEOF\n>  \ttest_write_lines s y g 1 p | git add -p >actual &&\n>  \ttail -n 7 <actual >actual.trimmed &&\n>  \ttest_cmp expect actual.trimmed\n>  '\n>  \n> +test_expect_success TTY 'print again the hunk (PAGER)' '\n> +\ttest_when_finished \"git reset\" &&\n> +\tcat >expect <<-EOF &&\n> +\t<GREEN>+<RESET><GREEN>15<RESET>\n> +\t 20<RESET>\n> +\t<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>\n> +\tPAGER  10<RESET>\n> +\tPAGER <GREEN>+<RESET><GREEN>15<RESET>\n> +\tPAGER  20<RESET>\n> +\t<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? <RESET>\n> +\tEOF\n> +\ttest_write_lines s y g 1 \\| |\n> +\t(\n> +\t\tGIT_PAGER=\"sed s/^/PAGER\\ /\" &&\n> +\t\texport GIT_PAGER &&\n> +\t\ttest_terminal --no-stdin-pty git add -p >actual\n> +\t) &&\n> +\ttail -n 7 <actual | test_decode_color >actual.trimmed &&\n> +\ttest_cmp expect actual.trimmed\n> +'\n> +\n>  test_expect_success 'split hunk \"add -p (edit)\"' '\n>  \t# Split, say Edit and do nothing.  Then:\n>  \t#\n> @@ -780,21 +801,21 @@ test_expect_success 'colors can be overridden' '\n>  \t<BLUE>+<RESET><BLUE>new<RESET>\n>  \t<CYAN> more-context<RESET>\n>  \t<BLUE>+<RESET><BLUE>another-one<RESET>\n> -\t<YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,?]? <RESET><BOLD>Split into 2 hunks.<RESET>\n> +\t<YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,|,?]? <RESET><BOLD>Split into 2 hunks.<RESET>\n>  \t<MAGENTA>@@ -1,3 +1,3 @@<RESET>\n>  \t<CYAN> context<RESET>\n>  \t<BOLD>-old<RESET>\n>  \t<BLUE>+<RESET><BLUE>new<RESET>\n>  \t<CYAN> more-context<RESET>\n> -\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>\n> +\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>\n>  \t<CYAN> more-context<RESET>\n>  \t<BLUE>+<RESET><BLUE>another-one<RESET>\n> -\t<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>\n> +\t<YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,|,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>\n>  \t<CYAN> context<RESET>\n>  \t<BOLD>-old<RESET>\n>  \t<BLUE>+new<RESET>\n>  \t<CYAN> more-context<RESET>\n> -\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>\n> +\t<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,|,?]? <RESET>\n>  \tEOF\n>  \ttest_cmp expect actual\n>  '\n"},{"id":"496293","messageId":"8bbb3503cee92a66092073d990a9606a@manjaro.org","threadId":"61514","inReplyTo":"xmqqsexu6o60.fsf@gitster.g","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-04T17:34:42Z","receivedAt":"2024-06-04T17:34:50Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-06-03 17:28, Junio C Hamano wrote:\n> Dragan Simic <dsimic@manjaro.org> writes:\n> \n>>> I'd probably just say $GIT_PAGER instead of its expansion if we were\n>>> go that route.\n>> \n>> Makes sense to me.  More precisely, the environment should be checked\n>> to see is it \"$GIT_PAGER\" or \"$PAGER\" that needs to be printed \n>> literally\n>> as part of the help message.\n> \n> I was sure somebody will split a hair like that.  At that point we\n> are better off mentioning 'git var' X-<.  Or just 'Your Pager'.\n\nI agree that it can be seen as splitting hairs, but the general approach\nof Git is accuracy, so using \"$GIT_PAGER\" regardless of it being used or\nnot would be simply against the general approach.\n\nThough, I agree that using \"pager\" or maybe even \"your pager\" would be\nsimpler, yet a very good option.\n"},{"id":"496294","messageId":"c9888ea1e70d892e9f21db70977e0631@manjaro.org","threadId":"61514","inReplyTo":"xmqqcyoy6mnl.fsf@gitster.g","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-04T17:41:13Z","receivedAt":"2024-06-04T17:41:15Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-06-03 18:01, Junio C Hamano wrote:\n> Dragan Simic <dsimic@manjaro.org> writes:\n> \n>> Thus, when piping to a custom program, the escape codes that\n>> perform the coloring should be stripped.\n> \n> I tend to agree that if we do not give a way to toggle between\n> \"with\" and \"without\" color when piping to a program, it is safer to\n> make the default uncolored.\n\nGood point.  The default behavior or \"|xyz\" should be to strip\nthe coloring escape sequences, because we don't know is \"xyz\"\ncapable of handling those escape sequences properly, and to keep\nthe coloring with \"||xyz\" or whatever we come up with for our\nequivalent \"--color=always\", so to speak.\n\n> The user's configured pager is expected to deal with colors just\n> fine (or the user has globally configured colors to be off).  As we\n> are capable of telling if the user is asking to spawn the default\n> pager (by not giving a custom command or by clearing the previous\n> custom command given in the same session) or a custom one, it should\n> be easily doable to give colored version to the configured pager and\n> uncolored version to a custom/one-shot command.  Unlike the existing\n> support for (e)dit command, we do not read back from what the\n> command does using the hunk and present it again to the user, it\n> should be a relatively easy and safe thing to do.\n\nExactly.\n"},{"id":"496295","messageId":"9556a5875a6004b35a5df26866426a6d@manjaro.org","threadId":"61514","inReplyTo":"c9888ea1e70d892e9f21db70977e0631@manjaro.org","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-04T17:42:10Z","receivedAt":"2024-06-04T17:42:12Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-06-04 19:41, Dragan Simic wrote:\n> On 2024-06-03 18:01, Junio C Hamano wrote:\n>> Dragan Simic <dsimic@manjaro.org> writes:\n>> \n>>> Thus, when piping to a custom program, the escape codes that\n>>> perform the coloring should be stripped.\n>> \n>> I tend to agree that if we do not give a way to toggle between\n>> \"with\" and \"without\" color when piping to a program, it is safer to\n>> make the default uncolored.\n> \n> Good point.  The default behavior or \"|xyz\" should be to strip\n> the coloring escape sequences, because we don't know is \"xyz\"\n> capable of handling those escape sequences properly, and to keep\n> the coloring with \"||xyz\" or whatever we come up with for our\n> equivalent \"--color=always\", so to speak.\n\nOops, sorry...  s/equivalent/equivalent of/\n\n>> The user's configured pager is expected to deal with colors just\n>> fine (or the user has globally configured colors to be off).  As we\n>> are capable of telling if the user is asking to spawn the default\n>> pager (by not giving a custom command or by clearing the previous\n>> custom command given in the same session) or a custom one, it should\n>> be easily doable to give colored version to the configured pager and\n>> uncolored version to a custom/one-shot command.  Unlike the existing\n>> support for (e)dit command, we do not read back from what the\n>> command does using the hunk and present it again to the user, it\n>> should be a relatively easy and safe thing to do.\n> \n> Exactly.\n"},{"id":"496302","messageId":"844704794168f9fcb85c75014c84cde0@manjaro.org","threadId":"61514","inReplyTo":"9d05b41f-c120-4db0-9ee5-e24d20389129@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-04T18:13:43Z","receivedAt":"2024-06-04T18:13:46Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-06-03 22:19, Rubén Justo wrote:\n> On Sun, Jun 02, 2024 at 07:36:37PM +0200, Dragan Simic wrote:\n> \n>> The way I see it, using \"| <program>\" should follow the de facto rules\n>> already established by the \"--color=auto\" command-line option in \n>> multiple\n>> utilities.  Thus, when piping to a custom program, the escape codes \n>> that\n>> perform the coloring should be stripped.\n> \n> Interesting.  However, I'd like to find a way to keep the escape codes\n> when using programs like: '|head';  perhaps with the '>' command,\n> suggested by Junio.\n> \n> At any rate, I feel we can leave that, perhaps corner-case scenario, \n> for\n> a future series.  As this series is mainly about the 'pager' machinery.\n\nI'd suggest that \"| <program>\" is made to work as \"--color=auto\" in\nthe current series, i.e. with the coloring escape sequences stripped,\nwhich is the safe approach because we don't know is \"<program>\" capable\nof handling those escape sequences, while implementing support for\n\"--color=always\" would be left for a follow-up series.  Using \">\" for\nthat future command makes sense to me, because it suggests an output\nredirection in raw form.\n\nOf course, the plain \"|\" should still leave the coloring escape \nsequences\nintact, because the configured pager (or the default pager, configured\nby Git internally) is expected to handle them properly.\n\n>> > This, a new 'interactive.pipeCommand' setting, or a new switch: 'add\n>> > -P',\n>> > are left for discussing in, hopefully, a future series.\n>> >\n>> > One final note;  I preferred to model the help text this way:\n>> >\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>> >     j - leave this hunk undecided, see next undecided hunk\n>> >     J - leave this hunk undecided, see next hunk\n>> >     g - select a hunk to go to\n>> >     / - search for a hunk matching the given regex\n>> >     s - split the current hunk into smaller hunks\n>> >     e - manually edit the current hunk\n>> >     p - print the current hunk\n>> >     | - pipe the current hunk to the pager, or |<program> to use a\n>> > program'\n>> >     ? - print help\n>> \n>> I also like this form better, but I think wording could be improved.\n>> I'll think a bit more about it, maybe something like this:\n>> \n>>       | - use pager to show the current hunk, or use |<program> to \n>> customize\n> \n> Certainly!  It is indeed a sensible idea to improve the wording, \n> avoiding\n> the word \"pipe\" :-).  Thank you.\n\nI'm glad that you like it. :)\n\n>> Also, what's the single quote doing after \"use a program\"?\n> \n> Just a typo.  Sorry.\n\nAh, I see.  It looked a bit strange. :)\n\n>> > Instead of:\n>> >\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>> >     j - leave this hunk undecided, see next undecided hunk\n>> >     J - leave this hunk undecided, see next hunk\n>> >     g - select a hunk to go to\n>> >     / - search for a hunk matching the given regex\n>> >     s - split the current hunk into smaller hunks\n>> >     e - manually edit the current hunk\n>> >     p - print the current hunk\n>> >     |[program] - pipe the current hunk to a program, the pager if\n>> > none...\n>> >     ? - print help\n>> >\n>> > Because I believe it reads better by maintaining a single character\n>> > before the dash.  But I am not opposed to the latter.\n"},{"id":"496308","messageId":"a3dc59f9b2178fe263df2df23e792ede@manjaro.org","threadId":"61514","inReplyTo":"xmqqy17kws2k.fsf@gitster.g","subject":"Re: [PATCH v4 6/6] add-patch: introduce the command '|'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-04T20:05:54Z","receivedAt":"2024-06-04T20:05:56Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-06-04 19:12, Junio C Hamano wrote:\n> Rubén Justo <rjusto@gmail.com> writes:\n> \n>> @@ -1389,6 +1390,7 @@ N_(\"j - leave this hunk undecided, see next \n>> undecided hunk\\n\"\n>>     \"s - split the current hunk into smaller hunks\\n\"\n>>     \"e - manually edit the current hunk\\n\"\n>>     \"p - print the current hunk\\n\"\n>> +   \"| - use pager to show the current hunk, or use |<program> to \n>> customize\\n\"\n>>     \"? - print help\\n\");\n> \n> \"to customize\" strongly hints that the customization will stick, at\n> least during this session.  Is that what actually happens?\n\nGood point.  I used \"customize\" in the proposed wording improvement\nbecause we previously (kind of) agreed about making \"| <program>\",\nonce selected, stick to the end of the current session.\n"},{"id":"496321","messageId":"xmqq4ja8ynop.fsf@gitster.g","threadId":"61514","inReplyTo":"xmqqy17kws2k.fsf@gitster.g","subject":"Re: [PATCH v4 6/6] add-patch: introduce the command '|'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-05T05:16:06Z","receivedAt":"2024-06-05T05:16:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> By the way, it should be trivial to make the \"custom\" pager more sticky.\n\nHere is what you can squash into this step.  I gave many other\npieces of style and design advices in other messages, which are not\ncovered by this patch but the necessary fixes should be obvious.\n\nThis message is only about making the custom pager stick during a\nsession.  It does not adjust the command help to give the last pager\ncommand (or literally \"your pager\"), either.\n\n---- >8 ----\nSubject: [PATCH] add-p: make custom pager sticky during a session\n\nThe original design kept resetting the choice of the custom pager\nevery time the '|' command is used.  This was way cumbersome to use.\n\nKeep track of the last choice in the add_p_state.custom_pager\nmember.  This value can stick across calls to patch_update_file()\nfunction, so a custom pager used for choosing hunks in one file\ncan be carried over to the view hunks in the next file.\n\nAs we make the custom pager stick, we need a way to reset it back to\nthe default value (which we use NULL for, as set_custom_pager()\ntakes the value to mean \"use the default one\").\n\nAs there is no value that can say \"pager is not used\" suitable for\nthe custom_pager member to take, we need a separate \"use_pager\" flag\nso that the fact that '|' command was used can be propagated to the\nnext iteration of the loop, independent from what custom pager is\nused.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n add-patch.c | 33 +++++++++++++++++++++++----------\n 1 file changed, 23 insertions(+), 10 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex da13e267db..71ee7f9a94 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -272,6 +272,7 @@ struct add_p_state {\n \t/* patch mode */\n \tstruct patch_mode *mode;\n \tconst char *revision;\n+\tchar *custom_pager;\n };\n \n static void add_p_state_clear(struct add_p_state *s)\n@@ -285,6 +286,7 @@ static void add_p_state_clear(struct add_p_state *s)\n \tfor (i = 0; i < s->file_diff_nr; i++)\n \t\tfree(s->file_diff[i].hunk);\n \tfree(s->file_diff);\n+\tfree(s->custom_pager);\n \tclear_add_i_state(&s->s);\n }\n \n@@ -1403,7 +1405,7 @@ static int patch_update_file(struct add_p_state *s,\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint colored = !!s->colored.len, quit = 0;\n \tenum prompt_mode_type prompt_mode_type;\n-\tconst char* pager = NULL;\n+\tint use_pager = 0;\n \tenum {\n \t\tALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,\n \t\tALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,\n@@ -1452,14 +1454,14 @@ static int patch_update_file(struct add_p_state *s,\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n \t\t\tif (rendered_hunk_index != hunk_index) {\n-\t\t\t\tif (pager)\n-\t\t\t\t\tsetup_custom_pager(pager);\n+\t\t\t\tif (use_pager)\n+\t\t\t\t\tsetup_custom_pager(s->custom_pager);\n \t\t\t\trender_hunk(s, hunk, 0, colored, &s->buf);\n \t\t\t\tfputs(s->buf.buf, stdout);\n \t\t\t\trendered_hunk_index = hunk_index;\n-\t\t\t\tif (pager) {\n+\t\t\t\tif (use_pager) {\n \t\t\t\t\twait_for_pager();\n-\t\t\t\t\tpager = NULL;\n+\t\t\t\t\tuse_pager = 0;\n \t\t\t\t}\n \t\t\t}\n \n@@ -1685,15 +1687,26 @@ static int patch_update_file(struct add_p_state *s,\n \t\t} else if (s->answer.buf[0] == 'p') {\n \t\t\trendered_hunk_index = -1;\n \t\t} else if (ch == '|') {\n-\t\t\tstrbuf_remove(&s->answer, 0, 1);\n-\t\t\tif (s->s.use_single_key && s->answer.len == 0) {\n+\t\t\tif (!s->s.use_single_key) {\n+\t\t\t\tstrbuf_remove(&s->answer, 0, 1);\n+\t\t\t} else {\n \t\t\t\tprintf(\"%s\", _(\"program? \"));\n \t\t\t\tfflush(stdout);\n \t\t\t\tstrbuf_getline(&s->answer, stdin);\n-\t\t\t\tstrbuf_trim_trailing_newline(&s->answer);\n \t\t\t}\n-\t\t\tstrbuf_trim(&s->answer);\n-\t\t\tpager = s->answer.buf;\n+\t\t\tstrbuf_trim_trailing_newline(&s->answer);\n+\n+\t\t\tif (!s->answer.len)\n+\t\t\t\t; /* empty input - reuse the previous */\n+\t\t\telse {\n+\t\t\t\tstrbuf_trim(&s->answer);\n+\t\t\t\tFREE_AND_NULL(s->custom_pager);\n+\t\t\t\tif (!s->answer.len)\n+\t\t\t\t\t; /* semi-empty - use your pager */\n+\t\t\t\telse\n+\t\t\t\t\ts->custom_pager = xstrdup(s->answer.buf);\n+\t\t\t}\n+\t\t\tuse_pager = 1;\n \t\t\trendered_hunk_index = -1;\n \t\t} else if (s->answer.buf[0] == '?') {\n \t\t\tconst char *p = _(help_patch_remainder), *eol = p;\n-- \n2.45.2-409-g7b0defb391\n\n"},{"id":"496351","messageId":"20240605090935.GF2345232@coredump.intra.peff.net","threadId":"61514","inReplyTo":"xmqqikyo207f.fsf@gitster.g","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-06-05T09:09:35Z","receivedAt":"2024-06-05T09:09:37Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jun 04, 2024 at 08:32:04AM -0700, Junio C Hamano wrote:\n\n> The default that colors the output is something we might later\n> regret.  Those who want colored output can always use the\n> interactive.diffFilter configuration, but I am not sure if going\n> the other direction to strip coloring is just as easy.  But other\n> than that, I think we are at an OK place to stop.\n\nI don't think diffFilter is a great solution there. In my experience,\nthat is augmenting the existing coloring done by \"diff --color\" itself\n(and of course many people do not use a separate filter in the first\nplace, and just see the normal color output). So there's not an easy way\nto add the color back to a stripped version.\n\nThe interactive-patch code is literally holding the two color/non-color\nvariants in memory, splitting the hunks based on line counts, and then\nshowing you one and applying the other. Even if you wanted to run \"git\ndiff --color\" yourself in the \"|\" command, it would be a lot of work to\npick out the hunk of interest.\n\nGiven that the main use case for \"|\" is for human viewing through a\npager, I think the colorful, filtered version meant for users is the\nbest default. And then the \"bare\" version can come from an alternate\ncommand or a knob.\n\nJust to note some prior art, mutt's \"<pipe-message>\" faces a similar\nproblem. You might want the raw message (if you're going to poke at\nheaders, MIME parts, etc yourself) or you may want a decoded one (if you\njust care about body text and don't want to deal with base64, qp, etc,\nyourself). They provide a stateful config knob, but then you end up with\nhorrible macros that toggle the knob, like:\n\n  :set pipe_decode=yes<enter><pipe-message>my-script<enter>:set pipe_decode=no<enter>\n\nI think having two separate commands for the two modes would be less\nconfusing.\n\n-Peff\n"},{"id":"496369","messageId":"6056d585-6380-43e7-adf1-9f9aadd2a7db@gmail.com","threadId":"61514","inReplyTo":"20240605090935.GF2345232@coredump.intra.peff.net","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-06-05T13:21:30Z","receivedAt":"2024-06-05T13:21:32Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 05/06/2024 10:09, Jeff King wrote:\n> On Tue, Jun 04, 2024 at 08:32:04AM -0700, Junio C Hamano wrote:\n> \n> Given that the main use case for \"|\" is for human viewing through a\n> pager, I think the colorful, filtered version meant for users is the\n> best default. And then the \"bare\" version can come from an alternate\n> command or a knob.\n\nI think that's a very good point. It is hard to see what \"|\" can be used \nfor other than viewing the hunk as (a) git does not read the output so \nit cannot be used to filter or edit the hunk that is applied and (b) we \npass an isolated hunk so the post-image offset in the hunk header is \nlikely to be wrong and there is no indication as to which file it comes \nfrom so the program being run cannot apply the hunk itself. Having the \nescape codes does make it harder to filter the hunk. For example to just \nlook at the post-image as one needs to do something like\n\n\tgrep '^[^-+ @]*[+ @]'\n\ninstead of just using '^[+ @]' as the pattern but the bonus is that the \noutput is colored.\n\n> Just to note some prior art, mutt's \"<pipe-message>\" faces a similar\n> problem. You might want the raw message (if you're going to poke at\n> headers, MIME parts, etc yourself) or you may want a decoded one (if you\n> just care about body text and don't want to deal with base64, qp, etc,\n> yourself). They provide a stateful config knob, but then you end up with\n> horrible macros that toggle the knob, like:\n> \n>    :set pipe_decode=yes<enter><pipe-message>my-script<enter>:set pipe_decode=no<enter>\n> \n> I think having two separate commands for the two modes would be less\n> confusing.\n\nThat does sound simpler\n\nBest Wishes\n\nPhillip\n\n"},{"id":"496372","messageId":"xmqqfrtrxusw.fsf@gitster.g","threadId":"61514","inReplyTo":"20240604103305.GB1781455@coredump.intra.peff.net","subject":"Re: [PATCH v4 5/6] test-terminal: introduce --no-stdin-pty","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-05T15:39:59Z","receivedAt":"2024-06-05T15:40:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> IMHO we should consider getting rid of it entirely. I think the only\n> thing that uses it is t4153 (AFAICT it is luckily not racy because it\n> does not actually read stdin, but only checks isatty).\n\nSounds like a better approach than piling another workaround on the\ntest helper.  Reading the old discussion, we seem to have been in\nagreement that we generally do not have to insist reading from a tty\nand certainly the \"add -p\" codepath is not one of those \"if your\nother payload must come from the standard input, your instructions\nto specify how to handle that data needs to come from elsewhere, and\nthat is /dev/tty\" cases.\n\nThanks.\n"},{"id":"496385","messageId":"xmqq5xunti94.fsf@gitster.g","threadId":"61514","inReplyTo":"20240605090935.GF2345232@coredump.intra.peff.net","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-05T17:24:39Z","receivedAt":"2024-06-05T17:24:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> Just to note some prior art, mutt's \"<pipe-message>\" faces a similar\n> problem. You might want the raw message (if you're going to poke at\n> headers, MIME parts, etc yourself) or you may want a decoded one (if you\n> just care about body text and don't want to deal with base64, qp, etc,\n> yourself). They provide a stateful config knob, but then you end up with\n> horrible macros that toggle the knob, like:\n>\n>   :set pipe_decode=yes<enter><pipe-message>my-script<enter>:set pipe_decode=no<enter>\n>\n> I think having two separate commands for the two modes would be less\n> confusing.\n\nYup, I have been suffering from its equivalent in Gnus/Emacs every\nday, where decoded version of multipart/signed messages look\ndifferent enough from valid RFC2822 messages and break application\nwith \"git am\", and there of course is \"feed the undecoded original\nto pipe\" option.\n\nBut the need for both is pretty much known, and the real issue is\nthat what the default should be.  The default destination of the\npipe being a pager, making the colored output the default is\nconsistent with that, I would think.\n\nThanks.\n"},{"id":"496404","messageId":"0c93f4ea-aef1-4639-9d58-1decaf24c90e@gmail.com","threadId":"61514","inReplyTo":"3f085795-79bd-4a56-9df8-659e32179925@gmail.com","subject":"Re: [PATCH v4 3/6] pager: introduce wait_for_pager","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-05T22:03:07Z","receivedAt":"2024-06-05T22:03:10Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Tue, Jun 04, 2024 at 11:00:37AM +0100, Phillip Wood wrote:\n> Hi Rubén\n> \n> On 03/06/2024 21:38, Rubén Justo wrote:\n> > Since f67b45f862 (Introduce trivial new pager.c helper infrastructure,\n> > 2006-02-28) we have the machinery to send our output to a pager.\n> > \n> > That machinery, once set up, does not allow us to regain the original\n> > stdio streams.\n> > \n> > In the interactive commands (i.e.: add -p) we want to use the pager for\n> > some output, while maintaining the interaction with the user.\n> > \n> > Modify the pager machinery so that we can use setup_pager and, once\n> > we've finished sending the desired output for the pager, wait for the\n> > pager termination using a new function wait_for_pager.   Make this\n> > function reset the pager machinery before returning.\n> \n> This makes sense, I've left a few comments below\n> \n> > Signed-off-by: Rubén Justo <rjusto@gmail.com>\n> > ---\n> \n> >   static void wait_for_pager_atexit(void)\n> >   {\n> > +\tif (old_fd1 == -1)\n> > +\t\treturn;\n> > +\n> \n> This is good - we'll return early if we've already cleaned up the pager.\n> \n> >   \tfflush(stdout);\n> >   \tfflush(stderr);\n> >   \tclose_pager_fds();\n> >   \tfinish_command(&pager_process);\n> >   }\n> > +void wait_for_pager(void)\n> > +{\n> > +\tif (old_fd1 == -1)\n> > +\t\treturn;\n> \n> Isn't it a bug to call this with old_fd1 == -1 or have I missed something?\n\nIt is.  I'll remove it to avoid confusion.\n\n> \n> > +\twait_for_pager_atexit();\n> > +\tunsetenv(\"GIT_PAGER_IN_USE\");\n> > +\tdup2(old_fd1, 1);\n> > +\told_fd1 = -1;\n> > +\tif (old_fd2 != -1) {\n> > +\t\tdup2(old_fd2, 2);\n> > +\t\told_fd2 = -1;\n> \n> We're leaking old_fd1 and old_fd2 here.\n\nGood eyes.  Will fix.  Thanks.\n\n> wait_for_pager_atexit() flushes\n> stdout and stderr so this switching of fds should play nicely with code that\n> uses stdio.\n> \n> > @@ -113,6 +134,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)\n> >   void setup_pager(void)\n> >   {\n> > +\tstatic int once = 0;\n> >   \tconst char *pager = git_pager(isatty(1));\n> >   \tif (!pager)\n> > @@ -142,16 +164,18 @@ void setup_pager(void)\n> >   \t\treturn;\n> >   \t/* original process continues, but writes to the pipe */\n> > +\told_fd1 = dup(1);\n> >   \tdup2(pager_process.in, 1);\n> >   \tif (isatty(2)) {\n> > -\t\tclose_fd2 = 1;\n> > +\t\told_fd2 = dup(2);\n> >   \t\tdup2(pager_process.in, 2);\n> >   \t}\n> >   \tclose(pager_process.in);\n> > -\t/* this makes sure that the parent terminates after the pager */\n> > -\tsigchain_push_common(wait_for_pager_signal);\n> > -\tatexit(wait_for_pager_atexit);\n> > +\tif (!once++) {\n> \n> We only need to increment \"once\" when we enter this block, not every time\n> the code is run.\n\nOK. :-)\n\n> \n> > +\t\tsigchain_push_common(wait_for_pager_signal);\n> \n> I think we should be calling this each time we setup the pager and pop it in\n> wait_for_pager(). Imagine a caller sets up a signal handler before calling\n> setup_pager() and wants to pop it after the pager has finished\n> \n> \tsigchain_push(...)\n> \tsetup_pager(...)\n> \tdo_something()\n> \twait_for_pager()\n> \tsigchain_pop(...)\n> \n> With the changes here it will pop the signal handler added by setup_pager()\n> rather than the one it is expecting.\n\nI hadn't thought about this.  Thank you for pointing it out.\n\n> \n> > +\t\tatexit(wait_for_pager_atexit);\n> \n> It is a bit of a shame we have to leave this function active when the pager\n> has finished. We could add a wrapper around atexit() that allows us to pop\n> functions we no-longer want to call but I don't think it is worth the effort\n> here. wait_for_pager_atexit() is careful to return early if it is not\n> needed.\n> \n> \n> Best Wishes\n> \n> Phillip\n> \n\nThanks!\n"},{"id":"496406","messageId":"9c76f1f3-f858-400e-8fc7-8e3bc9764e87@gmail.com","threadId":"61514","inReplyTo":"600d27c1-f9e2-4a03-af24-4de8f66526d6@gmail.com","subject":"Re: [PATCH v4 5/6] test-terminal: introduce --no-stdin-pty","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-05T22:50:39Z","receivedAt":"2024-06-05T22:50:42Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Tue, Jun 04, 2024 at 11:05:15AM +0100, Phillip Wood wrote:\n\n> Rather than adding a new flag to work around a bug in our script it might be\n> better to try and fix the bug by using a loop that reads blocks of data from\n> the source and writes them to the destination instead of calling copy.\n\nTo be honest, I've tried.  I haven't found a way to fix it properly.\n\nI also thought about removing it.  I agree with Peff, removing the stdin\nredirection makes more sense.  IMHO simplifies.\n\nBut, perhaps we can happily live another almost-decade with this new\n--no-stdin-pty, before finally removing the stdin redirection in this\nhelper. :-)\n"},{"id":"496463","messageId":"20240606082447.GA1166753@coredump.intra.peff.net","threadId":"61514","inReplyTo":"xmqqfrtrxusw.fsf@gitster.g","subject":"Re: [PATCH v4 5/6] test-terminal: introduce --no-stdin-pty","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-06-06T08:24:47Z","receivedAt":"2024-06-06T08:24:49Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 05, 2024 at 08:39:59AM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > IMHO we should consider getting rid of it entirely. I think the only\n> > thing that uses it is t4153 (AFAICT it is luckily not racy because it\n> > does not actually read stdin, but only checks isatty).\n> \n> Sounds like a better approach than piling another workaround on the\n> test helper.  Reading the old discussion, we seem to have been in\n> agreement that we generally do not have to insist reading from a tty\n> and certainly the \"add -p\" codepath is not one of those \"if your\n> other payload must come from the standard input, your instructions\n> to specify how to handle that data needs to come from elsewhere, and\n> that is /dev/tty\" cases.\n\nI think we got rid of all of the \"read interactive input over tty\"\ncases. The one that I still see is am's heuristic to use isatty() to\ndecide whether the user might send patches over stdin.\n\nI just sent a separate patch series which I think improves the\nsituation. And then either Rubén could build on top of that (if we think\nit will graduate quickly) or he could do his optional patch, and we\ncould rip it back out when the two are merged.\n\n-Peff\n"},{"id":"496465","messageId":"20240606082748.GD658959@coredump.intra.peff.net","threadId":"61514","inReplyTo":"9c76f1f3-f858-400e-8fc7-8e3bc9764e87@gmail.com","subject":"Re: [PATCH v4 5/6] test-terminal: introduce --no-stdin-pty","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-06-06T08:27:48Z","receivedAt":"2024-06-06T08:27:50Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jun 06, 2024 at 12:50:39AM +0200, Rubén Justo wrote:\n\n> On Tue, Jun 04, 2024 at 11:05:15AM +0100, Phillip Wood wrote:\n> \n> > Rather than adding a new flag to work around a bug in our script it might be\n> > better to try and fix the bug by using a loop that reads blocks of data from\n> > the source and writes them to the destination instead of calling copy.\n> \n> To be honest, I've tried.  I haven't found a way to fix it properly.\n\nI think File::Copy() is not the culprit. The problem is more at the\nsyscall level. Once test_terminal finishes writing all of the output to\nthe tty, it closes its end of the descriptor. And now the tty is \"gone\",\nisatty() returns false, and further reads get EIO (even if we didn't\nread all of the bytes! They're lost).\n\nSo I think the only thing we could do is _not_ actually close the\ndescriptor. But now we don't have a way to signal EOF to the other side.\nUnless perhaps there is some clever way to do so. I'd still favor\nripping it out.\n\n> I also thought about removing it.  I agree with Peff, removing the stdin\n> redirection makes more sense.  IMHO simplifies.\n> \n> But, perhaps we can happily live another almost-decade with this new\n> --no-stdin-pty, before finally removing the stdin redirection in this\n> helper. :-)\n\nHopefully not a decade. ;) I just sent a series, and I think you could\neither build on top, or the merge resolution could just drop your new\noption.\n\n-Peff\n"},{"id":"496690","messageId":"a8d3415e3913e3a0798a748ed7f7a093@manjaro.org","threadId":"61514","inReplyTo":"6056d585-6380-43e7-adf1-9f9aadd2a7db@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-08T05:54:34Z","receivedAt":"2024-06-08T05:54:44Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-06-05 15:21, Phillip Wood wrote:\n> On 05/06/2024 10:09, Jeff King wrote:\n>> On Tue, Jun 04, 2024 at 08:32:04AM -0700, Junio C Hamano wrote:\n>> \n>> Given that the main use case for \"|\" is for human viewing through a\n>> pager, I think the colorful, filtered version meant for users is the\n>> best default. And then the \"bare\" version can come from an alternate\n>> command or a knob.\n> \n> I think that's a very good point. It is hard to see what \"|\" can be\n> used for other than viewing the hunk as (a) git does not read the\n> output so it cannot be used to filter or edit the hunk that is applied\n> and (b) we pass an isolated hunk so the post-image offset in the hunk\n> header is likely to be wrong and there is no indication as to which\n> file it comes from so the program being run cannot apply the hunk\n> itself. Having the escape codes does make it harder to filter the\n> hunk. For example to just look at the post-image as one needs to do\n> something like\n> \n> \tgrep '^[^-+ @]*[+ @]'\n> \n> instead of just using '^[+ @]' as the pattern but the bonus is that\n> the output is colored.\n\nAgreed, but as I already explained, [1] only when using the bare \"|\"\ncommand.  When \"|xyz\" is used instead, the version of the hunk with\nno coloring escape sequences should be piped to xyz.\n\n[1] \nhttps://lore.kernel.org/git/844704794168f9fcb85c75014c84cde0@manjaro.org/\n"},{"id":"496729","messageId":"da18f9c2-018f-4808-92e0-d2fb67a427fb@gmail.com","threadId":"61514","inReplyTo":"20240606082748.GD658959@coredump.intra.peff.net","subject":"Re: [PATCH v4 5/6] test-terminal: introduce --no-stdin-pty","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-09T07:26:12Z","receivedAt":"2024-06-09T07:26:28Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Thu, Jun 06, 2024 at 04:27:48AM -0400, Jeff King wrote:\n\n> I just sent a series, and I think you could either build on top, or\n> the merge resolution could just drop your new option.\n\nI've already responded to your series, but just to confirm here.  I'm\nglad to drop this step and building on top of jk/am-retry.\n\nThanks!\n"},{"id":"496734","messageId":"219a195c-74d0-4c21-bf54-0752bb5b01df@gmail.com","threadId":"61514","inReplyTo":"a8d3415e3913e3a0798a748ed7f7a093@manjaro.org","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-09T07:44:42Z","receivedAt":"2024-06-09T07:45:03Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Sat, Jun 08, 2024 at 07:54:34AM +0200, Dragan Simic wrote:\n\n> When \"|xyz\" is used instead, the version of the hunk with no coloring\n> escape sequences should be piped to xyz.\n\nThat is a sane and conservative approach, and I'm not opposed.  However,\ngiving the colorful version though a custom pager is a good thing to\nhave, I think, i.e: allowing a simple \"head\" without losing the\ncoloring.\n\nLet's recap a bit.\n\nInitially, this series aimed to enable sending chunks to the pager\nduring \"add -p\" sessions.\n\nTo reduce the blast radius of spawning a pager for each chunk, we\nintroduced a new command \"P\".\n\nJunio suggested opening up the command to allow specifying a custom\npager, in the form of \"P<program>\".\n\nThe \"P\" command started to resemble a lot to the common pipe operator.\nThus, we shifted to \"|<program>\".\n\nSome concerns were raised about controlling when to send coloring escape\nsequences.  Several ideas were discussed to address this, including\nintroducing a new command \">\", a modifier for \"|\": \"||\", and others.\nAlternatively, we could leave it up to the user to filter as needed.\nOr, simply, do not send escape codes at all.\n\nSo, looking back at the ideas discussed in the thread, perhaps a\nreasonable next step might be to reintroduce the 'P<program>' command\nand let '|<program>' be the way to send raw, uncolored, chunks.\n\nThis approach makes sense to me.  I'll wait a bit before sending a\nreroll to gather feedback, though.\n\nThanks.\n"},{"id":"496735","messageId":"7937845d7cb7ae0179c4922ed154c5c7@manjaro.org","threadId":"61514","inReplyTo":"219a195c-74d0-4c21-bf54-0752bb5b01df@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-09T07:57:20Z","receivedAt":"2024-06-09T07:57:28Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"Hello Ruben,\n\nOn 2024-06-09 09:44, Rubén Justo wrote:\n> Some concerns were raised about controlling when to send coloring \n> escape\n> sequences.  Several ideas were discussed to address this, including\n> introducing a new command \">\", a modifier for \"|\": \"||\", and others.\n> Alternatively, we could leave it up to the user to filter as needed.\n> Or, simply, do not send escape codes at all.\n> \n> So, looking back at the ideas discussed in the thread, perhaps a\n> reasonable next step might be to reintroduce the 'P<program>' command\n> and let '|<program>' be the way to send raw, uncolored, chunks.\n\nActually, it would be better to re-introduce the \"P\" option, without\nany parameters, which would display the current hunk through the\nalready configured pager, and let \"|<program>\" be the new option\nthat pipes hunks _without_ coloring escape sequences to \"<program>\".\n\nAs a follow-up, after the dust settles, we could add \"><program>\" as\nanother new option that pipes hunks _with_ the coloring escape sequences\nto \"<program>\".  That would be a rather clean approach, which would\nalso reserve the \"weird-looking\" commands with additional parameters\n(i.e. \"|<program>\" and \"><program>\") for the more advanced operations,\nwhile the \"normal-looking\" command (i.e. \"P\") would be assigned to\nthe, presumably, most commonly used new hunk operation.\n"},{"id":"496736","messageId":"a2a59f5e-fd55-41d3-8472-b99256e1f428@gmail.com","threadId":"61514","inReplyTo":"a8d3415e3913e3a0798a748ed7f7a093@manjaro.org","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-06-09T14:29:30Z","receivedAt":"2024-06-09T14:29:38Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Dragan\n\nOn 08/06/2024 06:54, Dragan Simic wrote:\n> On 2024-06-05 15:21, Phillip Wood wrote:\n>> On 05/06/2024 10:09, Jeff King wrote:\n>>> On Tue, Jun 04, 2024 at 08:32:04AM -0700, Junio C Hamano wrote:\n>>>\n>>> Given that the main use case for \"|\" is for human viewing through a\n>>> pager, I think the colorful, filtered version meant for users is the\n>>> best default. And then the \"bare\" version can come from an alternate\n>>> command or a knob.\n>>\n>> I think that's a very good point. It is hard to see what \"|\" can be\n>> used for other than viewing the hunk as (a) git does not read the\n>> output so it cannot be used to filter or edit the hunk that is applied\n>> and (b) we pass an isolated hunk so the post-image offset in the hunk\n>> header is likely to be wrong and there is no indication as to which\n>> file it comes from so the program being run cannot apply the hunk\n>> itself. Having the escape codes does make it harder to filter the\n>> hunk. For example to just look at the post-image as one needs to do\n>> something like\n>>\n>>     grep '^[^-+ @]*[+ @]'\n>>\n>> instead of just using '^[+ @]' as the pattern but the bonus is that\n>> the output is colored.\n> \n> Agreed, but as I already explained, [1] only when using the bare \"|\"\n> command.  When \"|xyz\" is used instead, the version of the hunk with\n> no coloring escape sequences should be piped to xyz.\n\nHaving read the message you referenced I'm struggling to understand the \nuse-case for stripping escape codes - what do you want to do with the \nhunk that means you want to remove the color?\n\nBest Wishes\n\nPhillip\n\n> \n> [1] \n> https://lore.kernel.org/git/844704794168f9fcb85c75014c84cde0@manjaro.org/\n"},{"id":"496739","messageId":"d092f5bb1d3bc7b7a821000a3cad8a1e@manjaro.org","threadId":"61514","inReplyTo":"a2a59f5e-fd55-41d3-8472-b99256e1f428@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-09T17:20:36Z","receivedAt":"2024-06-09T17:20:39Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"Hello Phillip,\n\nOn 2024-06-09 16:29, phillip.wood123@gmail.com wrote:\n> On 08/06/2024 06:54, Dragan Simic wrote:\n>> On 2024-06-05 15:21, Phillip Wood wrote:\n>>> On 05/06/2024 10:09, Jeff King wrote:\n>>>> On Tue, Jun 04, 2024 at 08:32:04AM -0700, Junio C Hamano wrote:\n>>>> \n>>>> Given that the main use case for \"|\" is for human viewing through a\n>>>> pager, I think the colorful, filtered version meant for users is the\n>>>> best default. And then the \"bare\" version can come from an alternate\n>>>> command or a knob.\n>>> \n>>> I think that's a very good point. It is hard to see what \"|\" can be\n>>> used for other than viewing the hunk as (a) git does not read the\n>>> output so it cannot be used to filter or edit the hunk that is \n>>> applied\n>>> and (b) we pass an isolated hunk so the post-image offset in the hunk\n>>> header is likely to be wrong and there is no indication as to which\n>>> file it comes from so the program being run cannot apply the hunk\n>>> itself. Having the escape codes does make it harder to filter the\n>>> hunk. For example to just look at the post-image as one needs to do\n>>> something like\n>>> \n>>>     grep '^[^-+ @]*[+ @]'\n>>> \n>>> instead of just using '^[+ @]' as the pattern but the bonus is that\n>>> the output is colored.\n>> \n>> Agreed, but as I already explained, [1] only when using the bare \"|\"\n>> command.  When \"|xyz\" is used instead, the version of the hunk with\n>> no coloring escape sequences should be piped to xyz.\n> \n> Having read the message you referenced I'm struggling to understand\n> the use-case for stripping escape codes - what do you want to do with\n> the hunk that means you want to remove the color?\n\nLet me recap, please.  Basically, when an output of some command is\npiped into another command, e.g. by running \"grep -r abc . | grep def\",\nthe command that produces the piped output doesn't put the coloring\nescape codes into the produced output, because it's unknown can the\ncommand that receives it handle those escape codes properly.  That's\nbecome some kind of de facto standard embodied into the \"--color=auto\"\ncommand-line option for various utilities.\n\nIn the example above, one can have \"grep -n --color=auto\" defined as\ntheir alias for \"grep\", which is what I use, and the \"grep -r abc\"\nproduces the output with no coloring escape sequences, which gets\npiped into \"grep def\" that does produce coloring escape codes, because\nits output goes to the terminal emulator, which is expected to handle\nthose escape codes properly.\n\nIn our use case, Git becomes what produces a hunk as the output that\ngets piped into some program \"xyz\" by receiving \"|xyz\" as the command\nwhile \"git add -p\" is executed, but it isn't known can \"xyz\" handle\nthe coloring escape sequences properly, so they should not be included\nin the piped output, i.e. in the produced hunk.\n\nAs discussed later, [2] we could introduce \">xyz\" as another command\nfor \"git add -p\", so receiving \">xyz\" from the user would pipe the\nhunk to \"xyz\" with the coloring escape sequences included, because\nthe user already knows that \"xyz\" can handle them.  Separating this\ninto two commands is a safe approach.\n\nI hope this makes the whole thing more clear.\n\n[1] \nhttps://lore.kernel.org/git/844704794168f9fcb85c75014c84cde0@manjaro.org/\n[2] \nhttps://lore.kernel.org/git/7937845d7cb7ae0179c4922ed154c5c7@manjaro.org/\n"},{"id":"496760","messageId":"1ae0715d-df76-4019-995e-f00f3506f2ac@gmail.com","threadId":"61514","inReplyTo":"d092f5bb1d3bc7b7a821000a3cad8a1e@manjaro.org","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-06-10T08:27:49Z","receivedAt":"2024-06-10T08:27:55Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Dragan\n\nOn 09/06/2024 18:20, Dragan Simic wrote:\n> On 2024-06-09 16:29, phillip.wood123@gmail.com wrote:\n>\n>> Having read the message you referenced I'm struggling to understand\n>> the use-case for stripping escape codes - what do you want to do with\n>> the hunk that means you want to remove the color?\n> \n> Let me recap, please.\n> [...]\n> I hope this makes the whole thing more clear.\n\nIt is very clear _how_ you think it should work and I agree that makes \nsense in the context of a generic shell pipeline. What's not clear to me \nis _why_ that is useful in the context of displaying hunks in \"git add \n-p\". The purpose of \"git add -p\" is to allow the user to interactively \nstage individual hunks. The \"|\" command allows the user to display the \nhunk in a way that helps them decide whether to stage that particular \nhunk. Are you able to give a specific example of a command that would \nhelp you decide whether to stage a particular hunk where you would not \nwant to keep the escape codes?\n\nBest Wishes\n\nPhillip\n"},{"id":"496762","messageId":"bed3ddc6e330c67fb127c045ee4530ba@manjaro.org","threadId":"61514","inReplyTo":"1ae0715d-df76-4019-995e-f00f3506f2ac@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-10T09:09:37Z","receivedAt":"2024-06-10T09:09:45Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"Hello Phillip,\n\nOn 2024-06-10 10:27, Phillip Wood wrote:\n> On 09/06/2024 18:20, Dragan Simic wrote:\n>> On 2024-06-09 16:29, phillip.wood123@gmail.com wrote:\n>> \n>>> Having read the message you referenced I'm struggling to understand\n>>> the use-case for stripping escape codes - what do you want to do with\n>>> the hunk that means you want to remove the color?\n>> \n>> Let me recap, please.\n>> [...]\n>> I hope this makes the whole thing more clear.\n> \n> It is very clear _how_ you think it should work and I agree that makes\n> sense in the context of a generic shell pipeline. What's not clear to\n> me is _why_ that is useful in the context of displaying hunks in \"git\n> add -p\". The purpose of \"git add -p\" is to allow the user to\n> interactively stage individual hunks. The \"|\" command allows the user\n> to display the hunk in a way that helps them decide whether to stage\n> that particular hunk. Are you able to give a specific example of a\n> command that would help you decide whether to stage a particular hunk\n> where you would not want to keep the escape codes?\n\nWell, it isn't about not _wanting_ to keep the coloring escape\nsequences, but about the _need_ to play it safe, so to speak.\nIn other words, we can't know that a random \"xyz\", as in \"|xyz\",\ncan actually handle the coloring escape sequences, so we need\nto be on the safe side and protect the users from being hit by\ngarbled outputs.\n\nThat's why it would be the best to have \"P\", \"|xyz\" and \">xyz\"\nas the different \"git add -p\" options (piped to the configured\npager with preserved escape sequences, piped to \"xyz\" with\nstripped escape sequences, and piped to \"xyz\" with preserved\nescape sequences, respectively), as I proposed earlier.\n"},{"id":"496774","messageId":"9f1884ae-0f9f-4d9f-a262-b6929b81d7d8@gmail.com","threadId":"61514","inReplyTo":"219a195c-74d0-4c21-bf54-0752bb5b01df@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-06-10T14:09:48Z","receivedAt":"2024-06-10T14:09:56Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Rubén\n\nOn 09/06/2024 08:44, Rubén Justo wrote:\n> On Sat, Jun 08, 2024 at 07:54:34AM +0200, Dragan Simic wrote:\n> \n>> When \"|xyz\" is used instead, the version of the hunk with no coloring\n>> escape sequences should be piped to xyz.\n> \n> That is a sane and conservative approach, and I'm not opposed.  However,\n> giving the colorful version though a custom pager is a good thing to\n> have, I think, i.e: allowing a simple \"head\" without losing the\n> coloring.\n> \n> Let's recap a bit.\n> \n> Initially, this series aimed to enable sending chunks to the pager\n> during \"add -p\" sessions.\n> \n> To reduce the blast radius of spawning a pager for each chunk, we\n> introduced a new command \"P\".\n> \n> Junio suggested opening up the command to allow specifying a custom\n> pager, in the form of \"P<program>\".\n> \n> The \"P\" command started to resemble a lot to the common pipe operator.\n> Thus, we shifted to \"|<program>\".\n> \n> Some concerns were raised about controlling when to send coloring escape\n> sequences.\n\nI'm still not really convinced that the escape sequences are a problem. \nAs Peff has pointed out [1] this new command exists primarily to display \nthe current hunk. I've asked for concrete examples of programs that it \nwould be useful to run from \"git add -p\" where the escape codes are a \nproblem [2,3]. Sadly the replies talked in generic terms about an \nimaginary program without any reference to displaying or processing a \nhunk from \"git add -p\". Without a clear use case for stripping the \nescape codes I think we should add a single command that pipes the \ncolored output to a user specified program. We can make it clear in the \ndocumentation that the input to the user's command will contain escape \nsequences unless they pass \"-c color.diff=false\" when starting \"git add \n-p\". If it becomes clear that there is a use for the plain output we can \nadd that at a later stage.\n\nBest Wishes\n\nPhillip\n\n[1] \nhttps://lore.kernel.org/git/20240605090935.GF2345232@coredump.intra.peff.net\n[2] \nhttps://lore.kernel.org/git/a2a59f5e-fd55-41d3-8472-b99256e1f428@gmail.com>\n[3] \nhttps://lore.kernel.org/git/1ae0715d-df76-4019-995e-f00f3506f2ac@gmail.com\n\n> Several ideas were discussed to address this, including\n> introducing a new command \">\", a modifier for \"|\": \"||\", and others.\n> Alternatively, we could leave it up to the user to filter as needed.\n> Or, simply, do not send escape codes at all.\n> \n> So, looking back at the ideas discussed in the thread, perhaps a\n> reasonable next step might be to reintroduce the 'P<program>' command\n> and let '|<program>' be the way to send raw, uncolored, chunks.\n> \n> This approach makes sense to me.  I'll wait a bit before sending a\n> reroll to gather feedback, though.\n> \n> Thanks.\n"},{"id":"496778","messageId":"xmqq34pk6aju.fsf@gitster.g","threadId":"61514","inReplyTo":"9f1884ae-0f9f-4d9f-a262-b6929b81d7d8@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-10T16:13:09Z","receivedAt":"2024-06-10T16:13:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> ... We can make it\n> clear in the documentation that the input to the user's command will\n> contain escape sequences unless they pass \"-c color.diff=false\" when\n> starting \"git add -p\". If it becomes clear that there is a use for the\n> plain output we can add that at a later stage.\n\nIt is a reasonable stance to take, I would think.  I do not _mind_ a\nnon-colored mode existing once we identify a use case that needs it.\nThose who want colored output already are known (i.e. users of color\ncapable pagers with customization).  Once we give output that are\ncolored only, those who want plain output can make complain with\nconcrete use cases they have.\n\nThanks.\n\n\n"},{"id":"496788","messageId":"c4f54fdb-bf89-405b-a48a-acb58088557c@gmail.com","threadId":"61514","inReplyTo":"7937845d7cb7ae0179c4922ed154c5c7@manjaro.org","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-10T19:09:34Z","receivedAt":"2024-06-10T19:09:38Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Sun, Jun 09, 2024 at 09:57:20AM +0200, Dragan Simic wrote:\n> Hello Ruben,\n> \n> On 2024-06-09 09:44, Rubén Justo wrote:\n> > Some concerns were raised about controlling when to send coloring escape\n> > sequences.  Several ideas were discussed to address this, including\n> > introducing a new command \">\", a modifier for \"|\": \"||\", and others.\n> > Alternatively, we could leave it up to the user to filter as needed.\n> > Or, simply, do not send escape codes at all.\n> > \n> > So, looking back at the ideas discussed in the thread, perhaps a\n> > reasonable next step might be to reintroduce the 'P<program>' command\n> > and let '|<program>' be the way to send raw, uncolored, chunks.\n> \n> Actually, it would be better to re-introduce the \"P\" option, without\n> any parameters, which would display the current hunk through the\n> already configured pager\n\nI'm sorry, but why limit the \"P\" command now?  \n\nI understand the caution expressed in another message of this thread\nabout playing it safe, but I think the user won't be surprised if we\nrespect here the \"color.diff\" setting, just like we do with \"p\", and\n...\n\n> and let \"|<program>\" be the new option\n> that pipes hunks _without_ coloring escape sequences to \"<program>\".\n\n... we'll offer the command \"|\" to allow the user to process the raw\nchunk.\n"},{"id":"496789","messageId":"1f7b27b1-9bb2-49af-854a-762d0e75d508@gmail.com","threadId":"61514","inReplyTo":"9f1884ae-0f9f-4d9f-a262-b6929b81d7d8@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Rubén Justo","fromEmail":"rjusto@gmail.com","sentAt":"2024-06-10T19:14:04Z","receivedAt":"2024-06-10T19:14:07Z","isPatch":true,"sender":{"key":"rjusto@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5685487?v=4"},"body":"On Mon, Jun 10, 2024 at 03:09:48PM +0100, Phillip Wood wrote:\n\n> I'm still not really convinced that the escape sequences are a problem.\n\nA concern about the escape sequences was mentioned in this message:\nhttps://lore.kernel.org/git/b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com/\n\nIt arose when exploring the possibility of using the new command \"|\"\nwith tools that do not support escape sequences, e.g.: \"| vim -\", or\n\"| clip.exe\" to send the hunk to the clipboard, on Windows.\n"},{"id":"496790","messageId":"1a9f8385377d3aadfeb07ad62810b2df@manjaro.org","threadId":"61514","inReplyTo":"9f1884ae-0f9f-4d9f-a262-b6929b81d7d8@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-10T19:28:40Z","receivedAt":"2024-06-10T19:28:45Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"Hello Phillip,\n\nOn 2024-06-10 16:09, Phillip Wood wrote:\n> On 09/06/2024 08:44, Rubén Justo wrote:\n>> On Sat, Jun 08, 2024 at 07:54:34AM +0200, Dragan Simic wrote:\n>> \n>>> When \"|xyz\" is used instead, the version of the hunk with no coloring\n>>> escape sequences should be piped to xyz.\n>> \n>> That is a sane and conservative approach, and I'm not opposed.  \n>> However,\n>> giving the colorful version though a custom pager is a good thing to\n>> have, I think, i.e: allowing a simple \"head\" without losing the\n>> coloring.\n>> \n>> Let's recap a bit.\n>> \n>> Initially, this series aimed to enable sending chunks to the pager\n>> during \"add -p\" sessions.\n>> \n>> To reduce the blast radius of spawning a pager for each chunk, we\n>> introduced a new command \"P\".\n>> \n>> Junio suggested opening up the command to allow specifying a custom\n>> pager, in the form of \"P<program>\".\n>> \n>> The \"P\" command started to resemble a lot to the common pipe operator.\n>> Thus, we shifted to \"|<program>\".\n>> \n>> Some concerns were raised about controlling when to send coloring \n>> escape\n>> sequences.\n> \n> I'm still not really convinced that the escape sequences are a\n> problem. As Peff has pointed out [1] this new command exists primarily\n> to display the current hunk. I've asked for concrete examples of\n> programs that it would be useful to run from \"git add -p\" where the\n> escape codes are a problem [2,3]. Sadly the replies talked in generic\n> terms about an imaginary program without any reference to displaying\n> or processing a hunk from \"git add -p\". Without a clear use case for\n> stripping the escape codes I think we should add a single command that\n> pipes the colored output to a user specified program. We can make it\n> clear in the documentation that the input to the user's command will\n> contain escape sequences unless they pass \"-c color.diff=false\" when\n> starting \"git add -p\". If it becomes clear that there is a use for the\n> plain output we can add that at a later stage.\n\nRegarding examples, just have a look at less(1), [4] which is probably\nthe most commonly used pager.  It requires \"-R\" to be specified as one\nof its command-line parameters for the coloring escape sequences to be\nprocessed properly.  The presence of \"-R\" is taken as granted these \ndays,\nbut it hasn't always been the case.\n\nIf you want to see an example of the garbled output, as a result of the\ncoloring escape sequences not being processed correctly, just \"cd\" into\nyour favorite Git repository and run the following commands (assuming\nyou're using bash, if not, please adjust the first command):\n\n   export GIT_PAGER='less'\n   git log --patch\n\nI hope that will make more clear why I'm \"advocating\" for three separate\nnew commands for \"git add -p\", i.e. \"P\", \"|xyz\" and \">xyz\".\n\n> [1] \n> https://lore.kernel.org/git/20240605090935.GF2345232@coredump.intra.peff.net\n> [2] \n> https://lore.kernel.org/git/a2a59f5e-fd55-41d3-8472-b99256e1f428@gmail.com>\n> [3] \n> https://lore.kernel.org/git/1ae0715d-df76-4019-995e-f00f3506f2ac@gmail.com\n[4] https://man.archlinux.org/man/less.1.en\n"},{"id":"496791","messageId":"xmqq1q543729.fsf@gitster.g","threadId":"61514","inReplyTo":"1f7b27b1-9bb2-49af-854a-762d0e75d508@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-10T19:56:46Z","receivedAt":"2024-06-10T19:56:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Rubén Justo <rjusto@gmail.com> writes:\n\n> On Mon, Jun 10, 2024 at 03:09:48PM +0100, Phillip Wood wrote:\n>\n>> I'm still not really convinced that the escape sequences are a problem.\n>\n> A concern about the escape sequences was mentioned in this message:\n> https://lore.kernel.org/git/b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com/\n>\n> It arose when exploring the possibility of using the new command \"|\"\n> with tools that do not support escape sequences, e.g.: \"| vim -\", or\n> \"| clip.exe\" to send the hunk to the clipboard, on Windows.\n\nLet's not bikeshed this for too long.\n\nPipe \"|\" stuff that is suitable for pager in the context of Git\n(i.e. if color.ui does not forbid coloring with \"no\", their usual\npager is capable of color, so we send colored output to the pipe),\nand if somebody wants uncolored output later, they can use \"P\" to\npipe or \">\" to file or whatever other enhancement they want, but\nlet's leave them out of this round.\n"},{"id":"496794","messageId":"xmqqle3c1ryz.fsf@gitster.g","threadId":"61514","inReplyTo":"1a9f8385377d3aadfeb07ad62810b2df@manjaro.org","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-06-10T20:08:04Z","receivedAt":"2024-06-10T20:08:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dragan Simic <dsimic@manjaro.org> writes:\n\n> If you want to see an example of the garbled output, as a result of the\n> coloring escape sequences not being processed correctly, just \"cd\" into\n> your favorite Git repository and run the following commands (assuming\n> you're using bash, if not, please adjust the first command):\n>\n>   export GIT_PAGER='less'\n>   git log --patch\n>\n> I hope that will make more clear why I'm \"advocating\" for three separate\n> new commands for \"git add -p\", i.e. \"P\", \"|xyz\" and \">xyz\".\n\nWe are talking about folks who use a pager in the context of Git,\nand to them, your example is totally made-up and irrelevant one.\n\nEither they use a color-enabled pager (like \"less -R\") or they have\nconfigured color.ui to disable coloring.  So I do not see this quite\nas a safety issue.\n\n"},{"id":"496802","messageId":"3268c498c9ee60803384db6db2d0e94b@manjaro.org","threadId":"61514","inReplyTo":"c4f54fdb-bf89-405b-a48a-acb58088557c@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-10T21:02:08Z","receivedAt":"2024-06-10T21:02:11Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"Hello all,\n\nOn 2024-06-10 21:09, Rubén Justo wrote:\n> On Sun, Jun 09, 2024 at 09:57:20AM +0200, Dragan Simic wrote:\n>> On 2024-06-09 09:44, Rubén Justo wrote:\n>> > Some concerns were raised about controlling when to send coloring escape\n>> > sequences.  Several ideas were discussed to address this, including\n>> > introducing a new command \">\", a modifier for \"|\": \"||\", and others.\n>> > Alternatively, we could leave it up to the user to filter as needed.\n>> > Or, simply, do not send escape codes at all.\n>> >\n>> > So, looking back at the ideas discussed in the thread, perhaps a\n>> > reasonable next step might be to reintroduce the 'P<program>' command\n>> > and let '|<program>' be the way to send raw, uncolored, chunks.\n>> \n>> Actually, it would be better to re-introduce the \"P\" option, without\n>> any parameters, which would display the current hunk through the\n>> already configured pager\n> \n> I'm sorry, but why limit the \"P\" command now?\n\nI'll explain below, and provide an alternate approach.\n\n> I understand the caution expressed in another message of this thread\n> about playing it safe, but I think the user won't be surprised if we\n> respect here the \"color.diff\" setting, just like we do with \"p\", and\n> ...\n\nLet me provide a quotation from git-config(1):\n\n   color.diff\n            Whether to use ANSI escape sequences to add color to\n            patches. If this is set to always, git-diff(1), git-log(1),\n            and git-show(1) will use color for all patches. If it\n            is set to true or auto, those commands will only use color\n            when output is to the terminal. If unset, then the value\n            of color.ui is used (auto by default).\n\nThe key takeaway there is \"when output is to the terminal\", but when\nyou think about it, the description is a bit vague, because the output\nto the terminal is also piped through the default pager when running\ngit-log(1), for example.  Perhaps the more accurate wording could be\n\"when the known destination of the output is the terminal\", but that\nmight also be a bit confusing.\n\nThus, if we want to honor the \"color.diff\" setting, which I think we\nshould, then we can do everything with only one new command for \"git\nadd -p\", which would be \"P<program>\".  It would be seen as just another\n\"channel\" for the \"output to the terminal\" category, and it would honor\n\"color.diff\" just like git-log(1) does, for example, both when received\nas \"P\" and as \"Pxyz\" from the user.\n\nAs a result, there would be no way to pipe a hunk to \"xyz\" with the\ncoloring escape sequences removed, unless \"color.diff\" is set to false.\nThat would be the most consistent approach, because \"P<program>\" would\nbe just another \"channel\" to reaching the terminal with the output.\n\nAlso, we'd need to update the documentation for \"color.diff\" to include\na description of the new \"git add -p\" command.\n\n>> and let \"|<program>\" be the new option\n>> that pipes hunks _without_ coloring escape sequences to \"<program>\".\n> \n> ... we'll offer the command \"|\" to allow the user to process the raw\n> chunk.\n\nSorry, but now I have to ask why do we want that?  If the goal is to\nopen the hunk in an editor, there's already a command in \"git add -p\"\nfor that purpose.\n"},{"id":"496804","messageId":"6c40aa4d829cc80114062984f9d148cf@manjaro.org","threadId":"61514","inReplyTo":"1f7b27b1-9bb2-49af-854a-762d0e75d508@gmail.com","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-10T21:08:23Z","receivedAt":"2024-06-10T21:08:25Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-06-10 21:14, Rubén Justo wrote:\n> On Mon, Jun 10, 2024 at 03:09:48PM +0100, Phillip Wood wrote:\n> \n>> I'm still not really convinced that the escape sequences are a \n>> problem.\n> \n> A concern about the escape sequences was mentioned in this message:\n> https://lore.kernel.org/git/b7e24b08-40a1-4b18-89f6-e25ab96facaf@gmail.com/\n> \n> It arose when exploring the possibility of using the new command \"|\"\n> with tools that do not support escape sequences, e.g.: \"| vim -\", or\n> \"| clip.exe\" to send the hunk to the clipboard, on Windows.\n\nHmm, I asked [1] why do we need \"|xyz\" and escape-sequence-free hunks,\nand the answer is above.  Another example would be \"|xclip\" on Linux.\n\nWith that in mind, let's have \"P\" and \"Pxyz\" that both honor \n\"color.diff\",\nas already proposed, [1] and \"|xyz\" that strips the coloring escape\nsequences.\n\n[1] \nhttps://lore.kernel.org/git/3268c498c9ee60803384db6db2d0e94b@manjaro.org/\n"},{"id":"496805","messageId":"e2c07b0cd0a47b9b9cf5736571f19d2f@manjaro.org","threadId":"61514","inReplyTo":"xmqqle3c1ryz.fsf@gitster.g","subject":"Re: [PATCH v3 0/6] use the pager in 'add -p'","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-06-10T21:16:40Z","receivedAt":"2024-06-10T21:16:42Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-06-10 22:08, Junio C Hamano wrote:\n> Dragan Simic <dsimic@manjaro.org> writes:\n> \n>> If you want to see an example of the garbled output, as a result of \n>> the\n>> coloring escape sequences not being processed correctly, just \"cd\" \n>> into\n>> your favorite Git repository and run the following commands (assuming\n>> you're using bash, if not, please adjust the first command):\n>> \n>>   export GIT_PAGER='less'\n>>   git log --patch\n>> \n>> I hope that will make more clear why I'm \"advocating\" for three \n>> separate\n>> new commands for \"git add -p\", i.e. \"P\", \"|xyz\" and \">xyz\".\n> \n> We are talking about folks who use a pager in the context of Git,\n> and to them, your example is totally made-up and irrelevant one.\n\nAh, that wasn't my intention.  I just wanted to provide an example\nthat shows what a garbled output looks like, because I was under\nimpression that Phillip asked for such an example.\n\n> Either they use a color-enabled pager (like \"less -R\") or they have\n> configured color.ui to disable coloring.  So I do not see this quite\n> as a safety issue.\n\nAgreed, but only if we end up honoring \"color.ui\" and \"color.diff\"\nin the new \"git add -p\" command(s).\n"}]}