threads / patch / 64557

patchadd -p: show hunk selection state when selecting hunks

Subject: [PATCH] add -p: show hunk selection state when selecting hunks

## tl;dr

25 messages between Nov 30, 2025 and Jan 11, 2026. Diffs are folded; open one to read it.

replies: 24people: 6as markdown or json

Abraham Samuel Adekunle· Nov 30, 2025, 15:06 UTC · lore

When selecting hunks to stage or not to stage, there is no way to know if a hunk has been selected or not when navigating through the previous and next hunks using K/J respectively.

Improve the UI to show whether a particular hunk has been selected or deselected to improve clarity and aid the navigation process.

Reported-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>
---
 add-patch.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)
Show changes to add-patch.c +14 −2
diff --git a/add-patch.c b/add-patch.c
index 173a53241e..e70e390506 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -45,7 +45,7 @@ static struct patch_mode patch_mode_add = {
 		N_("Stage mode change [y,n,q,a,d%s,?]? "),
 		N_("Stage deletion [y,n,q,a,d%s,?]? "),
 		N_("Stage addition [y,n,q,a,d%s,?]? "),
-		N_("Stage this hunk [y,n,q,a,d%s,?]? ")
+		N_("Stage this hunk [y,n,q,a,d%s,?] %s? ")
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for staging."),
@@ -1564,7 +1564,19 @@ static int patch_update_file(struct add_p_state *s,
 			      (uintmax_t)(file_diff->hunk_nr
 						? file_diff->hunk_nr
 						: 1));
-		printf(_(s->mode->prompt_mode[prompt_mode_type]),
+		if (prompt_mode_type == PROMPT_HUNK) {
+			const char *state = "";
+			if (file_diff->hunk_nr) {
+				if (hunk->use == USE_HUNK)
+					state = _("[selected]");
+				else if (hunk->use == SKIP_HUNK)
+					state = _("[deselected]");
+			}
+			printf(_(s->mode->prompt_mode[prompt_mode_type]),
+				s->buf.buf, state);
+		}
+		else
+			printf(_(s->mode->prompt_mode[prompt_mode_type]),
 		       s->buf.buf);
 		if (*s->s.reset_color_interactive)
 			fputs(s->s.reset_color_interactive, stdout);
-- 
2.39.5 (Apple Git-154)
Junio C Hamano· Nov 30, 2025, 18:32 UTC · re: Abraham Samuel Adekunle · lore

Re: [PATCH] add -p: show hunk selection state when selecting hunks

Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:
Show 11 quoted lines
> diff --git a/add-patch.c b/add-patch.c
> index 173a53241e..e70e390506 100644
> --- a/add-patch.c
> +++ b/add-patch.c
> @@ -45,7 +45,7 @@ static struct patch_mode patch_mode_add = {
>  		N_("Stage mode change [y,n,q,a,d%s,?]? "),
>  		N_("Stage deletion [y,n,q,a,d%s,?]? "),
>  		N_("Stage addition [y,n,q,a,d%s,?]? "),
> -		N_("Stage this hunk [y,n,q,a,d%s,?]? ")
> +		N_("Stage this hunk [y,n,q,a,d%s,?] %s? ")
>  	},
Three comments:
 * These sets of prompts exist for each front-end that uses the
   interactive patch machinery, and we are looking at the set used
   by "git add -p".  But the "I came back here with K, or I do not
   remember which between k and K I came back here with, and I
   cannot easily tell if the hunk I am looking at is already
   selected" issue is shared with other users like "git reset -p".
 * "chmod +x Makefile && echo >>Makefile && git add -p" would ask if
   you want to stage the mode change of the path and content change
   for the path separately.  You may skip, and later come back with
   K to this question.  The same "hmph, have I selected to use
   this?" issue exists, no?
 * The existing "[choices]? " was designed to be at the very end of
   the question, so that the answer given by the user will come
   immediately after the offered choices.  Adding an overly long
   "selected" or "deselected" to make it "[choices] selected?" does
   not give us a pleasant end-user experience.

Also, after you decided on one hunk when you have two hunks, typing 'j' or 'k' would tell you "No other undecided hunk". The phrase used here, "undecided", refers to the choice between USE or SKIP. To convey the intent clearly, "Select"/"Deselect" feels a rather indirect way (i.e. "selected for use" vs "selected to skip") to say what is happening.

Ideally, if we can convey
    Stage this mode change (you previously decided to use it) [y,n,q,a,d%s,?]?
    Stage this mode change (you previously decided to skip it) [y,n,q,a,d%s,?]?
    Stage this deletion (you previously decided to use it) [y,n,q,a,d%sm,?]?
    ...

without wasting too many extra display width, that would be great, but this patch is not quite there, I am afraid to say.

Show 21 quoted lines
> @@ -1564,7 +1564,19 @@ static int patch_update_file(struct add_p_state *s,
>  			      (uintmax_t)(file_diff->hunk_nr
>  						? file_diff->hunk_nr
>  						: 1));
> -		printf(_(s->mode->prompt_mode[prompt_mode_type]),
> +		if (prompt_mode_type == PROMPT_HUNK) {
> +			const char *state = "";
> +			if (file_diff->hunk_nr) {
> +				if (hunk->use == USE_HUNK)
> +					state = _("[selected]");
> +				else if (hunk->use == SKIP_HUNK)
> +					state = _("[deselected]");
> +			}
> +			printf(_(s->mode->prompt_mode[prompt_mode_type]),
> +				s->buf.buf, state);
> +		}
> +		else
> +			printf(_(s->mode->prompt_mode[prompt_mode_type]),
>  		       s->buf.buf);
>  		if (*s->s.reset_color_interactive)
>  			fputs(s->s.reset_color_interactive, stdout);
Abraham Samuel Adekunle· Dec 1, 2025, 10:01 UTC · re: Junio C Hamano · lore

Re: [PATCH] add -p: show hunk selection state when selecting hunks

On Sun, Nov 30, 2025 at 10:32:56AM -0800, Junio C Hamano wrote:
Show 22 quoted lines
> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:
> 
> > diff --git a/add-patch.c b/add-patch.c
> > index 173a53241e..e70e390506 100644
> > --- a/add-patch.c
> > +++ b/add-patch.c
> > @@ -45,7 +45,7 @@ static struct patch_mode patch_mode_add = {
> >  		N_("Stage mode change [y,n,q,a,d%s,?]? "),
> >  		N_("Stage deletion [y,n,q,a,d%s,?]? "),
> >  		N_("Stage addition [y,n,q,a,d%s,?]? "),
> > -		N_("Stage this hunk [y,n,q,a,d%s,?]? ")
> > +		N_("Stage this hunk [y,n,q,a,d%s,?] %s? ")
> >  	},
> 
> Three comments:
> 
>  * These sets of prompts exist for each front-end that uses the
>    interactive patch machinery, and we are looking at the set used
>    by "git add -p".  But the "I came back here with K, or I do not
>    remember which between k and K I came back here with, and I
>    cannot easily tell if the hunk I am looking at is already
>    selected" issue is shared with other users like "git reset -p".

Hello Junio, Thank you for your review.

Okay, are you suggesting I apply the tweak in all prompt_mode arrays used by other front-ends. I can see all the others modes (patch_mode_*) shown in the file.

Show 6 quoted lines
> 
>  * "chmod +x Makefile && echo >>Makefile && git add -p" would ask if
>    you want to stage the mode change of the path and content change
>    for the path separately.  You may skip, and later come back with
>    K to this question.  The same "hmph, have I selected to use
>    this?" issue exists, no?

Yes true, the issue does exist. I will fix the change for the others

Show 6 quoted lines
> 
>  * The existing "[choices]? " was designed to be at the very end of
>    the question, so that the answer given by the user will come
>    immediately after the offered choices.  Adding an overly long
>    "selected" or "deselected" to make it "[choices] selected?" does
>    not give us a pleasant end-user experience.
Okay.
Show 16 quoted lines
> 
> Also, after you decided on one hunk when you have two hunks, typing
> 'j' or 'k' would tell you "No other undecided hunk".  The phrase
> used here, "undecided", refers to the choice between USE or SKIP.
> To convey the intent clearly, "Select"/"Deselect" feels a rather
> indirect way (i.e. "selected for use" vs "selected to skip") to say
> what is happening.
> 
> Ideally, if we can convey
> 
>     Stage this mode change (you previously decided to use it) [y,n,q,a,d%s,?]?
>     Stage this mode change (you previously decided to skip it) [y,n,q,a,d%s,?]?
>     Stage this deletion (you previously decided to use it) [y,n,q,a,d%sm,?]?
>     ...
> 
> without wasting too many extra display width, that would be great,

Okay this makes sense. But since the display width is something to watch out for, would something like below siffice?

	Stage this mode change (previous decision: stage) [y,n,q,a,d%s,?]?
	Stage this mode change (previous decision: skip) [y,n,q,a,d%s,?]?
	Stage this deletion (previous decision: stage) [y,n,q,a,d%s,?]?
> but this patch is not quite there, I am afraid to say.

Thank you Junio, I will work towards getting it there. [...]

Abraham
Abraham Samuel Adekunle· Jan 1, 2026, 21:04 UTC · re: Abraham Samuel Adekunle · lore

[GSoC PATCH v2] 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: Reported-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>
---
Changes in v2:
--------------
- Modified subject to indicate GSoC participation and user's hunk decision.
- Modified the code to detect the user's decision for each front-end set
  that uses the interactive patch machinery
- Changes the decision phrase to 'previous decision: _decision_' from
  'Selected/Deselected'
- Placed the decision phrase before the [choices] instead of after the [choices].
 add-patch.c | 84 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 47 insertions(+), 37 deletions(-)
Show changes to add-patch.c +47 −37
diff --git a/add-patch.c b/add-patch.c
index 173a53241e..bc7a340d21 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -42,10 +42,10 @@ static struct patch_mode patch_mode_add = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stage mode change [y,n,q,a,d%s,?]? "),
-		N_("Stage deletion [y,n,q,a,d%s,?]? "),
-		N_("Stage addition [y,n,q,a,d%s,?]? "),
-		N_("Stage this hunk [y,n,q,a,d%s,?]? ")
+		N_("Stage mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Stage deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Stage addition%s[y,n,q,a,d%s,?]? "),
+		N_("Stage this hunk%s[y,n,q,a,d%s,?]? ")
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for staging."),
@@ -64,10 +64,10 @@ static struct patch_mode patch_mode_stash = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stash mode change [y,n,q,a,d%s,?]? "),
-		N_("Stash deletion [y,n,q,a,d%s,?]? "),
-		N_("Stash addition [y,n,q,a,d%s,?]? "),
-		N_("Stash this hunk [y,n,q,a,d%s,?]? "),
+		N_("Stash mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Stash deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Stash addition%s[y,n,q,a,d%s,?]? "),
+		N_("Stash this hunk%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for stashing."),
@@ -88,10 +88,10 @@ static struct patch_mode patch_mode_reset_head = {
 	.is_reverse = 1,
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Unstage mode change [y,n,q,a,d%s,?]? "),
-		N_("Unstage deletion [y,n,q,a,d%s,?]? "),
-		N_("Unstage addition [y,n,q,a,d%s,?]? "),
-		N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
+		N_("Unstage mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage addition%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage this hunk%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for unstaging."),
@@ -111,10 +111,10 @@ static struct patch_mode patch_mode_reset_nothead = {
 	.apply_check_args = { "--cached", NULL },
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -134,10 +134,10 @@ static struct patch_mode patch_mode_checkout_index = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -157,10 +157,10 @@ static struct patch_mode patch_mode_checkout_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from index and worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -179,10 +179,10 @@ static struct patch_mode patch_mode_checkout_nothead = {
 	.apply_for_checkout = 1,
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index and worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -202,10 +202,10 @@ static struct patch_mode patch_mode_worktree_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -224,10 +224,10 @@ static struct patch_mode patch_mode_worktree_nothead = {
 	.apply_args = { NULL },
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -1448,6 +1448,7 @@ static int patch_update_file(struct add_p_state *s,
 	ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
 	struct hunk *hunk;
 	char ch;
+	const char *hunk_use_decision;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	int colored = !!s->colored.len, quit = 0, use_pager = 0;
 	enum prompt_mode_type prompt_mode_type;
@@ -1564,8 +1565,17 @@ static int patch_update_file(struct add_p_state *s,
 			      (uintmax_t)(file_diff->hunk_nr
 						? file_diff->hunk_nr
 						: 1));
+		if (file_diff->hunk_nr) {
+			if (hunk->use == USE_HUNK)
+				hunk_use_decision = _(" (previous decision: use) ");
+			else if (hunk->use == SKIP_HUNK)
+				hunk_use_decision = _(" (previous decision: skip) ");
+			else
+				hunk_use_decision = " ";
+
+		}
 		printf(_(s->mode->prompt_mode[prompt_mode_type]),
-		       s->buf.buf);
+			hunk_use_decision, s->buf.buf);
 		if (*s->s.reset_color_interactive)
 			fputs(s->s.reset_color_interactive, stdout);
 		fflush(stdout);
-- 
2.39.5 (Apple Git-154)
Junio C Hamano· Jan 1, 2026, 22:54 UTC · re: Abraham Samuel Adekunle · lore

Re: [GSoC PATCH v2] add -p: show user's hunk decision when selecting hunks

Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:
Show 23 quoted lines
> 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: Reported-by: Junio C Hamano <gitster@pobox.com>
> Signed-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>
> ---
> Changes in v2:
> --------------
> - Modified subject to indicate GSoC participation and user's hunk decision.
> - Modified the code to detect the user's decision for each front-end set
>   that uses the interactive patch machinery
> - Changes the decision phrase to 'previous decision: _decision_' from
>   'Selected/Deselected'
> - Placed the decision phrase before the [choices] instead of after the [choices].
>  add-patch.c | 84 ++++++++++++++++++++++++++++++-----------------------
>  1 file changed, 47 insertions(+), 37 deletions(-)
Doesn't this break t3701?
Samuel Abraham· Jan 2, 2026, 07:20 UTC · re: Junio C Hamano · lore

Re: [GSoC PATCH v2] add -p: show user's hunk decision when selecting hunks

On Thu, Jan 1, 2026 at 11:54 PM Junio C Hamano <gitster@pobox.com> wrote:
Show 28 quoted lines
>
> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:
>
> > 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: Reported-by: Junio C Hamano <gitster@pobox.com>
> > Signed-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>
> > ---
> > Changes in v2:
> > --------------
> > - Modified subject to indicate GSoC participation and user's hunk decision.
> > - Modified the code to detect the user's decision for each front-end set
> >   that uses the interactive patch machinery
> > - Changes the decision phrase to 'previous decision: _decision_' from
> >   'Selected/Deselected'
> > - Placed the decision phrase before the [choices] instead of after the [choices].
> >  add-patch.c | 84 ++++++++++++++++++++++++++++++-----------------------
> >  1 file changed, 47 insertions(+), 37 deletions(-)
>
> Doesn't this break t3701?

Thank you for the review Junio. Yes it does break t3701. I will fix the broken test cases to conform to the new code and send an updated version.

Thanks Abraham.

Abraham Samuel Adekunle· Jan 2, 2026, 18:51 UTC · re: Abraham Samuel Adekunle · lore

[GSoC PATCH v3] 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>
---
Changes in v2:
--------------
- Modified t3701-add-interactive.sh for conform to new changes
 add-patch.c                | 84 +++++++++++++++++++++-----------------
 t/t3701-add-interactive.sh | 14 +++----
 2 files changed, 54 insertions(+), 44 deletions(-)
Show changes to 2 files +54 −44

add-patch.c, t/t3701-add-interactive.sh

diff --git a/add-patch.c b/add-patch.c
index 173a53241e..bc7a340d21 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -42,10 +42,10 @@ static struct patch_mode patch_mode_add = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stage mode change [y,n,q,a,d%s,?]? "),
-		N_("Stage deletion [y,n,q,a,d%s,?]? "),
-		N_("Stage addition [y,n,q,a,d%s,?]? "),
-		N_("Stage this hunk [y,n,q,a,d%s,?]? ")
+		N_("Stage mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Stage deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Stage addition%s[y,n,q,a,d%s,?]? "),
+		N_("Stage this hunk%s[y,n,q,a,d%s,?]? ")
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for staging."),
@@ -64,10 +64,10 @@ static struct patch_mode patch_mode_stash = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stash mode change [y,n,q,a,d%s,?]? "),
-		N_("Stash deletion [y,n,q,a,d%s,?]? "),
-		N_("Stash addition [y,n,q,a,d%s,?]? "),
-		N_("Stash this hunk [y,n,q,a,d%s,?]? "),
+		N_("Stash mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Stash deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Stash addition%s[y,n,q,a,d%s,?]? "),
+		N_("Stash this hunk%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for stashing."),
@@ -88,10 +88,10 @@ static struct patch_mode patch_mode_reset_head = {
 	.is_reverse = 1,
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Unstage mode change [y,n,q,a,d%s,?]? "),
-		N_("Unstage deletion [y,n,q,a,d%s,?]? "),
-		N_("Unstage addition [y,n,q,a,d%s,?]? "),
-		N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
+		N_("Unstage mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage addition%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage this hunk%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for unstaging."),
@@ -111,10 +111,10 @@ static struct patch_mode patch_mode_reset_nothead = {
 	.apply_check_args = { "--cached", NULL },
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -134,10 +134,10 @@ static struct patch_mode patch_mode_checkout_index = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -157,10 +157,10 @@ static struct patch_mode patch_mode_checkout_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from index and worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -179,10 +179,10 @@ static struct patch_mode patch_mode_checkout_nothead = {
 	.apply_for_checkout = 1,
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index and worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -202,10 +202,10 @@ static struct patch_mode patch_mode_worktree_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -224,10 +224,10 @@ static struct patch_mode patch_mode_worktree_nothead = {
 	.apply_args = { NULL },
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -1448,6 +1448,7 @@ static int patch_update_file(struct add_p_state *s,
 	ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
 	struct hunk *hunk;
 	char ch;
+	const char *hunk_use_decision;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	int colored = !!s->colored.len, quit = 0, use_pager = 0;
 	enum prompt_mode_type prompt_mode_type;
@@ -1564,8 +1565,17 @@ static int patch_update_file(struct add_p_state *s,
 			      (uintmax_t)(file_diff->hunk_nr
 						? file_diff->hunk_nr
 						: 1));
+		if (file_diff->hunk_nr) {
+			if (hunk->use == USE_HUNK)
+				hunk_use_decision = _(" (previous decision: use) ");
+			else if (hunk->use == SKIP_HUNK)
+				hunk_use_decision = _(" (previous decision: skip) ");
+			else
+				hunk_use_decision = " ";
+
+		}
 		printf(_(s->mode->prompt_mode[prompt_mode_type]),
-		       s->buf.buf);
+			hunk_use_decision, s->buf.buf);
 		if (*s->s.reset_color_interactive)
 			fputs(s->s.reset_color_interactive, stdout);
 		fflush(stdout);
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 4285314f35..e856683496 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -527,7 +527,7 @@ test_expect_success 'goto hunk 1 with "g 1"' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g 1 | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g1 | git add -p >actual &&
 	tail -n 4 <actual >actual.trimmed &&
@@ -554,7 +554,7 @@ test_expect_success 'navigate to hunk via regex /pattern' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y /1,2 | git add -p >actual &&
 	tail -n 5 <actual >actual.trimmed &&
@@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y / 1,2 | git add -p >actual &&
 	tail -n 4 <actual >actual.trimmed &&
@@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
 	tr _ " " >expect <<-EOF &&
 	+15
 	 20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
 	 10
 	+15
 	 20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g 1 p | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -810,7 +810,7 @@ test_expect_success 'colors can be overridden' '
 	<BOLD>-old<RESET>
 	<BLUE>+new<RESET>
 	<CYAN> more-context<RESET>
-	<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
+	<YELLOW>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
 	EOF
 	test_cmp expect actual
 '
-- 
2.39.5 (Apple Git-154)
Junio C Hamano· Jan 4, 2026, 06:03 UTC · re: Abraham Samuel Adekunle · lore

Re: [GSoC PATCH v3] add -p: show user's hunk decision when selecting hunks

Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:
Show 17 quoted lines
> 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>
> ---
>
> Changes in v2:
> --------------
> - Modified t3701-add-interactive.sh for conform to new changes
Here is how "cd t && sh t3701-add-interactive.sh -i -v" ends for me.
expecting success of 3701.50 'print again the hunk (PAGER)':
        test_when_finished "git reset" &&
        cat >expect <<-EOF &&
        <GREEN>+<RESET><GREEN>15<RESET>
         20<RESET>
        <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
        PAGER  10<RESET>
        PAGER <GREEN>+<RESET><GREEN>15<RESET>
        PAGER  20<RESET>
        <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
        EOF
        test_write_lines s y g 1 P |
        (
                GIT_PAGER="sed s/^/PAGER\ /" &&
                export GIT_PAGER &&
                test_terminal git add -p >actual
        ) &&
        tail -n 7 <actual | test_decode_color >actual.trimmed &&
        test_cmp expect actual.trimmed

--- expect 2026-01-04 06:01:25.931220332 +0000 +++ actual.trimmed 2026-01-04 06:01:26.079845771 +0000

Show changes to diff +2 −2
@@ -1,7 +1,7 @@
 <GREEN>+<RESET><GREEN>15<RESET>
  20<RESET>
-<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>
+<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
 PAGER  10<RESET>
 PAGER <GREEN>+<RESET><GREEN>15<RESET>
 PAGER  20<RESET>
-<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
+<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
not ok 50 - print again the hunk (PAGER)
#
#               test_when_finished "git reset" &&
#               cat >expect <<-EOF &&
#               <GREEN>+<RESET><GREEN>15<RESET>
#                20<RESET>
#               <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
#               PAGER  10<RESET>
#               PAGER <GREEN>+<RESET><GREEN>15<RESET>
#               PAGER  20<RESET>
#               <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
#               EOF
#               test_write_lines s y g 1 P |
#               (
#                       GIT_PAGER="sed s/^/PAGER\ /" &&
#                       export GIT_PAGER &&
#                       test_terminal git add -p >actual
#               ) &&
#               tail -n 7 <actual | test_decode_color >actual.trimmed &&
#               test_cmp expect actual.trimmed
#
1..50
Samuel Abraham· Jan 4, 2026, 10:36 UTC · re: Junio C Hamano · lore

Re: [GSoC PATCH v3] add -p: show user's hunk decision when selecting hunks

On Sun, Jan 4, 2026 at 7:03 AM Junio C Hamano <gitster@pobox.com> wrote:
Show 77 quoted lines
>
> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:
>
> > 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>
> > ---
> >
> > Changes in v2:
> > --------------
> > - Modified t3701-add-interactive.sh for conform to new changes
>
> Here is how "cd t && sh t3701-add-interactive.sh -i -v" ends for me.
>
> expecting success of 3701.50 'print again the hunk (PAGER)':
>         test_when_finished "git reset" &&
>         cat >expect <<-EOF &&
>         <GREEN>+<RESET><GREEN>15<RESET>
>          20<RESET>
>         <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
>         PAGER  10<RESET>
>         PAGER <GREEN>+<RESET><GREEN>15<RESET>
>         PAGER  20<RESET>
>         <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
>         EOF
>         test_write_lines s y g 1 P |
>         (
>                 GIT_PAGER="sed s/^/PAGER\ /" &&
>                 export GIT_PAGER &&
>                 test_terminal git add -p >actual
>         ) &&
>         tail -n 7 <actual | test_decode_color >actual.trimmed &&
>         test_cmp expect actual.trimmed
>
> --- expect      2026-01-04 06:01:25.931220332 +0000
> +++ actual.trimmed      2026-01-04 06:01:26.079845771 +0000
> @@ -1,7 +1,7 @@
>  <GREEN>+<RESET><GREEN>15<RESET>
>   20<RESET>
> -<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>
> +<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
>  PAGER  10<RESET>
>  PAGER <GREEN>+<RESET><GREEN>15<RESET>
>  PAGER  20<RESET>
> -<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
> +<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
> not ok 50 - print again the hunk (PAGER)
> #
> #               test_when_finished "git reset" &&
> #               cat >expect <<-EOF &&
> #               <GREEN>+<RESET><GREEN>15<RESET>
> #                20<RESET>
> #               <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
> #               PAGER  10<RESET>
> #               PAGER <GREEN>+<RESET><GREEN>15<RESET>
> #               PAGER  20<RESET>
> #               <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
> #               EOF
> #               test_write_lines s y g 1 P |
> #               (
> #                       GIT_PAGER="sed s/^/PAGER\ /" &&
> #                       export GIT_PAGER &&
> #                       test_terminal git add -p >actual
> #               ) &&
> #               tail -n 7 <actual | test_decode_color >actual.trimmed &&
> #               test_cmp expect actual.trimmed
> #
> 1..50

Thank you Junio for the reviews. I get the output when I run "cd t && ./t3701-add-interactive.sh -i -v"

[...] ok 49 - print again the hunk checking prerequisite: TTY mkdir -p "$TRASH_DIRECTORY/prereq-test-dir-TTY" && ( cd "$TRASH_DIRECTORY/prereq-test-dir-TTY" && test_have_prereq PERL && # Reading from the pty master seems to get stuck _sometimes_ # on Mac OS X 10.5.0, using Perl 5.10.0 or 5.8.9. # # Reproduction recipe: run # # i=0 # while ./test-terminal.perl echo hi $i # do # : $((i = $i + 1)) # done # # After 2000 iterations or so it hangs. # https://rt.cpan.org/Ticket/Display.html?id=65692 # test "$(uname -s)" != Darwin && perl "$TEST_DIRECTORY"/test-terminal.perl \ sh -c "test -t 1 && test -t 2" ) prerequisite TTY not satisfied ok 50 # skip print again the hunk (PAGER) (missing TTY) ok 51 # skip P handles SIGPIPE when writing to pager (missing TTY)

So that test skipped on my end because of this `test "$(uname -s)" != Darwin &&` since I am working on a MacOS.

I will correct it and send an updated version. Thanks

Abraham
Abraham Samuel Adekunle· Jan 4, 2026, 11:02 UTC · re: Abraham Samuel Adekunle · lore

[GSoC PATCH v4] 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>
---
Changes in v4:
--------------
- Modified the test case
  "expecting success of 3701.50 'print again the hunk (PAGER)'"
  which was skipped in v3 due to the test being run on a MacOs
 add-patch.c                | 84 +++++++++++++++++++++-----------------
 t/t3701-add-interactive.sh | 18 ++++----
 2 files changed, 56 insertions(+), 46 deletions(-)
Show changes to 2 files +56 −46

add-patch.c, t/t3701-add-interactive.sh

diff --git a/add-patch.c b/add-patch.c
index 173a53241e..bc7a340d21 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -42,10 +42,10 @@ static struct patch_mode patch_mode_add = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stage mode change [y,n,q,a,d%s,?]? "),
-		N_("Stage deletion [y,n,q,a,d%s,?]? "),
-		N_("Stage addition [y,n,q,a,d%s,?]? "),
-		N_("Stage this hunk [y,n,q,a,d%s,?]? ")
+		N_("Stage mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Stage deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Stage addition%s[y,n,q,a,d%s,?]? "),
+		N_("Stage this hunk%s[y,n,q,a,d%s,?]? ")
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for staging."),
@@ -64,10 +64,10 @@ static struct patch_mode patch_mode_stash = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stash mode change [y,n,q,a,d%s,?]? "),
-		N_("Stash deletion [y,n,q,a,d%s,?]? "),
-		N_("Stash addition [y,n,q,a,d%s,?]? "),
-		N_("Stash this hunk [y,n,q,a,d%s,?]? "),
+		N_("Stash mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Stash deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Stash addition%s[y,n,q,a,d%s,?]? "),
+		N_("Stash this hunk%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for stashing."),
@@ -88,10 +88,10 @@ static struct patch_mode patch_mode_reset_head = {
 	.is_reverse = 1,
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Unstage mode change [y,n,q,a,d%s,?]? "),
-		N_("Unstage deletion [y,n,q,a,d%s,?]? "),
-		N_("Unstage addition [y,n,q,a,d%s,?]? "),
-		N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
+		N_("Unstage mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage addition%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage this hunk%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for unstaging."),
@@ -111,10 +111,10 @@ static struct patch_mode patch_mode_reset_nothead = {
 	.apply_check_args = { "--cached", NULL },
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -134,10 +134,10 @@ static struct patch_mode patch_mode_checkout_index = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -157,10 +157,10 @@ static struct patch_mode patch_mode_checkout_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from index and worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -179,10 +179,10 @@ static struct patch_mode patch_mode_checkout_nothead = {
 	.apply_for_checkout = 1,
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index and worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -202,10 +202,10 @@ static struct patch_mode patch_mode_worktree_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -224,10 +224,10 @@ static struct patch_mode patch_mode_worktree_nothead = {
 	.apply_args = { NULL },
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -1448,6 +1448,7 @@ static int patch_update_file(struct add_p_state *s,
 	ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
 	struct hunk *hunk;
 	char ch;
+	const char *hunk_use_decision;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	int colored = !!s->colored.len, quit = 0, use_pager = 0;
 	enum prompt_mode_type prompt_mode_type;
@@ -1564,8 +1565,17 @@ static int patch_update_file(struct add_p_state *s,
 			      (uintmax_t)(file_diff->hunk_nr
 						? file_diff->hunk_nr
 						: 1));
+		if (file_diff->hunk_nr) {
+			if (hunk->use == USE_HUNK)
+				hunk_use_decision = _(" (previous decision: use) ");
+			else if (hunk->use == SKIP_HUNK)
+				hunk_use_decision = _(" (previous decision: skip) ");
+			else
+				hunk_use_decision = " ";
+
+		}
 		printf(_(s->mode->prompt_mode[prompt_mode_type]),
-		       s->buf.buf);
+			hunk_use_decision, s->buf.buf);
 		if (*s->s.reset_color_interactive)
 			fputs(s->s.reset_color_interactive, stdout);
 		fflush(stdout);
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 4285314f35..cc3986a9d7 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -527,7 +527,7 @@ test_expect_success 'goto hunk 1 with "g 1"' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g 1 | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g1 | git add -p >actual &&
 	tail -n 4 <actual >actual.trimmed &&
@@ -554,7 +554,7 @@ test_expect_success 'navigate to hunk via regex /pattern' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y /1,2 | git add -p >actual &&
 	tail -n 5 <actual >actual.trimmed &&
@@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y / 1,2 | git add -p >actual &&
 	tail -n 4 <actual >actual.trimmed &&
@@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
 	tr _ " " >expect <<-EOF &&
 	+15
 	 20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
 	 10
 	+15
 	 20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g 1 p | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -595,11 +595,11 @@ test_expect_success TTY 'print again the hunk (PAGER)' '
 	cat >expect <<-EOF &&
 	<GREEN>+<RESET><GREEN>15<RESET>
 	 20<RESET>
-	<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>
+	<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
 	PAGER  10<RESET>
 	PAGER <GREEN>+<RESET><GREEN>15<RESET>
 	PAGER  20<RESET>
-	<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
+	<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
 	EOF
 	test_write_lines s y g 1 P |
 	(
@@ -810,7 +810,7 @@ test_expect_success 'colors can be overridden' '
 	<BOLD>-old<RESET>
 	<BLUE>+new<RESET>
 	<CYAN> more-context<RESET>
-	<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
+	<YELLOW>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
 	EOF
 	test_cmp expect actual
 '
-- 
2.39.5 (Apple Git-154)
SZEDER Gábor· Jan 5, 2026, 19:35 UTC · re: Abraham Samuel Adekunle · lore

Re: [GSoC PATCH v4] add -p: show user's hunk decision when selecting hunks

On Sun, Jan 04, 2026 at 12:02:55PM +0100, Abraham Samuel Adekunle wrote:
> diff --git a/add-patch.c b/add-patch.c
> index 173a53241e..bc7a340d21 100644
> --- a/add-patch.c
> +++ b/add-patch.c
Show 5 quoted lines
> @@ -1448,6 +1448,7 @@ static int patch_update_file(struct add_p_state *s,
>  	ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
>  	struct hunk *hunk;
>  	char ch;
> +	const char *hunk_use_decision;
'hunk_use_decision' is defined here, but is left uninitialized.
Show 14 quoted lines
>  	struct child_process cp = CHILD_PROCESS_INIT;
>  	int colored = !!s->colored.len, quit = 0, use_pager = 0;
>  	enum prompt_mode_type prompt_mode_type;
> @@ -1564,8 +1565,17 @@ static int patch_update_file(struct add_p_state *s,
>  			      (uintmax_t)(file_diff->hunk_nr
>  						? file_diff->hunk_nr
>  						: 1));
> +		if (file_diff->hunk_nr) {
> +			if (hunk->use == USE_HUNK)
> +				hunk_use_decision = _(" (previous decision: use) ");
> +			else if (hunk->use == SKIP_HUNK)
> +				hunk_use_decision = _(" (previous decision: skip) ");
> +			else
> +				hunk_use_decision = " ";

'hunk_use_decision' is set here, but only if 'file_diff->hunk_nr' is non-zero.

> +`
> +		}
Nit: unnecessary empty line before the closing brace.
>  		printf(_(s->mode->prompt_mode[prompt_mode_type]),
> -		       s->buf.buf);
> +			hunk_use_decision, s->buf.buf);

So 'hunk_use_decision' is used uninitialized here if 'file_diff->hunk_nr' is zero.

>  		if (*s->s.reset_color_interactive)
>  			fputs(s->s.reset_color_interactive, stdout);
>  		fflush(stdout);
Samuel Abraham· Jan 5, 2026, 21:52 UTC · re: SZEDER Gábor · lore

Re: [GSoC PATCH v4] add -p: show user's hunk decision when selecting hunks

On Mon, Jan 5, 2026 at 8:35 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:
Show 45 quoted lines
>
> On Sun, Jan 04, 2026 at 12:02:55PM +0100, Abraham Samuel Adekunle wrote:
> > diff --git a/add-patch.c b/add-patch.c
> > index 173a53241e..bc7a340d21 100644
> > --- a/add-patch.c
> > +++ b/add-patch.c
>
> > @@ -1448,6 +1448,7 @@ static int patch_update_file(struct add_p_state *s,
> >       ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
> >       struct hunk *hunk;
> >       char ch;
> > +     const char *hunk_use_decision;
>
> 'hunk_use_decision' is defined here, but is left uninitialized.
>
> >       struct child_process cp = CHILD_PROCESS_INIT;
> >       int colored = !!s->colored.len, quit = 0, use_pager = 0;
> >       enum prompt_mode_type prompt_mode_type;
> > @@ -1564,8 +1565,17 @@ static int patch_update_file(struct add_p_state *s,
> >                             (uintmax_t)(file_diff->hunk_nr
> >                                               ? file_diff->hunk_nr
> >                                               : 1));
> > +             if (file_diff->hunk_nr) {
> > +                     if (hunk->use == USE_HUNK)
> > +                             hunk_use_decision = _(" (previous decision: use) ");
> > +                     else if (hunk->use == SKIP_HUNK)
> > +                             hunk_use_decision = _(" (previous decision: skip) ");
> > +                     else
> > +                             hunk_use_decision = " ";
>
> 'hunk_use_decision' is set here, but only if 'file_diff->hunk_nr' is
> non-zero.
>
> > +`
> > +             }
>
> Nit: unnecessary empty line before the closing brace.
>
> >               printf(_(s->mode->prompt_mode[prompt_mode_type]),
> > -                    s->buf.buf);
> > +                     hunk_use_decision, s->buf.buf);
>
> So 'hunk_use_decision' is used uninitialized here if
> 'file_diff->hunk_nr' is zero.
>

hello SZEDER, Thank you very much for the review Yes, you are right, `hunk_use_decision` is left uninitialized if `file_diff->hunk` is zero I will fix the patch and send an updated version.

Abraham.
Abraham Samuel Adekunle· Jan 6, 2026, 11:05 UTC · re: Abraham Samuel Adekunle · lore

[GSoC PATCH v5] 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: Reported-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>
---
Changes in v5:
===============
- Modified code to handle uninitialized variable `hunk_use_decision`
  observed by SZEDER Gábor.
- Removed an unnecessary empty line
 add-patch.c                | 81 +++++++++++++++++++++-----------------
 t/t3701-add-interactive.sh | 18 ++++-----
 2 files changed, 53 insertions(+), 46 deletions(-)
Show changes to 2 files +53 −46

add-patch.c, t/t3701-add-interactive.sh

diff --git a/add-patch.c b/add-patch.c
index 173a53241e..a383ea7f45 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -42,10 +42,10 @@ static struct patch_mode patch_mode_add = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stage mode change [y,n,q,a,d%s,?]? "),
-		N_("Stage deletion [y,n,q,a,d%s,?]? "),
-		N_("Stage addition [y,n,q,a,d%s,?]? "),
-		N_("Stage this hunk [y,n,q,a,d%s,?]? ")
+		N_("Stage mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Stage deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Stage addition%s[y,n,q,a,d%s,?]? "),
+		N_("Stage this hunk%s[y,n,q,a,d%s,?]? ")
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for staging."),
@@ -64,10 +64,10 @@ static struct patch_mode patch_mode_stash = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stash mode change [y,n,q,a,d%s,?]? "),
-		N_("Stash deletion [y,n,q,a,d%s,?]? "),
-		N_("Stash addition [y,n,q,a,d%s,?]? "),
-		N_("Stash this hunk [y,n,q,a,d%s,?]? "),
+		N_("Stash mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Stash deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Stash addition%s[y,n,q,a,d%s,?]? "),
+		N_("Stash this hunk%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for stashing."),
@@ -88,10 +88,10 @@ static struct patch_mode patch_mode_reset_head = {
 	.is_reverse = 1,
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Unstage mode change [y,n,q,a,d%s,?]? "),
-		N_("Unstage deletion [y,n,q,a,d%s,?]? "),
-		N_("Unstage addition [y,n,q,a,d%s,?]? "),
-		N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
+		N_("Unstage mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage addition%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage this hunk%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for unstaging."),
@@ -111,10 +111,10 @@ static struct patch_mode patch_mode_reset_nothead = {
 	.apply_check_args = { "--cached", NULL },
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -134,10 +134,10 @@ static struct patch_mode patch_mode_checkout_index = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -157,10 +157,10 @@ static struct patch_mode patch_mode_checkout_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from index and worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -179,10 +179,10 @@ static struct patch_mode patch_mode_checkout_nothead = {
 	.apply_for_checkout = 1,
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index and worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -202,10 +202,10 @@ static struct patch_mode patch_mode_worktree_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -224,10 +224,10 @@ static struct patch_mode patch_mode_worktree_nothead = {
 	.apply_args = { NULL },
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -1460,6 +1460,7 @@ static int patch_update_file(struct add_p_state *s,
 	render_diff_header(s, file_diff, colored, &s->buf);
 	fputs(s->buf.buf, stdout);
 	for (;;) {
+		const char *hunk_use_decision = " ";
 		enum {
 			ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
 			ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
@@ -1564,8 +1565,14 @@ static int patch_update_file(struct add_p_state *s,
 			      (uintmax_t)(file_diff->hunk_nr
 						? file_diff->hunk_nr
 						: 1));
+		if (file_diff->hunk_nr && hunk->use != UNDECIDED_HUNK) {
+			if (hunk->use == USE_HUNK)
+				hunk_use_decision = _(" (previous decision: use) ");
+			else
+				hunk_use_decision = _(" (previous decision: skip) ");
+		}
 		printf(_(s->mode->prompt_mode[prompt_mode_type]),
-		       s->buf.buf);
+			hunk_use_decision, s->buf.buf);
 		if (*s->s.reset_color_interactive)
 			fputs(s->s.reset_color_interactive, stdout);
 		fflush(stdout);
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 4285314f35..cc3986a9d7 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -527,7 +527,7 @@ test_expect_success 'goto hunk 1 with "g 1"' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g 1 | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g1 | git add -p >actual &&
 	tail -n 4 <actual >actual.trimmed &&
@@ -554,7 +554,7 @@ test_expect_success 'navigate to hunk via regex /pattern' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y /1,2 | git add -p >actual &&
 	tail -n 5 <actual >actual.trimmed &&
@@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y / 1,2 | git add -p >actual &&
 	tail -n 4 <actual >actual.trimmed &&
@@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
 	tr _ " " >expect <<-EOF &&
 	+15
 	 20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
 	 10
 	+15
 	 20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g 1 p | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -595,11 +595,11 @@ test_expect_success TTY 'print again the hunk (PAGER)' '
 	cat >expect <<-EOF &&
 	<GREEN>+<RESET><GREEN>15<RESET>
 	 20<RESET>
-	<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>
+	<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
 	PAGER  10<RESET>
 	PAGER <GREEN>+<RESET><GREEN>15<RESET>
 	PAGER  20<RESET>
-	<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
+	<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
 	EOF
 	test_write_lines s y g 1 P |
 	(
@@ -810,7 +810,7 @@ test_expect_success 'colors can be overridden' '
 	<BOLD>-old<RESET>
 	<BLUE>+new<RESET>
 	<CYAN> more-context<RESET>
-	<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
+	<YELLOW>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
 	EOF
 	test_cmp expect actual
 '
-- 
2.39.5 (Apple Git-154)
Kristoffer Haugsbakk· Jan 6, 2026, 11:08 UTC · re: Abraham Samuel Adekunle · lore

Re: [GSoC PATCH v5] add -p: show user's hunk decision when selecting hunks

On Tue, Jan 6, 2026, at 12:05, Abraham Samuel Adekunle wrote:
Show 11 quoted lines
> 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: Reported-by: Junio C Hamano <gitster@pobox.com>
This is doubled again like it was in the first version.
> Signed-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>
>[snip]
Samuel Abraham· Jan 6, 2026, 11:46 UTC · re: Kristoffer Haugsbakk · lore

Re: [GSoC PATCH v5] add -p: show user's hunk decision when selecting hunks

On Tue, Jan 6, 2026 at 12:09 PM Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com> wrote:

Show 15 quoted lines
>
> On Tue, Jan 6, 2026, at 12:05, Abraham Samuel Adekunle wrote:
> > 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: Reported-by: Junio C Hamano <gitster@pobox.com>
>
> This is doubled again like it was in the first version.

Ah!! I'm sorry about this I will fix it now.

Thanks Abraham

Abraham Samuel Adekunle· Jan 6, 2026, 12:01 UTC · re: Abraham Samuel Adekunle · lore

[GSoC PATCH v6] 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 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>
---
Changes in v6:
==============
- Corrected duplicate Reported-by tag
 add-patch.c                | 81 +++++++++++++++++++++-----------------
 t/t3701-add-interactive.sh | 18 ++++-----
 2 files changed, 53 insertions(+), 46 deletions(-)
Show changes to 2 files +53 −46

add-patch.c, t/t3701-add-interactive.sh

diff --git a/add-patch.c b/add-patch.c
index 173a53241e..a383ea7f45 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -42,10 +42,10 @@ static struct patch_mode patch_mode_add = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stage mode change [y,n,q,a,d%s,?]? "),
-		N_("Stage deletion [y,n,q,a,d%s,?]? "),
-		N_("Stage addition [y,n,q,a,d%s,?]? "),
-		N_("Stage this hunk [y,n,q,a,d%s,?]? ")
+		N_("Stage mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Stage deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Stage addition%s[y,n,q,a,d%s,?]? "),
+		N_("Stage this hunk%s[y,n,q,a,d%s,?]? ")
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for staging."),
@@ -64,10 +64,10 @@ static struct patch_mode patch_mode_stash = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stash mode change [y,n,q,a,d%s,?]? "),
-		N_("Stash deletion [y,n,q,a,d%s,?]? "),
-		N_("Stash addition [y,n,q,a,d%s,?]? "),
-		N_("Stash this hunk [y,n,q,a,d%s,?]? "),
+		N_("Stash mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Stash deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Stash addition%s[y,n,q,a,d%s,?]? "),
+		N_("Stash this hunk%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for stashing."),
@@ -88,10 +88,10 @@ static struct patch_mode patch_mode_reset_head = {
 	.is_reverse = 1,
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Unstage mode change [y,n,q,a,d%s,?]? "),
-		N_("Unstage deletion [y,n,q,a,d%s,?]? "),
-		N_("Unstage addition [y,n,q,a,d%s,?]? "),
-		N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
+		N_("Unstage mode change%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage deletion%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage addition%s[y,n,q,a,d%s,?]? "),
+		N_("Unstage this hunk%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for unstaging."),
@@ -111,10 +111,10 @@ static struct patch_mode patch_mode_reset_nothead = {
 	.apply_check_args = { "--cached", NULL },
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -134,10 +134,10 @@ static struct patch_mode patch_mode_checkout_index = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -157,10 +157,10 @@ static struct patch_mode patch_mode_checkout_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from index and worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -179,10 +179,10 @@ static struct patch_mode patch_mode_checkout_nothead = {
 	.apply_for_checkout = 1,
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index and worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index and worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -202,10 +202,10 @@ static struct patch_mode patch_mode_worktree_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -224,10 +224,10 @@ static struct patch_mode patch_mode_worktree_nothead = {
 	.apply_args = { NULL },
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply addition to worktree%s[y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to worktree%s[y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -1460,6 +1460,7 @@ static int patch_update_file(struct add_p_state *s,
 	render_diff_header(s, file_diff, colored, &s->buf);
 	fputs(s->buf.buf, stdout);
 	for (;;) {
+		const char *hunk_use_decision = " ";
 		enum {
 			ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
 			ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
@@ -1564,8 +1565,14 @@ static int patch_update_file(struct add_p_state *s,
 			      (uintmax_t)(file_diff->hunk_nr
 						? file_diff->hunk_nr
 						: 1));
+		if (file_diff->hunk_nr && hunk->use != UNDECIDED_HUNK) {
+			if (hunk->use == USE_HUNK)
+				hunk_use_decision = _(" (previous decision: use) ");
+			else
+				hunk_use_decision = _(" (previous decision: skip) ");
+		}
 		printf(_(s->mode->prompt_mode[prompt_mode_type]),
-		       s->buf.buf);
+			hunk_use_decision, s->buf.buf);
 		if (*s->s.reset_color_interactive)
 			fputs(s->s.reset_color_interactive, stdout);
 		fflush(stdout);
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 4285314f35..cc3986a9d7 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -527,7 +527,7 @@ test_expect_success 'goto hunk 1 with "g 1"' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g 1 | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g1 | git add -p >actual &&
 	tail -n 4 <actual >actual.trimmed &&
@@ -554,7 +554,7 @@ test_expect_success 'navigate to hunk via regex /pattern' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y /1,2 | git add -p >actual &&
 	tail -n 5 <actual >actual.trimmed &&
@@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y / 1,2 | git add -p >actual &&
 	tail -n 4 <actual >actual.trimmed &&
@@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
 	tr _ " " >expect <<-EOF &&
 	+15
 	 20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
 	 10
 	+15
 	 20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g 1 p | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -595,11 +595,11 @@ test_expect_success TTY 'print again the hunk (PAGER)' '
 	cat >expect <<-EOF &&
 	<GREEN>+<RESET><GREEN>15<RESET>
 	 20<RESET>
-	<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>
+	<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
 	PAGER  10<RESET>
 	PAGER <GREEN>+<RESET><GREEN>15<RESET>
 	PAGER  20<RESET>
-	<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
+	<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
 	EOF
 	test_write_lines s y g 1 P |
 	(
@@ -810,7 +810,7 @@ test_expect_success 'colors can be overridden' '
 	<BOLD>-old<RESET>
 	<BLUE>+new<RESET>
 	<CYAN> more-context<RESET>
-	<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
+	<YELLOW>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
 	EOF
 	test_cmp expect actual
 '
-- 
2.39.5 (Apple Git-154)
Phillip Wood· Jan 6, 2026, 16:10 UTC · re: Abraham Samuel Adekunle · lore

Re: [GSoC PATCH v6] add -p: show user's hunk decision when selecting hunks

Hi Abraham
On 06/01/2026 12:01, Abraham Samuel Adekunle wrote:
Show 9 quoted lines
> 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 and aid the navigation process for the
> user.

I like the idea of telling the user if the hunk is currently selected but say "(previous decision: use)" makes the prompt rather long (some of the prompts in the tests below are 80 characters long). I wonder if we can find a more compact notation. "(currently selected)" is a bit shorter and takes us under 80 characters but is still longer than I'd like - maybe someone reading this will have a better suggestion.

Show 16 quoted lines
> diff --git a/add-patch.c b/add-patch.c
> index 173a53241e..a383ea7f45 100644
> --- a/add-patch.c
> +++ b/add-patch.c
> @@ -42,10 +42,10 @@ static struct patch_mode patch_mode_add = {
>   	.apply_args = { "--cached", NULL },
>   	.apply_check_args = { "--cached", NULL },
>   	.prompt_mode = {
> -		N_("Stage mode change [y,n,q,a,d%s,?]? "),
> -		N_("Stage deletion [y,n,q,a,d%s,?]? "),
> -		N_("Stage addition [y,n,q,a,d%s,?]? "),
> -		N_("Stage this hunk [y,n,q,a,d%s,?]? ")
> +		N_("Stage mode change%s[y,n,q,a,d%s,?]? "),
> +		N_("Stage deletion%s[y,n,q,a,d%s,?]? "),
> +		N_("Stage addition%s[y,n,q,a,d%s,?]? "),
> +		N_("Stage this hunk%s[y,n,q,a,d%s,?]? ")

I'd find these strings easier to read if we kept the space and just passed an empty string when the hunk is undecided.

Show 5 quoted lines
> @@ -1564,8 +1565,14 @@ static int patch_update_file(struct add_p_state *s,
>   			      (uintmax_t)(file_diff->hunk_nr
>   						? file_diff->hunk_nr
>   						: 1));
> +		if (file_diff->hunk_nr && hunk->use != UNDECIDED_HUNK) {
Why do we need to check hunk_nr here?
Thanks
Phillip
Show 88 quoted lines
> +			if (hunk->use == USE_HUNK)
> +				hunk_use_decision = _(" (previous decision: use) ");
> +			else
> +				hunk_use_decision = _(" (previous decision: skip) ");
> +		}
>   		printf(_(s->mode->prompt_mode[prompt_mode_type]),
> -		       s->buf.buf);
> +			hunk_use_decision, s->buf.buf);
>   		if (*s->s.reset_color_interactive)
>   			fputs(s->s.reset_color_interactive, stdout);
>   		fflush(stdout);
> diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
> index 4285314f35..cc3986a9d7 100755
> --- a/t/t3701-add-interactive.sh
> +++ b/t/t3701-add-interactive.sh
> @@ -527,7 +527,7 @@ test_expect_success 'goto hunk 1 with "g 1"' '
>   	_10
>   	+15
>   	_20
> -	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
> +	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
>   	EOF
>   	test_write_lines s y g 1 | git add -p >actual &&
>   	tail -n 7 <actual >actual.trimmed &&
> @@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
>   	_10
>   	+15
>   	_20
> -	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
> +	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
>   	EOF
>   	test_write_lines s y g1 | git add -p >actual &&
>   	tail -n 4 <actual >actual.trimmed &&
> @@ -554,7 +554,7 @@ test_expect_success 'navigate to hunk via regex /pattern' '
>   	_10
>   	+15
>   	_20
> -	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
> +	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
>   	EOF
>   	test_write_lines s y /1,2 | git add -p >actual &&
>   	tail -n 5 <actual >actual.trimmed &&
> @@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
>   	_10
>   	+15
>   	_20
> -	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
> +	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
>   	EOF
>   	test_write_lines s y / 1,2 | git add -p >actual &&
>   	tail -n 4 <actual >actual.trimmed &&
> @@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
>   	tr _ " " >expect <<-EOF &&
>   	+15
>   	 20
> -	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
> +	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
>   	 10
>   	+15
>   	 20
> -	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
> +	(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
>   	EOF
>   	test_write_lines s y g 1 p | git add -p >actual &&
>   	tail -n 7 <actual >actual.trimmed &&
> @@ -595,11 +595,11 @@ test_expect_success TTY 'print again the hunk (PAGER)' '
>   	cat >expect <<-EOF &&
>   	<GREEN>+<RESET><GREEN>15<RESET>
>   	 20<RESET>
> -	<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>
> +	<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET>
>   	PAGER  10<RESET>
>   	PAGER <GREEN>+<RESET><GREEN>15<RESET>
>   	PAGER  20<RESET>
> -	<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
> +	<BOLD;BLUE>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
>   	EOF
>   	test_write_lines s y g 1 P |
>   	(
> @@ -810,7 +810,7 @@ test_expect_success 'colors can be overridden' '
>   	<BOLD>-old<RESET>
>   	<BLUE>+new<RESET>
>   	<CYAN> more-context<RESET>
> -	<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
> +	<YELLOW>(1/2) Stage this hunk (previous decision: use) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
>   	EOF
>   	test_cmp expect actual
>   '
Ben Knoble· Jan 6, 2026, 19:01 UTC · re: Phillip Wood · lore

Re: [GSoC PATCH v6] add -p: show user's hunk decision when selecting hunks

Show 15 quoted lines
> Le 6 janv. 2026 à 11:13, Phillip Wood <phillip.wood123@gmail.com> a écrit :
> 
> Hi Abraham
> 
>> On 06/01/2026 12:01, Abraham Samuel Adekunle wrote:
>> 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 and aid the navigation process for the
>> user.
> 
> I like the idea of telling the user if the hunk is currently selected but say "(previous decision: use)" makes the prompt rather long (some of the prompts in the tests below are 80 characters long). I wonder if we can find a more compact notation. "(currently selected)" is a bit shorter and takes us under 80 characters but is still longer than I'd like - maybe someone reading this will have a better suggestion.
I haven’t looked carefully, so apologies if this is nonsense.
What if the marker of current state were on a separate line before the prompt? That would allow more room, right?
Junio C Hamano· Jan 7, 2026, 00:13 UTC · re: Ben Knoble · lore

Re: [GSoC PATCH v6] add -p: show user's hunk decision when selecting hunks

Ben Knoble <ben.knoble@gmail.com> writes:
Show 12 quoted lines
>> I like the idea of telling the user if the hunk is currently
>> selected but say "(previous decision: use)" makes the prompt
>> rather long (some of the prompts in the tests below are 80
>> characters long). I wonder if we can find a more compact
>> notation. "(currently selected)" is a bit shorter and takes us
>> under 80 characters but is still longer than I'd like - maybe
>> someone reading this will have a better suggestion.
>
> I haven’t looked carefully, so apologies if this is nonsense.
>
> What if the marker of current state were on a separate line before
> the prompt? That would allow more room, right?

Vertical screen real estate is also a limited resource. Also, while it is good if we let users know what the current selection status is, I personally do not think the benefit outweighs the downside of cluttering the prompt unless it is kept to the minimum. So I very much appreciate that Phillip raised this point.

Thanks.
Samuel Abraham· Jan 6, 2026, 22:02 UTC · re: Phillip Wood · lore

Re: [GSoC PATCH v6] add -p: show user's hunk decision when selecting hunks

On Tue, Jan 6, 2026 at 5:10 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Abraham
Hello Phillip,
Show 18 quoted lines
>
> On 06/01/2026 12:01, Abraham Samuel Adekunle wrote:
> > 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 and aid the navigation process for the
> > user.
>
> I like the idea of telling the user if the hunk is currently selected
> but say "(previous decision: use)" makes the prompt rather long (some of
> the prompts in the tests below are 80 characters long). I wonder if we
> can find a more compact notation. "(currently selected)" is a bit
> shorter and takes us under 80 characters but is still longer than I'd
> like - maybe someone reading this will have a better suggestion.

Thank you for the review So I previously used selected/deselected. But Junio was not okay with those choice of words because they did not clearly tell If the user selected to skip or or selected to use the hunk.

But how about

Stage this mode change (you chose: use) [y,n,q,a,d%s,?]? Stage this mode change (you chose: skip) [y,n,q,a,d%s,?]? Stage this deletion (you chose: use) [y,n,q,a,d%sm,?]?

or Stage this mode change (choice: use) [y,n,q,a,d%s,?]? Stage this mode change (choice: skip)[y,n,q,a,d%s,?]? Stage this deletion (choice: skip)" [y,n,q,a,d%sm,?]?

or Stage this mode change (use: yes) [y,n,q,a,d%s,?]? Stage this mode change (use: no) [y,n,q,a,d%s,?]? Stage this deletion (use: no) [y,n,q,a,d%sm,?]?

Though I feel the last one does not fully tell what is happening at a glance. I can wait for more suggestions from other members if these do not suffice.

Show 20 quoted lines
>
> > diff --git a/add-patch.c b/add-patch.c
> > index 173a53241e..a383ea7f45 100644
> > --- a/add-patch.c
> > +++ b/add-patch.c
> > @@ -42,10 +42,10 @@ static struct patch_mode patch_mode_add = {
> >       .apply_args = { "--cached", NULL },
> >       .apply_check_args = { "--cached", NULL },
> >       .prompt_mode = {
> > -             N_("Stage mode change [y,n,q,a,d%s,?]? "),
> > -             N_("Stage deletion [y,n,q,a,d%s,?]? "),
> > -             N_("Stage addition [y,n,q,a,d%s,?]? "),
> > -             N_("Stage this hunk [y,n,q,a,d%s,?]? ")
> > +             N_("Stage mode change%s[y,n,q,a,d%s,?]? "),
> > +             N_("Stage deletion%s[y,n,q,a,d%s,?]? "),
> > +             N_("Stage addition%s[y,n,q,a,d%s,?]? "),
> > +             N_("Stage this hunk%s[y,n,q,a,d%s,?]? ")
>
> I'd find these strings easier to read if we kept the space and just
> passed an empty string when the hunk is undecided.

Okay I understand. Thank you. I will do that

Show 8 quoted lines
>
> > @@ -1564,8 +1565,14 @@ static int patch_update_file(struct add_p_state *s,
> >                             (uintmax_t)(file_diff->hunk_nr
> >                                               ? file_diff->hunk_nr
> >                                               : 1));
> > +             if (file_diff->hunk_nr && hunk->use != UNDECIDED_HUNK) {
>
> Why do we need to check hunk_nr here?

Okay it is actually not necessary to check `hunk_nr` since `hunk` is set to `file_diff->head` if `file_diff->hunk_nr` is zero Thank you for the observation.

Abraham.
Samuel Abraham· Jan 6, 2026, 22:19 UTC · re: Samuel Abraham · lore

Re: [GSoC PATCH v6] add -p: show user's hunk decision when selecting hunks

On Tue, Jan 6, 2026 at 11:02 PM Samuel Abraham <abrahamadekunle50@gmail.com> wrote:

Show 48 quoted lines
>
> On Tue, Jan 6, 2026 at 5:10 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> >
> > Hi Abraham
>
> Hello Phillip,
>
> >
> > On 06/01/2026 12:01, Abraham Samuel Adekunle wrote:
> > > 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 and aid the navigation process for the
> > > user.
> >
> > I like the idea of telling the user if the hunk is currently selected
> > but say "(previous decision: use)" makes the prompt rather long (some of
> > the prompts in the tests below are 80 characters long). I wonder if we
> > can find a more compact notation. "(currently selected)" is a bit
> > shorter and takes us under 80 characters but is still longer than I'd
> > like - maybe someone reading this will have a better suggestion.
>
> Thank you for the review
> So I previously used selected/deselected. But Junio was not okay with those
> choice of words because they did not clearly tell If the user selected
> to skip or
> or selected to use the hunk.
>
> But how about
>
> Stage this mode change (you chose: use) [y,n,q,a,d%s,?]?
> Stage this mode change (you chose: skip) [y,n,q,a,d%s,?]?
> Stage this deletion (you chose: use) [y,n,q,a,d%sm,?]?
>
> or
> Stage this mode change (choice: use) [y,n,q,a,d%s,?]?
> Stage this mode change (choice: skip)[y,n,q,a,d%s,?]?
> Stage this deletion (choice: skip)" [y,n,q,a,d%sm,?]?
>
> or
> Stage this mode change (use: yes) [y,n,q,a,d%s,?]?
> Stage this mode change (use: no) [y,n,q,a,d%s,?]?
> Stage this deletion (use: no) [y,n,q,a,d%sm,?]?
>

Also "(you chose yes)" i think is an option also. Stage this mode change (you chose yes) [y,n,q,a,d%s,?]? Stage this mode change (you chose no) [y,n,q,a,d%s,?]? Stage this deletion (you chose no) [y,n,q,a,d%sm,?]?

Abraham.
Samuel Abraham· Jan 7, 2026, 08:49 UTC · re: Samuel Abraham · lore

Re: [GSoC PATCH v6] add -p: show user's hunk decision when selecting hunks

On Tue, Jan 6, 2026 at 11:19 PM Samuel Abraham <abrahamadekunle50@gmail.com> wrote:

Show 58 quoted lines
>
> On Tue, Jan 6, 2026 at 11:02 PM Samuel Abraham
> <abrahamadekunle50@gmail.com> wrote:
> >
> > On Tue, Jan 6, 2026 at 5:10 PM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > >
> > > Hi Abraham
> >
> > Hello Phillip,
> >
> > >
> > > On 06/01/2026 12:01, Abraham Samuel Adekunle wrote:
> > > > 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 and aid the navigation process for the
> > > > user.
> > >
> > > I like the idea of telling the user if the hunk is currently selected
> > > but say "(previous decision: use)" makes the prompt rather long (some of
> > > the prompts in the tests below are 80 characters long). I wonder if we
> > > can find a more compact notation. "(currently selected)" is a bit
> > > shorter and takes us under 80 characters but is still longer than I'd
> > > like - maybe someone reading this will have a better suggestion.
> >
> > Thank you for the review
> > So I previously used selected/deselected. But Junio was not okay with those
> > choice of words because they did not clearly tell If the user selected
> > to skip or
> > or selected to use the hunk.
> >
> > But how about
> >
> > Stage this mode change (you chose: use) [y,n,q,a,d%s,?]?
> > Stage this mode change (you chose: skip) [y,n,q,a,d%s,?]?
> > Stage this deletion (you chose: use) [y,n,q,a,d%sm,?]?
> >
> > or
> > Stage this mode change (choice: use) [y,n,q,a,d%s,?]?
> > Stage this mode change (choice: skip)[y,n,q,a,d%s,?]?
> > Stage this deletion (choice: skip)" [y,n,q,a,d%sm,?]?
> >
> > or
> > Stage this mode change (use: yes) [y,n,q,a,d%s,?]?
> > Stage this mode change (use: no) [y,n,q,a,d%s,?]?
> > Stage this deletion (use: no) [y,n,q,a,d%sm,?]?
> >
>
> Also "(you chose yes)" i think is an option also.
> Stage this mode change (you chose yes) [y,n,q,a,d%s,?]?
> Stage this mode change (you chose no) [y,n,q,a,d%s,?]?
> Stage this deletion (you chose no) [y,n,q,a,d%sm,?]?
>
> Abraham.

I also think "(was: y)" or "(was: n)" is considerably shorter Stage this mode change (was: y) [y,n,q,a,d%s,?]? Stage this mode change (was: n) [y,n,q,a,d%s,?]? Stage this deletion (was: y) [y,n,q,a,d%sm,?]?

Abraham Samuel Adekunle· Jan 8, 2026, 15:07 UTC · re: Abraham Samuel Adekunle · lore

[GSoC PATCH v7] 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>
---
Changes in v7:
==============
- changed hunk selection state message to reduce prompt width
- Removed redundant `file_diff->hunk_nr` in hunk->use check
- Made the prompt mode more readable by retaining the space before choices
- set `hunk_use_decision` to empty string if `hunk->use == UNDECIDED` to match
  the prompt mode.
 add-patch.c                | 81 +++++++++++++++++++++-----------------
 t/t3701-add-interactive.sh | 18 ++++-----
 2 files changed, 53 insertions(+), 46 deletions(-)
Show changes to 2 files +53 −46

add-patch.c, t/t3701-add-interactive.sh

diff --git a/add-patch.c b/add-patch.c
index 173a53241e..df8f2e6d74 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -42,10 +42,10 @@ static struct patch_mode patch_mode_add = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stage mode change [y,n,q,a,d%s,?]? "),
-		N_("Stage deletion [y,n,q,a,d%s,?]? "),
-		N_("Stage addition [y,n,q,a,d%s,?]? "),
-		N_("Stage this hunk [y,n,q,a,d%s,?]? ")
+		N_("Stage mode change%s [y,n,q,a,d%s,?]? "),
+		N_("Stage deletion%s [y,n,q,a,d%s,?]? "),
+		N_("Stage addition%s [y,n,q,a,d%s,?]? "),
+		N_("Stage this hunk%s [y,n,q,a,d%s,?]? ")
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for staging."),
@@ -64,10 +64,10 @@ static struct patch_mode patch_mode_stash = {
 	.apply_args = { "--cached", NULL },
 	.apply_check_args = { "--cached", NULL },
 	.prompt_mode = {
-		N_("Stash mode change [y,n,q,a,d%s,?]? "),
-		N_("Stash deletion [y,n,q,a,d%s,?]? "),
-		N_("Stash addition [y,n,q,a,d%s,?]? "),
-		N_("Stash this hunk [y,n,q,a,d%s,?]? "),
+		N_("Stash mode change%s [y,n,q,a,d%s,?]? "),
+		N_("Stash deletion%s [y,n,q,a,d%s,?]? "),
+		N_("Stash addition%s [y,n,q,a,d%s,?]? "),
+		N_("Stash this hunk%s [y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for stashing."),
@@ -88,10 +88,10 @@ static struct patch_mode patch_mode_reset_head = {
 	.is_reverse = 1,
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Unstage mode change [y,n,q,a,d%s,?]? "),
-		N_("Unstage deletion [y,n,q,a,d%s,?]? "),
-		N_("Unstage addition [y,n,q,a,d%s,?]? "),
-		N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
+		N_("Unstage mode change%s [y,n,q,a,d%s,?]? "),
+		N_("Unstage deletion%s [y,n,q,a,d%s,?]? "),
+		N_("Unstage addition%s [y,n,q,a,d%s,?]? "),
+		N_("Unstage this hunk%s [y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for unstaging."),
@@ -111,10 +111,10 @@ static struct patch_mode patch_mode_reset_nothead = {
 	.apply_check_args = { "--cached", NULL },
 	.index_only = 1,
 	.prompt_mode = {
-		N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index%s [y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index%s [y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index%s [y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index%s [y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -134,10 +134,10 @@ static struct patch_mode patch_mode_checkout_index = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -157,10 +157,10 @@ static struct patch_mode patch_mode_checkout_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from index and worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from index and worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Discard addition from index and worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from index and worktree%s [y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -179,10 +179,10 @@ static struct patch_mode patch_mode_checkout_nothead = {
 	.apply_for_checkout = 1,
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to index and worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to index and worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Apply addition to index and worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to index and worktree%s [y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -202,10 +202,10 @@ static struct patch_mode patch_mode_worktree_head = {
 	.apply_check_args = { "-R", NULL },
 	.is_reverse = 1,
 	.prompt_mode = {
-		N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
-		N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+		N_("Discard mode change from worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Discard deletion from worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Discard addition from worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Discard this hunk from worktree%s [y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for discarding."),
@@ -224,10 +224,10 @@ static struct patch_mode patch_mode_worktree_nothead = {
 	.apply_args = { NULL },
 	.apply_check_args = { NULL },
 	.prompt_mode = {
-		N_("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply addition to worktree [y,n,q,a,d%s,?]? "),
-		N_("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
+		N_("Apply mode change to worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Apply deletion to worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Apply addition to worktree%s [y,n,q,a,d%s,?]? "),
+		N_("Apply this hunk to worktree%s [y,n,q,a,d%s,?]? "),
 	},
 	.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
 			     "will immediately be marked for applying."),
@@ -1460,6 +1460,7 @@ static int patch_update_file(struct add_p_state *s,
 	render_diff_header(s, file_diff, colored, &s->buf);
 	fputs(s->buf.buf, stdout);
 	for (;;) {
+		const char *hunk_use_decision = "";
 		enum {
 			ALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,
 			ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,
@@ -1564,8 +1565,14 @@ static int patch_update_file(struct add_p_state *s,
 			      (uintmax_t)(file_diff->hunk_nr
 						? file_diff->hunk_nr
 						: 1));
+		if (hunk->use != UNDECIDED_HUNK) {
+			if (hunk->use == USE_HUNK)
+				hunk_use_decision = _(" (was: y)");
+			else
+				hunk_use_decision = _(" (was: n)");
+		}
 		printf(_(s->mode->prompt_mode[prompt_mode_type]),
-		       s->buf.buf);
+			hunk_use_decision, s->buf.buf);
 		if (*s->s.reset_color_interactive)
 			fputs(s->s.reset_color_interactive, stdout);
 		fflush(stdout);
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 4285314f35..5ce9c6dd60 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -527,7 +527,7 @@ test_expect_success 'goto hunk 1 with "g 1"' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g 1 | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -540,7 +540,7 @@ test_expect_success 'goto hunk 1 with "g1"' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g1 | git add -p >actual &&
 	tail -n 4 <actual >actual.trimmed &&
@@ -554,7 +554,7 @@ test_expect_success 'navigate to hunk via regex /pattern' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y /1,2 | git add -p >actual &&
 	tail -n 5 <actual >actual.trimmed &&
@@ -567,7 +567,7 @@ test_expect_success 'navigate to hunk via regex / pattern' '
 	_10
 	+15
 	_20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y / 1,2 | git add -p >actual &&
 	tail -n 4 <actual >actual.trimmed &&
@@ -579,11 +579,11 @@ test_expect_success 'print again the hunk' '
 	tr _ " " >expect <<-EOF &&
 	+15
 	 20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
+	(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? @@ -1,2 +1,3 @@
 	 10
 	+15
 	 20
-	(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
+	(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?_
 	EOF
 	test_write_lines s y g 1 p | git add -p >actual &&
 	tail -n 7 <actual >actual.trimmed &&
@@ -595,11 +595,11 @@ test_expect_success TTY 'print again the hunk (PAGER)' '
 	cat >expect <<-EOF &&
 	<GREEN>+<RESET><GREEN>15<RESET>
 	 20<RESET>
-	<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>
+	<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>
 	PAGER  10<RESET>
 	PAGER <GREEN>+<RESET><GREEN>15<RESET>
 	PAGER  20<RESET>
-	<BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
+	<BOLD;BLUE>(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
 	EOF
 	test_write_lines s y g 1 P |
 	(
@@ -810,7 +810,7 @@ test_expect_success 'colors can be overridden' '
 	<BOLD>-old<RESET>
 	<BLUE>+new<RESET>
 	<CYAN> more-context<RESET>
-	<YELLOW>(1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
+	<YELLOW>(1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? <RESET>
 	EOF
 	test_cmp expect actual
 '
-- 
2.39.5 (Apple Git-154)
Junio C Hamano· Jan 11, 2026, 03:57 UTC · re: Abraham Samuel Adekunle · lore

Re: [GSoC PATCH v7] add -p: show user's hunk decision when selecting hunks

Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:
Show 20 quoted lines
> 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>
> ---
> Changes in v7:
> ==============
> - changed hunk selection state message to reduce prompt width
> - Removed redundant `file_diff->hunk_nr` in hunk->use check
> - Made the prompt mode more readable by retaining the space before choices
> - set `hunk_use_decision` to empty string if `hunk->use == UNDECIDED` to match
>   the prompt mode.
The latest "was: X" does look short and sweet.

Let's wait for a few days to see if others have more input and then mark the topic for 'next'.

Thanks.
Samuel Abraham· Jan 11, 2026, 11:04 UTC · re: Junio C Hamano · lore

Re: [GSoC PATCH v7] add -p: show user's hunk decision when selecting hunks

On Sun, Jan 11, 2026 at 4:57 AM Junio C Hamano <gitster@pobox.com> wrote:
Show 30 quoted lines
>
> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:
>
> > 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>
> > ---
> > Changes in v7:
> > ==============
> > - changed hunk selection state message to reduce prompt width
> > - Removed redundant `file_diff->hunk_nr` in hunk->use check
> > - Made the prompt mode more readable by retaining the space before choices
> > - set `hunk_use_decision` to empty string if `hunk->use == UNDECIDED` to match
> >   the prompt mode.
>
> The latest "was: X" does look short and sweet.
>
> Let's wait for a few days to see if others have more input and then
> mark the topic for 'next'.
>
> Thanks.

This sounds good. Thanks

Abraham.

← back to recent threads