add -p: show user's hunk decision when selecting hunks

When a user is interactively deciding which hunks to use or skip for staging, unstaging, stashing etc, there is no way to know the decision previously chosen for a hunk when navigating through the previous and next hunks using K/J respectively. Improve the UI to explicitly show if a user has previously decided to use a hunk (by pressing 'y') or skip the hunk (by pressing 'n'). This will improve clarity when and aid the navigation process for the user. Reported-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Abraham Samuel Adekunle committed Jan 8, 2026 at 16:07 UTC 8cafc305e22a59efb92472d4132616e24d3184c6
2 files changed +53 -46
add-patch.c
+44 -37
@@ -42,10 +42,10 @@ static struct patch_mode patch_mode_add = {
42 .apply_args = { "--cached", NULL },
43 .apply_check_args = { "--cached", NULL },
44 .prompt_mode = {
45 - N_("Stage mode change [y,n,q,a,d%s,?]? "),
46 - N_("Stage deletion [y,n,q,a,d%s,?]? "),
47 - N_("Stage addition [y,n,q,a,d%s,?]? "),
48 - N_("Stage this hunk [y,n,q,a,d%s,?]? ")
45 + N_("Stage mode change%s [y,n,q,a,d%s,?]? "),
46 + N_("Stage deletion%s [y,n,q,a,d%s,?]? "),
47 + N_("Stage addition%s [y,n,q,a,d%s,?]? "),
48 + N_("Stage this hunk%s [y,n,q,a,d%s,?]? ")
49 },
50 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
51 "will immediately be marked for staging."),
@@ -64,10 +64,10 @@ static struct patch_mode patch_mode_stash = {
64 .apply_args = { "--cached", NULL },
65 .apply_check_args = { "--cached", NULL },
66 .prompt_mode = {
67 - N_("Stash mode change [y,n,q,a,d%s,?]? "),
68 - N_("Stash deletion [y,n,q,a,d%s,?]? "),
69 - N_("Stash addition [y,n,q,a,d%s,?]? "),
70 - N_("Stash this hunk [y,n,q,a,d%s,?]? "),
67 + N_("Stash mode change%s [y,n,q,a,d%s,?]? "),
68 + N_("Stash deletion%s [y,n,q,a,d%s,?]? "),
69 + N_("Stash addition%s [y,n,q,a,d%s,?]? "),
70 + N_("Stash this hunk%s [y,n,q,a,d%s,?]? "),
71 },
72 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
73 "will immediately be marked for stashing."),
@@ -88,10 +88,10 @@ static struct patch_mode patch_mode_reset_head = {
88 .is_reverse = 1,
89 .index_only = 1,
90 .prompt_mode = {
91 - N_("Unstage mode change [y,n,q,a,d%s,?]? "),
92 - N_("Unstage deletion [y,n,q,a,d%s,?]? "),
93 - N_("Unstage addition [y,n,q,a,d%s,?]? "),
94 - N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
91 + N_("Unstage mode change%s [y,n,q,a,d%s,?]? "),
92 + N_("Unstage deletion%s [y,n,q,a,d%s,?]? "),
93 + N_("Unstage addition%s [y,n,q,a,d%s,?]? "),
94 + N_("Unstage this hunk%s [y,n,q,a,d%s,?]? "),
95 },
96 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
97 "will immediately be marked for unstaging."),
@@ -111,10 +111,10 @@ static struct patch_mode patch_mode_reset_nothead = {
111 .apply_check_args = { "--cached", NULL },
112 .index_only = 1,
113 .prompt_mode = {
114 - N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
115 - N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
116 - N_("Apply addition to index [y,n,q,a,d%s,?]? "),
117 - N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
114 + N_("Apply mode change to index%s [y,n,q,a,d%s,?]? "),
115 + N_("Apply deletion to index%s [y,n,q,a,d%s,?]? "),
116 + N_("Apply addition to index%s [y,n,q,a,d%s,?]? "),
117 + N_("Apply this hunk to index%s [y,n,q,a,d%s,?]? "),
118 },
119 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
120 "will immediately be marked for applying."),
@@ -134,10 +134,10 @@ static struct patch_mode patch_mode_checkout_index = {
134 .apply_check_args = { "-R", NULL },
135 .is_reverse = 1,
136 .prompt_mode = {
137 - N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
138 - N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
139 - N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
140 - N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
137 + N_("Discard mode change from worktree%s [y,n,q,a,d%s,?]? "),
138 + N_("Discard deletion from worktree%s [y,n,q,a,d%s,?]? "),
139 + N_("Discard addition from worktree%s [y,n,q,a,d%s,?]? "),
140 + N_("Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "),
141 },
142 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
143 "will immediately be marked for discarding."),
@@ -157,10 +157,10 @@ static struct patch_mode patch_mode_checkout_head = {
157 .apply_check_args = { "-R", NULL },
158 .is_reverse = 1,
159 .prompt_mode = {
160 - N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
161 - N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
162 - N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
163 - N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
160 + N_("Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "),
161 + N_("Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "),
162 + N_("Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "),
163 + N_("Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "),
164 },
165 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
166 "will immediately be marked for discarding."),
@@ -179,10 +179,10 @@ static struct patch_mode patch_mode_checkout_nothead = {
179 .apply_for_checkout = 1,
180 .apply_check_args = { NULL },
181 .prompt_mode = {
182 - N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
183 - N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
184 - N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
185 - N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
182 + N_("Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "),
183 + N_("Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "),
184 + N_("Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "),
185 + N_("Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "),
186 },
187 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
188 "will immediately be marked for applying."),
@@ -202,10 +202,10 @@ static struct patch_mode patch_mode_worktree_head = {
202 .apply_check_args = { "-R", NULL },
203 .is_reverse = 1,
204 .prompt_mode = {
205 - N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
206 - N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
207 - N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
208 - N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
205 + N_("Discard mode change from worktree%s [y,n,q,a,d%s,?]? "),
206 + N_("Discard deletion from worktree%s [y,n,q,a,d%s,?]? "),
207 + N_("Discard addition from worktree%s [y,n,q,a,d%s,?]? "),
208 + N_("Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "),
209 },
210 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
211 "will immediately be marked for discarding."),
@@ -224,10 +224,10 @@ static struct patch_mode patch_mode_worktree_nothead = {
224 .apply_args = { NULL },
225 .apply_check_args = { NULL },
226 .prompt_mode = {
227 - N_("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
228 - N_("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
229 - N_("Apply addition to worktree [y,n,q,a,d%s,?]? "),
230 - N_("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
227 + N_("Apply mode change to worktree%s [y,n,q,a,d%s,?]? "),
228 + N_("Apply deletion to worktree%s [y,n,q,a,d%s,?]? "),
229 + N_("Apply addition to worktree%s [y,n,q,a,d%s,?]? "),
230 + N_("Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "),
231 },
232 .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
233 "will immediately be marked for applying."),
@@ -1460,6 +1460,7 @@ static int patch_update_file(struct add_p_state *s,
1460 render_diff_header(s, file_diff, colored, &s->buf);
1461 fputs(s->buf.buf, stdout);
1462 for (;;) {
1463 + const char *hunk_use_decision = "";
1464 enum {
1465 ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
1466 ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
@@ -1564,8 +1565,14 @@ static int patch_update_file(struct add_p_state *s,
1565 (uintmax_t)(file_diff->hunk_nr
1566 ? file_diff->hunk_nr
1567 : 1));
1568 + if (hunk->use != UNDECIDED_HUNK) {
1569 + if (hunk->use == USE_HUNK)
1570 + hunk_use_decision = _(" (was: y)");
1571 + else
1572 + hunk_use_decision = _(" (was: n)");
1573 + }
1574 printf(_(s->mode->prompt_mode[prompt_mode_type]),
1568 - s->buf.buf);
1575 + hunk_use_decision, s->buf.buf);
1576 if (*s->s.reset_color_interactive)
1577 fputs(s->s.reset_color_interactive, stdout);
1578 fflush(stdout);
t/t3701-add-interactive.sh
+9 -9
@@ -527,7 +527,7 @@ test_expect_success 'goto hunk 1 with "g 1"' '
527 _10
528 +15
529 _20
530 - (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
530 + (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
531 EOF
532 test_write_lines s y g 1 | git add -p >actual &&
533 tail -n 7 <actual >actual.trimmed &&
@@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
540 _10
541 +15
542 _20
543 - (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
543 + (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
544 EOF
545 test_write_lines s y g1 | git add -p >actual &&
546 tail -n 4 <actual >actual.trimmed &&
@@ -554,7 +554,7 @@ test_expect_success 'navigate to hunk via regex /pattern' '
554 _10
555 +15
556 _20
557 - (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
557 + (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
558 EOF
559 test_write_lines s y /1,2 | git add -p >actual &&
560 tail -n 5 <actual >actual.trimmed &&
@@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
567 _10
568 +15
569 _20
570 - (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
570 + (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
571 EOF
572 test_write_lines s y / 1,2 | git add -p >actual &&
573 tail -n 4 <actual >actual.trimmed &&
@@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
579 tr _ " " >expect <<-EOF &&
580 +15
581 20
582 - (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
582 + (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
583 10
584 +15
585 20
586 - (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
586 + (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
587 EOF
588 test_write_lines s y g 1 p | git add -p >actual &&
589 tail -n 7 <actual >actual.trimmed &&
@@ -595,11 +595,11 @@ test_expect_success TTY 'print again the hunk (PAGER)' '
595 cat >expect <<-EOF &&
596 <GREEN>+<RESET><GREEN>15<RESET>
597 20<RESET>
598 - <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
598 + <BOLD;BLUE>(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
599 PAGER 10<RESET>
600 PAGER <GREEN>+<RESET><GREEN>15<RESET>
601 PAGER 20<RESET>
602 - <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
602 + <BOLD;BLUE>(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
603 EOF
604 test_write_lines s y g 1 P |
605 (
@@ -810,7 +810,7 @@ test_expect_success 'colors can be overridden' '
810 <BOLD>-old<RESET>
811 <BLUE>+new<RESET>
812 <CYAN> more-context<RESET>
813 - <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
813 + <YELLOW>(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
814 EOF
815 test_cmp expect actual
816 '