{"thread":{"id":"64857","subject":"[RFC PATCH 0/1] add-patch: Allow reworking with a file after deciding on its hunks","startedAt":"2026-01-23T11:56:39Z","lastAt":"2026-02-21T09:06:59Z","messageCount":54,"participants":["Abraham Samuel Adekunle","Junio C Hamano","Samuel Abraham"],"isPatch":true,"patchVersion":1,"patchTotal":1},"messages":[{"id":"534538","messageId":"cover.1769164663.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":null,"subject":"[RFC PATCH 0/1] add-patch: Allow reworking with a file after deciding on its hunks","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-01-23T11:56:47Z","receivedAt":"2026-01-23T11:56:39Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"Hello,\nIn the discussion between Phillip Wood and Junio C Hamano in [1], Junio suggested\nsome enhancements to the UI of the add interactive.\n\nThey include;\ni. Add a way to see the previous hunk decision of the current hunk after the\n   user navigates back with K/J.\nii. Allow reworking with a file after deciding on all its hunks without\n    auto advancing.\niii. When having multiple modified files, allow switching from the current file to\n     another file whose hunks have already been decided.\n\nI have been able to work on 'i' above in [2] and this RFC seeks to find suggestions\non 'ii' and maybe 'iii' as this enters design realms\n\nThe patch is in no way a final version. I just want to present something that\nmembers giving suggestions can work with.\n\nWhile trying to follow the suggestions in [1] in the patch, when all hunks\nhave been decided;\n\t* A what_now prompts appears, allowing navigation with J/K, q to quit\n\tand '>' to go to the next file if there is a next file\n\n\t* If K/J is used to return to a hunk from the what_now mode, after any new decision,\n\ton the hunk, the user is brought back to the what_now prompt since all hunks\n\thad previously been decided on.\n\nI would appreciate your thoughts on this.\nThanks\n\n1. https://lore.kernel.org/git/xmqqseg9azdc.fsf@gitster.g/\n2. https://lore.kernel.org/git/aV_IGCld5T_dBxTs@Adekunles-MacBook-Air.local/\n\nAbraham Samuel Adekunle (1):\n  add-patch: Allow reworking with a file after deciding on all its hunks\n\n add-patch.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 65 insertions(+), 6 deletions(-)\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"534539","messageId":"e98d8aa20fb4a82b93b9887e38eb8289252b936d.1769164663.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1769164663.git.abrahamadekunle50@gmail.com","subject":"[RFC PATCH 1/1] add-patch: Allow reworking with a file after deciding on all its hunks","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-01-23T11:58:45Z","receivedAt":"2026-01-23T11:58:38Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"After deciding on all hunks in a file, the interactive session\nadvances automatically to the next file if there is another,\nor the process ends.\n\nAllow for reworking with a file by introducing a what_now prompt which\nallows for navigating with J/K or advancing to the next file if there is one.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-patch.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 65 insertions(+), 6 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 173a53241e..1ac565b0ab 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1449,7 +1449,7 @@ static int patch_update_file(struct add_p_state *s,\n \tstruct hunk *hunk;\n \tchar ch;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tint colored = !!s->colored.len, quit = 0, use_pager = 0;\n+\tint colored = !!s->colored.len, quit = 0, use_pager = 0, skip_what_now = 0;\n \tenum prompt_mode_type prompt_mode_type;\n \n \t/* Empty added files have no hunks */\n@@ -1498,12 +1498,61 @@ static int patch_update_file(struct add_p_state *s,\n \n \t\t/* Everything decided? */\n \t\tif (undecided_previous < 0 && undecided_next < 0 &&\n-\t\t    hunk->use != UNDECIDED_HUNK)\n-\t\t\tbreak;\n+\t\t    hunk->use != UNDECIDED_HUNK && !skip_what_now ) {\n+\t\t\tconst char *prompt_whatnow;\n+\t\t\t/* Allow navigation between hunks or go to next file */\n \n+\t\t\tif (s->file_diff_nr > 1)\n+\t\t\t\tprompt_whatnow = _(\"What now? [J,K,q,>]? \");\n+\t\t\telse\n+\t\t\t\tprompt_whatnow = _(\"What now? [J,K,q]? \");\n+\t\t\tprintf(\"%s %s\",\n+\t\t\t\ts->s.prompt_color,\n+\t\t\t\tprompt_whatnow);\n+\t\t\tif (*s->s.reset_color_interactive)\n+\t\t\t\tfputs(s->s.reset_color_interactive, stdout);\n+\t\t\tfflush(stdout);\n+\t\t\tif (read_single_character(s) == EOF) {\n+\t\t\t\tquit = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (!s->answer.len)\n+\t\t\t\tcontinue;\n+\t\t\tif (s->answer.buf[0] == '>' && s->file_diff_nr > 1) {\n+\t\t\t\tskip_what_now = 0;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\telse if (s->answer.buf[0] == 'K') {\n+\t\t\t\tif (file_diff->hunk_nr > 1) {\n+\t\t\t\t\thunk_index = dec_mod(hunk_index, file_diff->hunk_nr);\n+\t\t\t\t\tskip_what_now = 1;\n+\t\t\t\t}\n+\t\t\t\telse\n+\t\t\t\t\terr(s, _(\"No other hunk\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\telse if (s->answer.buf[0] == 'J') {\n+\t\t\t\tif (file_diff->hunk_nr > 1) {\n+\t\t\t\t\thunk_index = inc_mod(hunk_index, file_diff->hunk_nr);\n+\t\t\t\t\tskip_what_now = 1;\n+\t\t\t\t}\n+\t\t\t\telse\n+\t\t\t\t\terr(s, _(\"No other hunk\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\telse if (s->answer.buf[0] == 'q') {\n+\t\t\t\tskip_what_now = 0;\n+\t\t\t\tquit = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\telse {\n+\t\t\t\terr(s, _(\"All hunks decided (use '?' for help)\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n-\t\t\tif (rendered_hunk_index != hunk_index) {\n+\t\t\tif (rendered_hunk_index != hunk_index || skip_what_now == 1) {\n \t\t\t\tif (use_pager) {\n \t\t\t\t\tsetup_pager(the_repository);\n \t\t\t\t\tsigchain_push(SIGPIPE, SIG_IGN);\n@@ -1586,12 +1635,18 @@ static int patch_update_file(struct add_p_state *s,\n \t\tif (ch == 'y') {\n \t\t\thunk->use = USE_HUNK;\n soft_increment:\n-\t\t\thunk_index = undecided_next < 0 ?\n-\t\t\t\tfile_diff->hunk_nr : undecided_next;\n+\t\t\tif (skip_what_now) {\n+\t\t\t\thunk_index = inc_mod(hunk_index, file_diff->hunk_nr);\n+\t\t\t\tskip_what_now = 0;\n+\t\t\t} else\n+\t\t\t\thunk_index = undecided_next < 0 ?\n+\t\t\t\t\tfile_diff->hunk_nr : undecided_next;\n \t\t} else if (ch == 'n') {\n \t\t\thunk->use = SKIP_HUNK;\n \t\t\tgoto soft_increment;\n \t\t} else if (ch == 'a') {\n+\t\t\tif (skip_what_now)\n+\t\t\t\tskip_what_now = 0;\n \t\t\tif (file_diff->hunk_nr) {\n \t\t\t\tfor (; hunk_index < file_diff->hunk_nr; hunk_index++) {\n \t\t\t\t\thunk = file_diff->hunk + hunk_index;\n@@ -1604,6 +1659,8 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\thunk->use = USE_HUNK;\n \t\t\t}\n \t\t} else if (ch == 'd') {\n+\t\t\tif (skip_what_now)\n+\t\t\t\tskip_what_now = 0;\n \t\t\tif (file_diff->hunk_nr) {\n \t\t\t\tfor (; hunk_index < file_diff->hunk_nr; hunk_index++) {\n \t\t\t\t\thunk = file_diff->hunk + hunk_index;\n@@ -1616,6 +1673,8 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\thunk->use = SKIP_HUNK;\n \t\t\t}\n \t\t} else if (ch == 'q') {\n+\t\t\tif (skip_what_now)\n+\t\t\t\tskip_what_now = 0;\n \t\t\tquit = 1;\n \t\t\tbreak;\n \t\t} else if (s->answer.buf[0] == 'K') {\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"534558","messageId":"xmqqv7gsi8s6.fsf@gitster.g","threadId":"64857","inReplyTo":"e98d8aa20fb4a82b93b9887e38eb8289252b936d.1769164663.git.abrahamadekunle50@gmail.com","subject":"Re: [RFC PATCH 1/1] add-patch: Allow reworking with a file after deciding on all its hunks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-23T16:38:33Z","receivedAt":"2026-01-23T16:38:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> After deciding on all hunks in a file, the interactive session\n> advances automatically to the next file if there is another,\n> or the process ends.\n>\n> Allow for reworking with a file by introducing a what_now prompt which\n> allows for navigating with J/K or advancing to the next file if there is one.\n\nDescribe \"how\" you are allowing these new things that users used not\nto be able to do (no, not in the \"by adding this variable and\nswitching on its value\" sense, but in the \"now deciding on all the\nhunks in a file does not automatically advance to the next file, and\nthe user has to do X to move forward\" sense).\n\n> -\tint colored = !!s->colored.len, quit = 0, use_pager = 0;\n> +\tint colored = !!s->colored.len, quit = 0, use_pager = 0, skip_what_now = 0;\n\nThis is getting overly long.  Wouldn't it be easier to follow if a\npreliminary patch split these existing variables into three\nindependent definitions, and the main patch adds the fourth one?\n\n> +\t\t\tif (s->file_diff_nr > 1)\n> +\t\t\t\tprompt_whatnow = _(\"What now? [J,K,q,>]? \");\n> +\t\t\telse\n> +\t\t\t\tprompt_whatnow = _(\"What now? [J,K,q]? \");\n\nI wonder if \">\" has to be made so special.  Wouldn't it be easier to\nreason about the logic if \">\" (and probably \"<\" to go back by one\nfile) are added to the prompt in the same logic that decides 'g',\n'k', 's', etc. should be shown using the \"permitted\" variable?\n\nAnd when the inter-file navigation is in the permitted set (i.e.,\nthere are multiple files involved), you'd show \">\" (or \"<\", or both\nif you are dealing with the second file among three files) and ask,\ninstead of silently moving to the next one, or something like that.\n\nOrganizing the logic that way will also allow you to move to the\nnext file _without_ first having to decide on all hunks in the\ncurrent file.  Just say \">\" to deal with the next file first, and\nafter you are done, either come back with \"<\", or the system notices\nthat there are undecided hunks in the earlier file and takes you\nback automatically.\n\nI also have a hunch that with such a code structure you may not even\nneed skip_what_now flag, but I haven't even written the code in my\nhead, so if somebody tries to do so, they may discover the reason\nwhy such a flag is still needed.\n\n>  \t\tstrbuf_reset(&s->buf);\n>  \t\tif (file_diff->hunk_nr) {\n> -\t\t\tif (rendered_hunk_index != hunk_index) {\n> +\t\t\tif (rendered_hunk_index != hunk_index || skip_what_now == 1) {\n\nStyle (which may become irrelevant, as I just said the variable may\nnot be needed after all, but anyway).  Elsewhere skip_what_now is\nused only for \"is it zero, or is it not zero?\".  Comparing\nexplicitly with 1 only here makes readers suspect if assigning 2 or\n70 to the variable has special meanings and wastes their brain\ncycles.\n"},{"id":"534585","messageId":"CADYq+fZ-U-iG==0e24E7ncNcjSUaBJz9qsKKEG6UENjxHnW4pg@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqv7gsi8s6.fsf@gitster.g","subject":"Re: [RFC PATCH 1/1] add-patch: Allow reworking with a file after deciding on all its hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-01-23T21:43:09Z","receivedAt":"2026-01-23T21:43:09Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Fri, Jan 23, 2026 at 5:38 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > After deciding on all hunks in a file, the interactive session\n> > advances automatically to the next file if there is another,\n> > or the process ends.\n> >\n> > Allow for reworking with a file by introducing a what_now prompt which\n> > allows for navigating with J/K or advancing to the next file if there is one.\n>\n> Describe \"how\" you are allowing these new things that users used not\n> to be able to do (no, not in the \"by adding this variable and\n> switching on its value\" sense, but in the \"now deciding on all the\n> hunks in a file does not automatically advance to the next file, and\n> the user has to do X to move forward\" sense).\n\nThank you for your feedback Junio.\n\nOkay, this is noted.\n\n>\n> > -     int colored = !!s->colored.len, quit = 0, use_pager = 0;\n> > +     int colored = !!s->colored.len, quit = 0, use_pager = 0, skip_what_now = 0;\n>\n> This is getting overly long.  Wouldn't it be easier to follow if a\n> preliminary patch split these existing variables into three\n> independent definitions, and the main patch adds the fourth one?\n\nYes it will be easier to follow.\nI will do that.\n\n>\n> > +                     if (s->file_diff_nr > 1)\n> > +                             prompt_whatnow = _(\"What now? [J,K,q,>]? \");\n> > +                     else\n> > +                             prompt_whatnow = _(\"What now? [J,K,q]? \");\n>\n> I wonder if \">\" has to be made so special.  Wouldn't it be easier to\n> reason about the logic if \">\" (and probably \"<\" to go back by one\n> file) are added to the prompt in the same logic that decides 'g',\n> 'k', 's', etc. should be shown using the \"permitted\" variable?\n\nYes this makes a lot of sense.\nThank you for the guidance.\n\n>\n> And when the inter-file navigation is in the permitted set (i.e.,\n> there are multiple files involved), you'd show \">\" (or \"<\", or both\n> if you are dealing with the second file among three files) and ask,\n> instead of silently moving to the next one, or something like that.\n\nYes I understand.\n\n>\n> Organizing the logic that way will also allow you to move to the\n> next file _without_ first having to decide on all hunks in the\n> current file.  Just say \">\" to deal with the next file first, and\n> after you are done, either come back with \"<\", or the system notices\n> that there are undecided hunks in the earlier file and takes you\n> back automatically.\n\nThis is very insightful.\nI will work with this design in mind\nThank you\n\n>\n> I also have a hunch that with such a code structure you may not even\n> need skip_what_now flag, but I haven't even written the code in my\n> head, so if somebody tries to do so, they may discover the reason\n> why such a flag is still needed.\n\nYes\n\n>\n> >               strbuf_reset(&s->buf);\n> >               if (file_diff->hunk_nr) {\n> > -                     if (rendered_hunk_index != hunk_index) {\n> > +                     if (rendered_hunk_index != hunk_index || skip_what_now == 1) {\n>\n> Style (which may become irrelevant, as I just said the variable may\n> not be needed after all, but anyway).  Elsewhere skip_what_now is\n> used only for \"is it zero, or is it not zero?\".  Comparing\n> explicitly with 1 only here makes readers suspect if assigning 2 or\n> 70 to the variable has special meanings and wastes their brain\n> cycles.\n\nI will give more thoughtful efforts into the next versions I will send after\nyour recommendations.\n\nThank you very much\n\nAbraham.\n"},{"id":"534713","messageId":"cover.1769522219.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1769164663.git.abrahamadekunle50@gmail.com","subject":"[PATCH v2 0/1] Allow reworking with a file when making hunk decisions","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-01-27T15:43:06Z","receivedAt":"2026-01-27T15:42:57Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"Hello,\nAfter review and suggestions from Junio, I have been able to add the '<' and '>'\noptions for going to the previous file and next file respectively.\nIf there is only one file, neither of the options will be available, if we are in the\nsecond of three or more file, both '<' and '>' will be available and if we are at the last file,\nonly '<' will be available.\n\nThis will enable simultaneous hunk decisions between between files.\nAfter all decisions have been made in a file, a prompt shows which asks\n\"All hunks decided. What now?\" that allows reworking with the file,\nmoving to the next or previous file as the case may be.\n\nSince all hunks in the file have been decided, if the user navigates to a particular\nhunk with 'K' or 'J' and redecides on an already decided hunk with options such as, 'y' or 'n',\nthe user is taken back to the first hunk with the \"what now?\" prompt shown.\n\nThe decision to use 'q' as a submit is because after some or all the decisions have been made\nin a file, 'q' submits them as is even though in the `help_patch_text` it say `q` will\nnot stage the current hunk and all hunks after it. This is not true if hunks decisions\nhave been made and the user navigates with 'K' and 'J' or uses 'a' to select all hunks and\n'q' after wards\n\nI have not attempted to work on the t/3701-interactive.sh yet but this will be done\nafter concensus on the UI when all hunks have been decided.\n\nAbraham Samuel Adekunle (1):\n  Allow reworking with a file after deciding on all its hunks\n\n add-patch.c | 139 ++++++++++++++++++++++++++++++++++++++--------------\n 1 file changed, 102 insertions(+), 37 deletions(-)\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"534714","messageId":"9b21cb901ab14397af94b8ed2d09da1a9a6d862b.1769522219.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1769522219.git.abrahamadekunle50@gmail.com","subject":"[PATCH v2 1/1] Allow reworking with a file after deciding on all its hunks","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-01-27T15:45:13Z","receivedAt":"2026-01-27T15:45:08Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"After deciding on all hunks in a file, the interactive session\nadvances automatically to the next file if there is another,\nor the process ends.\n\nNow the process does not advance automatically. A user can choose to\ngo to the next file by pressing '>' or the previous file by pressing '<',\nbefore or after deciding on all hunks in the current file.\n\nAfter all hunks have been decided in a file, a prompt appears,\nwhich allow the user to still rework with the file by applying\nthe options available in the permit set for that hunk, and\nafter all the decisions, the user presses 'q' to submit.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\nChanges in v2:\n=============\n- Added '<' and '>' to the permit set\n- All patches are now applied after all decisions in all files have been\n  made by submitting with 'q'.\n\n add-patch.c | 139 ++++++++++++++++++++++++++++++++++++++--------------\n 1 file changed, 102 insertions(+), 37 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 173a53241e..edb2fab3fd 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1418,6 +1418,8 @@ N_(\"j - go to the next undecided hunk, roll over at the bottom\\n\"\n    \"e - manually edit the current hunk\\n\"\n    \"p - print the current hunk\\n\"\n    \"P - print the current hunk using the pager\\n\"\n+   \"> - go to the next file\\n\"\n+   \"< - go to the previous file\\n\"\n    \"? - print help\\n\");\n \n static size_t dec_mod(size_t a, size_t m)\n@@ -1441,6 +1443,17 @@ static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)\n \treturn false;\n }\n \n+static size_t get_file_diff_index(struct add_p_state *s, struct file_diff *file_diff) {\n+\tsize_t idx = 0;\n+\tfor (size_t i = 0; i < s->file_diff_nr; i++) {\n+\t\tif (s->file_diff + i == file_diff) {\n+\t\t\tidx = i;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\treturn idx;\n+}\n+\n static int patch_update_file(struct add_p_state *s,\n \t\t\t     struct file_diff *file_diff)\n {\n@@ -1448,9 +1461,10 @@ static int patch_update_file(struct add_p_state *s,\n \tssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n \tstruct hunk *hunk;\n \tchar ch;\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint colored = !!s->colored.len, quit = 0, use_pager = 0;\n \tenum prompt_mode_type prompt_mode_type;\n+\tsize_t file_diff_index = get_file_diff_index(s, file_diff);\n+\tint all_decided = 0;\n \n \t/* Empty added files have no hunks */\n \tif (!file_diff->hunk_nr && !file_diff->added)\n@@ -1467,7 +1481,9 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\tALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,\n \t\t\tALLOW_SEARCH_AND_GOTO = 1 << 4,\n \t\t\tALLOW_SPLIT = 1 << 5,\n-\t\t\tALLOW_EDIT = 1 << 6\n+\t\t\tALLOW_EDIT = 1 << 6,\n+\t\t\tALLOW_GOTO_PREVIOUS_FILE = 1 << 7,\n+\t\t\tALLOW_GOTO_NEXT_FILE = 1 << 8\n \t\t} permitted = 0;\n \n \t\tif (hunk_index >= file_diff->hunk_nr)\n@@ -1499,8 +1515,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t/* Everything decided? */\n \t\tif (undecided_previous < 0 && undecided_next < 0 &&\n \t\t    hunk->use != UNDECIDED_HUNK)\n-\t\t\tbreak;\n-\n+\t\t\t\tall_decided = 1;\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n \t\t\tif (rendered_hunk_index != hunk_index) {\n@@ -1548,6 +1563,16 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\tpermitted |= ALLOW_EDIT;\n \t\t\t\tstrbuf_addstr(&s->buf, \",e\");\n \t\t\t}\n+\t\t\tif (file_diff_index >= 0 &&\n+\t\t\t\tfile_diff_index < s->file_diff_nr - 1) {\n+\t\t\t\tpermitted |= ALLOW_GOTO_NEXT_FILE;\n+\t\t\t\tstrbuf_addstr(&s->buf, \",>\");\n+\t\t\t}\n+\t\t\tif (file_diff_index > 0 &&\n+\t\t\t\tfile_diff_index <= s->file_diff_nr - 1) {\n+\t\t\t\tpermitted |= ALLOW_GOTO_PREVIOUS_FILE;\n+\t\t\t\tstrbuf_addstr(&s->buf, \",<\");\n+\t\t\t}\n \t\t\tstrbuf_addstr(&s->buf, \",p,P\");\n \t\t}\n \t\tif (file_diff->deleted)\n@@ -1566,6 +1591,9 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\t\t\t: 1));\n \t\tprintf(_(s->mode->prompt_mode[prompt_mode_type]),\n \t\t       s->buf.buf);\n+\t\tif (all_decided)\n+\t\t\tprintf(_(\"\\n%s All hunks decided. What now? \"),\n+\t\t\t\ts->s.prompt_color);\n \t\tif (*s->s.reset_color_interactive)\n \t\t\tfputs(s->s.reset_color_interactive, stdout);\n \t\tfflush(stdout);\n@@ -1618,7 +1646,24 @@ static int patch_update_file(struct add_p_state *s,\n \t\t} else if (ch == 'q') {\n \t\t\tquit = 1;\n \t\t\tbreak;\n-\t\t} else if (s->answer.buf[0] == 'K') {\n+\t\t} else if (s->answer.buf[0] == '>') {\n+\t\t\tif (permitted & ALLOW_GOTO_NEXT_FILE) {\n+\t\t\t\tquit = 0;\n+\t\t\t\tbreak;\n+\t\t\t} else {\n+\t\t\t\terr(s, _(\"No next file\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t} else if (s->answer.buf[0] == '<') {\n+\t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_FILE) {\n+\t\t\t\tquit = 2;\n+\t\t\t\tbreak;\n+\t\t\t} else {\n+\t\t\t\terr(s, _(\"No previous file\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\t\telse if (s->answer.buf[0] == 'K') {\n \t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n \t\t\t\thunk_index = dec_mod(hunk_index,\n \t\t\t\t\t\t     file_diff->hunk_nr);\n@@ -1775,33 +1820,6 @@ static int patch_update_file(struct add_p_state *s,\n \t\t}\n \t}\n \n-\t/* Any hunk to be used? */\n-\tfor (i = 0; i < file_diff->hunk_nr; i++)\n-\t\tif (file_diff->hunk[i].use == USE_HUNK)\n-\t\t\tbreak;\n-\n-\tif (i < file_diff->hunk_nr ||\n-\t    (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n-\t\t/* At least one hunk selected: apply */\n-\t\tstrbuf_reset(&s->buf);\n-\t\treassemble_patch(s, file_diff, 0, &s->buf);\n-\n-\t\tdiscard_index(s->s.r->index);\n-\t\tif (s->mode->apply_for_checkout)\n-\t\t\tapply_for_checkout(s, &s->buf,\n-\t\t\t\t\t   s->mode->is_reverse);\n-\t\telse {\n-\t\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n-\t\t\tstrvec_pushv(&cp.args, s->mode->apply_args);\n-\t\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n-\t\t\t\t\t NULL, 0, NULL, 0))\n-\t\t\t\terror(_(\"'git apply' failed\"));\n-\t\t}\n-\t\tif (repo_read_index(s->s.r) >= 0)\n-\t\t\trepo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n-\t\t\t\t\t\t     1, NULL, NULL, NULL);\n-\t}\n-\n \tputchar('\\n');\n \treturn quit;\n }\n@@ -1813,7 +1831,9 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \tstruct add_p_state s = {\n \t\t{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT\n \t};\n-\tsize_t i, binary_count = 0;\n+\tsize_t i, j, binary_count = 0;\n+\tsize_t patch_update_result = 0;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n \n \tinit_add_i_state(&s.s, r, o);\n \n@@ -1852,11 +1872,56 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\treturn -1;\n \t}\n \n-\tfor (i = 0; i < s.file_diff_nr; i++)\n-\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)\n+\tfor (i = 0; i < s.file_diff_nr;) {\n+\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {\n \t\t\tbinary_count++;\n-\t\telse if (patch_update_file(&s, s.file_diff + i))\n-\t\t\tbreak;\n+\t\t\ti++;\n+\t\t\tcontinue;\n+\t\t}\n+\t\telse {\n+\t\t\tpatch_update_result = patch_update_file(&s, s.file_diff + i);\n+\t\t\tif (patch_update_result == 0) {\n+\t\t\t\ti++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (patch_update_result == 1)\n+\t\t\t\tbreak;\n+\t\t\tif (patch_update_result == 2) {\n+\t\t\t\ti--;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\t}\n+\tfor (i = 0; i < s.file_diff_nr; i++) {\n+\n+\t\t\t/* Any hunk to be used? */\n+\t\tfor (j = 0; j < s.file_diff[i].hunk_nr; j++)\n+\t\t\tif (s.file_diff[i].hunk[j].use == USE_HUNK)\n+\t\t\t\tbreak;\n+\n+\t\tif (j < s.file_diff[i].hunk_nr ||\n+\t    (!s.file_diff[i].hunk_nr && s.file_diff[i].head.use == USE_HUNK)) {\n+\t\t\t/* At least one hunk selected: apply */\n+\t\t\tstrbuf_reset(&s.buf);\n+\t\t\treassemble_patch(&s, s.file_diff + i, 0, &s.buf);\n+\n+\t\t\tdiscard_index(s.s.r->index);\n+\t\t\tif (s.mode->apply_for_checkout)\n+\t\t\t\tapply_for_checkout(&s, &s.buf,\n+\t\t\t\t\t\ts.mode->is_reverse);\n+\t\t\telse {\n+\t\t\t\tsetup_child_process(&s, &cp, \"apply\", NULL);\n+\t\t\t\tstrvec_pushv(&cp.args, s.mode->apply_args);\n+\t\t\t\tif (pipe_command(&cp, s.buf.buf, s.buf.len,\n+\t\t\t\t\t\tNULL, 0, NULL, 0))\n+\t\t\t\t\terror(_(\"'git apply' failed\"));\n+\t\t\t}\n+\t\t\tif (repo_read_index(s.s.r) >= 0)\n+\t\t\t\trepo_refresh_and_write_index(s.s.r, REFRESH_QUIET, 0,\n+\t\t\t\t\t\t\t\t1, NULL, NULL, NULL);\n+\t\t}\n+\n+\t}\n \n \tif (s.file_diff_nr == 0)\n \t\terr(&s, _(\"No changes.\"));\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"534717","messageId":"xmqqtsw7f0mz.fsf@gitster.g","threadId":"64857","inReplyTo":"cover.1769522219.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v2 0/1] Allow reworking with a file when making hunk decisions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-27T17:04:04Z","receivedAt":"2026-01-27T17:04:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> If there is only one file, neither of the options will be\n> available, if we are in the second of three or more file, both '<'\n> and '>' will be available and if we are at the last file, only '<'\n> will be available.\n\nAn obvious alternative would be to treat the files as a ring, going\nnext from the last one would take you to the first one, etc., but I\nthink what you described is just as good.\n\n> This will enable simultaneous hunk decisions between between files.\n> After all decisions have been made in a file, a prompt shows which asks\n> \"All hunks decided. What now?\" that allows reworking with the file,\n> moving to the next or previous file as the case may be.\n\nI forgot to mention this in the previous review, but this would be a\nchange that existing users may be surprised by.  We _might_ need to\nintroduce a flag to enable this as a new and optional feature.\n\n> The decision to use 'q' as a submit is because after some or all\n> the decisions have been made in a file, 'q' submits them as is\n> even though in the `help_patch_text` it say `q` will not stage the\n> current hunk and all hunks after it.\n\nThe users do need to _knowingly_ leave some hunks undecided and\napply what they already decided to use, and I think 'q' is an\nappropriate option to use.  It is what the current system does,\nand I do not think it changes with this new feature.\n\nThanks.\n"},{"id":"534728","messageId":"xmqq7bt2g4tl.fsf@gitster.g","threadId":"64857","inReplyTo":"9b21cb901ab14397af94b8ed2d09da1a9a6d862b.1769522219.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v2 1/1] Allow reworking with a file after deciding on all its hunks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-27T20:48:22Z","receivedAt":"2026-01-27T20:48:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> diff --git a/add-patch.c b/add-patch.c\n> index 173a53241e..edb2fab3fd 100644\n> --- a/add-patch.c\n> +++ b/add-patch.c\n> @@ -1418,6 +1418,8 @@ N_(\"j - go to the next undecided hunk, roll over at the bottom\\n\"\n>     \"e - manually edit the current hunk\\n\"\n>     \"p - print the current hunk\\n\"\n>     \"P - print the current hunk using the pager\\n\"\n> +   \"> - go to the next file\\n\"\n> +   \"< - go to the previous file\\n\"\n>     \"? - print help\\n\");\n\nAs I said earlier, these may have to be optional.  It may give\nexisting users a jarring experience to be given a prompt after\ndeciding on all the hunks in a file, when they expect to be on\nthe next file already.\n\n> @@ -1441,6 +1443,17 @@ static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)\n>  \treturn false;\n>  }\n>  \n> +static size_t get_file_diff_index(struct add_p_state *s, struct file_diff *file_diff) {\n> +\tsize_t idx = 0;\n> +\tfor (size_t i = 0; i < s->file_diff_nr; i++) {\n> +\t\tif (s->file_diff + i == file_diff) {\n> +\t\t\tidx = i;\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n> +\treturn idx;\n> +}\n\nYuck.  Can't we lose the need for this function if we change the\ninterface into patch_update_file so that it takes the index of the\nfile (i.e., instead of \"&s.file_diff[i]\", pass \"i\")?  There is only\none caller to patch_update_file() which is run_add_p(), so such a\nclean-up should be trivial.\n\n>  static int patch_update_file(struct add_p_state *s,\n>  \t\t\t     struct file_diff *file_diff)\n>  {\n> @@ -1448,9 +1461,10 @@ static int patch_update_file(struct add_p_state *s,\n>  \tssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n>  \tstruct hunk *hunk;\n>  \tchar ch;\n> -\tstruct child_process cp = CHILD_PROCESS_INIT;\n\nThis is related to the hoisting of the actual patch application to\nthe caller, but it is not explained why such a change is needed, and\nit byitself, even without the \"jump to the next file before deciding\non all the hunks\" feature.  What problem is it solving???\n\nIf it is necessary to move the code to run \"git apply\" to the\ncaller, would it make sense to split this patch into at least two\npatches, one to do such a move, possibly another patch to change the\nfunction signature of patch_update_file() so that it takes the file\nindex instead of file_diff struct, and finally another patch to\nallow jumping around the files?\n\n>  \tint colored = !!s->colored.len, quit = 0, use_pager = 0;\n>  \tenum prompt_mode_type prompt_mode_type;\n> +\tsize_t file_diff_index = get_file_diff_index(s, file_diff);\n> +\tint all_decided = 0;\n>  \n>  \t/* Empty added files have no hunks */\n>  \tif (!file_diff->hunk_nr && !file_diff->added)\n> @@ -1467,7 +1481,9 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\tALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,\n>  \t\t\tALLOW_SEARCH_AND_GOTO = 1 << 4,\n>  \t\t\tALLOW_SPLIT = 1 << 5,\n> -\t\t\tALLOW_EDIT = 1 << 6\n> +\t\t\tALLOW_EDIT = 1 << 6,\n> +\t\t\tALLOW_GOTO_PREVIOUS_FILE = 1 << 7,\n> +\t\t\tALLOW_GOTO_NEXT_FILE = 1 << 8\n>  \t\t} permitted = 0;\n>  \n>  \t\tif (hunk_index >= file_diff->hunk_nr)\n> @@ -1499,8 +1515,7 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t/* Everything decided? */\n>  \t\tif (undecided_previous < 0 && undecided_next < 0 &&\n>  \t\t    hunk->use != UNDECIDED_HUNK)\n> -\t\t\tbreak;\n> -\n> +\t\t\t\tall_decided = 1;\n>  \t\tstrbuf_reset(&s->buf);\n>  \t\tif (file_diff->hunk_nr) {\n>  \t\t\tif (rendered_hunk_index != hunk_index) {\n> @@ -1548,6 +1563,16 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\t\tpermitted |= ALLOW_EDIT;\n>  \t\t\t\tstrbuf_addstr(&s->buf, \",e\");\n>  \t\t\t}\n> +\t\t\tif (file_diff_index >= 0 &&\n> +\t\t\t\tfile_diff_index < s->file_diff_nr - 1) {\n> +\t\t\t\tpermitted |= ALLOW_GOTO_NEXT_FILE;\n> +\t\t\t\tstrbuf_addstr(&s->buf, \",>\");\n> +\t\t\t}\n> +\t\t\tif (file_diff_index > 0 &&\n> +\t\t\t\tfile_diff_index <= s->file_diff_nr - 1) {\n> +\t\t\t\tpermitted |= ALLOW_GOTO_PREVIOUS_FILE;\n> +\t\t\t\tstrbuf_addstr(&s->buf, \",<\");\n> +\t\t\t}\n\nAs can be seen in what patch_update_file() does when the user says\n'J' or 'K', hunks in a file are treated as a ring, and these\ncommands are enabled as long as there are more than one hunks.\n\nPerhaps that is more familiar than \"when we hit the floor, we cannot\nsink deeper, and when we hit the ceiling, we cannot float more\",\nwhich seems to be what the above implements.\n\n>  \t\t\tstrbuf_addstr(&s->buf, \",p,P\");\n>  \t\t}\n>  \t\tif (file_diff->deleted)\n> @@ -1566,6 +1591,9 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\t\t\t\t: 1));\n>  \t\tprintf(_(s->mode->prompt_mode[prompt_mode_type]),\n>  \t\t       s->buf.buf);\n> +\t\tif (all_decided)\n> +\t\t\tprintf(_(\"\\n%s All hunks decided. What now? \"),\n> +\t\t\t\ts->s.prompt_color);\n>  \t\tif (*s->s.reset_color_interactive)\n>  \t\t\tfputs(s->s.reset_color_interactive, stdout);\n>  \t\tfflush(stdout);\n> @@ -1618,7 +1646,24 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t} else if (ch == 'q') {\n>  \t\t\tquit = 1;\n>  \t\t\tbreak;\n> -\t\t} else if (s->answer.buf[0] == 'K') {\n> +\t\t} else if (s->answer.buf[0] == '>') {\n> +\t\t\tif (permitted & ALLOW_GOTO_NEXT_FILE) {\n> +\t\t\t\tquit = 0;\n> +\t\t\t\tbreak;\n> +\t\t\t} else {\n> +\t\t\t\terr(s, _(\"No next file\"));\n> +\t\t\t\tcontinue;\n> +\t\t\t}\n> +\t\t} else if (s->answer.buf[0] == '<') {\n> +\t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_FILE) {\n> +\t\t\t\tquit = 2;\n> +\t\t\t\tbreak;\n\nWhat's the magic number \"2\"?  Should \"quit\" become an enum with\nelements that are more meaningfully named?\n\n> +\t\t\t} else {\n> +\t\t\t\terr(s, _(\"No previous file\"));\n> +\t\t\t\tcontinue;\n> +\t\t\t}\n> +\t\t}\n> +\t\telse if (s->answer.buf[0] == 'K') {\n>  \t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n>  \t\t\t\thunk_index = dec_mod(hunk_index,\n>  \t\t\t\t\t\t     file_diff->hunk_nr);\n> @@ -1775,33 +1820,6 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t}\n>  \t}\n>  \n> -\t/* Any hunk to be used? */\n> -\tfor (i = 0; i < file_diff->hunk_nr; i++)\n> -\t\tif (file_diff->hunk[i].use == USE_HUNK)\n> -\t\t\tbreak;\n> -\n> -\tif (i < file_diff->hunk_nr ||\n> -\t    (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n> -\t\t/* At least one hunk selected: apply */\n> -\t\tstrbuf_reset(&s->buf);\n> -\t\treassemble_patch(s, file_diff, 0, &s->buf);\n> -\n> -\t\tdiscard_index(s->s.r->index);\n> -\t\tif (s->mode->apply_for_checkout)\n> -\t\t\tapply_for_checkout(s, &s->buf,\n> -\t\t\t\t\t   s->mode->is_reverse);\n> -\t\telse {\n> -\t\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n> -\t\t\tstrvec_pushv(&cp.args, s->mode->apply_args);\n> -\t\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n> -\t\t\t\t\t NULL, 0, NULL, 0))\n> -\t\t\t\terror(_(\"'git apply' failed\"));\n> -\t\t}\n> -\t\tif (repo_read_index(s->s.r) >= 0)\n> -\t\t\trepo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n> -\t\t\t\t\t\t     1, NULL, NULL, NULL);\n> -\t}\n\nIt is not obvious why the above code needs to be hoisted to the\ncaller.  \n\n>  \tputchar('\\n');\n>  \treturn quit;\n>  }\n> @@ -1813,7 +1831,9 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n>  \tstruct add_p_state s = {\n>  \t\t{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT\n>  \t};\n> -\tsize_t i, binary_count = 0;\n> +\tsize_t i, j, binary_count = 0;\n> +\tsize_t patch_update_result = 0;\n\nHmph, I think patch_update_file() returns \"int quit\".  Why do we\nwant overly wide type to store the result, which cannot even express\nnegative number to potentially signal a failure?\n\n> +\tstruct child_process cp = CHILD_PROCESS_INIT;\n>  \n>  \tinit_add_i_state(&s.s, r, o);\n>  \n> @@ -1852,11 +1872,56 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n>  \t\treturn -1;\n>  \t}\n>  \n> -\tfor (i = 0; i < s.file_diff_nr; i++)\n> -\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)\n> +\tfor (i = 0; i < s.file_diff_nr;) {\n> +\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {\n>  \t\t\tbinary_count++;\n> -\t\telse if (patch_update_file(&s, s.file_diff + i))\n> -\t\t\tbreak;\n> +\t\t\ti++;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\t\telse {\n> +\t\t\tpatch_update_result = patch_update_file(&s, s.file_diff + i);\n> +\t\t\tif (patch_update_result == 0) {\n> +\t\t\t\ti++;\n> +\t\t\t\tcontinue;\n> +\t\t\t}\n> +\t\t\tif (patch_update_result == 1)\n> +\t\t\t\tbreak;\n> +\t\t\tif (patch_update_result == 2) {\n> +\t\t\t\ti--;\n> +\t\t\t\tcontinue;\n> +\t\t\t}\n> +\t\t}\n> +\t}\n> +\tfor (i = 0; i < s.file_diff_nr; i++) {\n> +\n> +\t\t\t/* Any hunk to be used? */\n> +\t\tfor (j = 0; j < s.file_diff[i].hunk_nr; j++)\n> +\t\t\tif (s.file_diff[i].hunk[j].use == USE_HUNK)\n> +\t\t\t\tbreak;\n> +\n> +\t\tif (j < s.file_diff[i].hunk_nr ||\n> +\t    (!s.file_diff[i].hunk_nr && s.file_diff[i].head.use == USE_HUNK)) {\n> +\t\t\t/* At least one hunk selected: apply */\n> +\t\t\tstrbuf_reset(&s.buf);\n> +\t\t\treassemble_patch(&s, s.file_diff + i, 0, &s.buf);\n> +\n> +\t\t\tdiscard_index(s.s.r->index);\n> +\t\t\tif (s.mode->apply_for_checkout)\n> +\t\t\t\tapply_for_checkout(&s, &s.buf,\n> +\t\t\t\t\t\ts.mode->is_reverse);\n> +\t\t\telse {\n> +\t\t\t\tsetup_child_process(&s, &cp, \"apply\", NULL);\n> +\t\t\t\tstrvec_pushv(&cp.args, s.mode->apply_args);\n> +\t\t\t\tif (pipe_command(&cp, s.buf.buf, s.buf.len,\n> +\t\t\t\t\t\tNULL, 0, NULL, 0))\n> +\t\t\t\t\terror(_(\"'git apply' failed\"));\n> +\t\t\t}\n> +\t\t\tif (repo_read_index(s.s.r) >= 0)\n> +\t\t\t\trepo_refresh_and_write_index(s.s.r, REFRESH_QUIET, 0,\n> +\t\t\t\t\t\t\t\t1, NULL, NULL, NULL);\n> +\t\t}\n> +\n> +\t}\n\nOne upside of having \"git apply\" at the end of patch_update_file()\nis that you can \"^C\" out of \"git add -p\" or your terminal connection\ncan be cut off, after dealing with hunks in a few early files, and\nthese early part of your work that you have already done are already\nreflected to the working tree files.  By hoisting the logic to the\ncaller, this is making the update all-or-none, which is good in\ntransactional systems, but can make a horrible experience for an\ninteractive use where you make progress while thinking.\n\nSo I am not yet convinced if this change makes sense---it could be\nbecause of the lack of justification for this change.\n\n\n\n>  \n>  \tif (s.file_diff_nr == 0)\n>  \t\terr(&s, _(\"No changes.\"));\n"},{"id":"534749","messageId":"CADYq+fYBn4WKbtdeXM0bUVsTCa40wRM3TRZOK=XXst-wEep4mQ@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqtsw7f0mz.fsf@gitster.g","subject":"Re: [PATCH v2 0/1] Allow reworking with a file when making hunk decisions","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-01-28T09:49:34Z","receivedAt":"2026-01-28T09:49:33Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Tue, Jan 27, 2026 at 6:04 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > If there is only one file, neither of the options will be\n> > available, if we are in the second of three or more file, both '<'\n> > and '>' will be available and if we are at the last file, only '<'\n> > will be available.\n>\n> An obvious alternative would be to treat the files as a ring, going\n> next from the last one would take you to the first one, etc., but I\n> think what you described is just as good.\n\nOkay I will work that\n\n>\n> > This will enable simultaneous hunk decisions between between files.\n> > After all decisions have been made in a file, a prompt shows which asks\n> > \"All hunks decided. What now?\" that allows reworking with the file,\n> > moving to the next or previous file as the case may be.\n>\n> I forgot to mention this in the previous review, but this would be a\n> change that existing users may be surprised by.  We _might_ need to\n> introduce a flag to enable this as a new and optional feature.\n\nYes I thought about this too and it is a good idea.\nThanks\n\n>\n> > The decision to use 'q' as a submit is because after some or all\n> > the decisions have been made in a file, 'q' submits them as is\n> > even though in the `help_patch_text` it say `q` will not stage the\n> > current hunk and all hunks after it.\n>\n> The users do need to _knowingly_ leave some hunks undecided and\n> apply what they already decided to use, and I think 'q' is an\n> appropriate option to use.  It is what the current system does,\n> and I do not think it changes with this new feature.\n\nOkay thank you.\n"},{"id":"534758","messageId":"CADYq+fYeWh0tLEepOGVa=1i9tXZfWaGfyi6H+xUB7rbdQ=t5aQ@mail.gmail.com","threadId":"64857","inReplyTo":"xmqq7bt2g4tl.fsf@gitster.g","subject":"Re: [PATCH v2 1/1] Allow reworking with a file after deciding on all its hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-01-28T11:26:48Z","receivedAt":"2026-01-28T11:26:48Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Tue, Jan 27, 2026 at 9:48 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > diff --git a/add-patch.c b/add-patch.c\n> > index 173a53241e..edb2fab3fd 100644\n> > --- a/add-patch.c\n> > +++ b/add-patch.c\n> > @@ -1418,6 +1418,8 @@ N_(\"j - go to the next undecided hunk, roll over at the bottom\\n\"\n> >     \"e - manually edit the current hunk\\n\"\n> >     \"p - print the current hunk\\n\"\n> >     \"P - print the current hunk using the pager\\n\"\n> > +   \"> - go to the next file\\n\"\n> > +   \"< - go to the previous file\\n\"\n> >     \"? - print help\\n\");\n>\n> As I said earlier, these may have to be optional.  It may give\n> existing users a jarring experience to be given a prompt after\n> deciding on all the hunks in a file, when they expect to be on\n> the next file already.\n\nYes I agree.\nI will work on making it an optional feature.\n\n>\n> > @@ -1441,6 +1443,17 @@ static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)\n> >       return false;\n> >  }\n> >\n> > +static size_t get_file_diff_index(struct add_p_state *s, struct file_diff *file_diff) {\n> > +     size_t idx = 0;\n> > +     for (size_t i = 0; i < s->file_diff_nr; i++) {\n> > +             if (s->file_diff + i == file_diff) {\n> > +                     idx = i;\n> > +                     break;\n> > +             }\n> > +     }\n> > +     return idx;\n> > +}\n>\n> Yuck.  Can't we lose the need for this function if we change the\n> interface into patch_update_file so that it takes the index of the\n> file (i.e., instead of \"&s.file_diff[i]\", pass \"i\")?  There is only\n> one caller to patch_update_file() which is run_add_p(), so such a\n> clean-up should be trivial.\n\nAh yes this is definitely a sweet and better option.\n\n>\n> >  static int patch_update_file(struct add_p_state *s,\n> >                            struct file_diff *file_diff)\n> >  {\n> > @@ -1448,9 +1461,10 @@ static int patch_update_file(struct add_p_state *s,\n> >       ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n> >       struct hunk *hunk;\n> >       char ch;\n> > -     struct child_process cp = CHILD_PROCESS_INIT;\n>\n> This is related to the hoisting of the actual patch application to\n> the caller, but it is not explained why such a change is needed, and\n> it byitself, even without the \"jump to the next file before deciding\n> on all the hunks\" feature.  What problem is it solving???\n\nI explained this below\n\n>\n> If it is necessary to move the code to run \"git apply\" to the\n> caller, would it make sense to split this patch into at least two\n> patches, one to do such a move, possibly another patch to change the\n> function signature of patch_update_file() so that it takes the file\n> index instead of file_diff struct, and finally another patch to\n> allow jumping around the files?\n\nOkay yes it would make much sense.\n\n>\n> >       int colored = !!s->colored.len, quit = 0, use_pager = 0;\n> >       enum prompt_mode_type prompt_mode_type;\n> > +     size_t file_diff_index = get_file_diff_index(s, file_diff);\n> > +     int all_decided = 0;\n> >\n> >       /* Empty added files have no hunks */\n> >       if (!file_diff->hunk_nr && !file_diff->added)\n> > @@ -1467,7 +1481,9 @@ static int patch_update_file(struct add_p_state *s,\n> >                       ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,\n> >                       ALLOW_SEARCH_AND_GOTO = 1 << 4,\n> >                       ALLOW_SPLIT = 1 << 5,\n> > -                     ALLOW_EDIT = 1 << 6\n> > +                     ALLOW_EDIT = 1 << 6,\n> > +                     ALLOW_GOTO_PREVIOUS_FILE = 1 << 7,\n> > +                     ALLOW_GOTO_NEXT_FILE = 1 << 8\n> >               } permitted = 0;\n> >\n> >               if (hunk_index >= file_diff->hunk_nr)\n> > @@ -1499,8 +1515,7 @@ static int patch_update_file(struct add_p_state *s,\n> >               /* Everything decided? */\n> >               if (undecided_previous < 0 && undecided_next < 0 &&\n> >                   hunk->use != UNDECIDED_HUNK)\n> > -                     break;\n> > -\n> > +                             all_decided = 1;\n> >               strbuf_reset(&s->buf);\n> >               if (file_diff->hunk_nr) {\n> >                       if (rendered_hunk_index != hunk_index) {\n> > @@ -1548,6 +1563,16 @@ static int patch_update_file(struct add_p_state *s,\n> >                               permitted |= ALLOW_EDIT;\n> >                               strbuf_addstr(&s->buf, \",e\");\n> >                       }\n> > +                     if (file_diff_index >= 0 &&\n> > +                             file_diff_index < s->file_diff_nr - 1) {\n> > +                             permitted |= ALLOW_GOTO_NEXT_FILE;\n> > +                             strbuf_addstr(&s->buf, \",>\");\n> > +                     }\n> > +                     if (file_diff_index > 0 &&\n> > +                             file_diff_index <= s->file_diff_nr - 1) {\n> > +                             permitted |= ALLOW_GOTO_PREVIOUS_FILE;\n> > +                             strbuf_addstr(&s->buf, \",<\");\n> > +                     }\n>\n> As can be seen in what patch_update_file() does when the user says\n> 'J' or 'K', hunks in a file are treated as a ring, and these\n> commands are enabled as long as there are more than one hunks.\n>\n> Perhaps that is more familiar than \"when we hit the floor, we cannot\n> sink deeper, and when we hit the ceiling, we cannot float more\",\n> which seems to be what the above implements.\n\nYes I understand this now.\nIt does make sense this way.\n\n>\n> >                       strbuf_addstr(&s->buf, \",p,P\");\n> >               }\n> >               if (file_diff->deleted)\n> > @@ -1566,6 +1591,9 @@ static int patch_update_file(struct add_p_state *s,\n> >                                               : 1));\n> >               printf(_(s->mode->prompt_mode[prompt_mode_type]),\n> >                      s->buf.buf);\n> > +             if (all_decided)\n> > +                     printf(_(\"\\n%s All hunks decided. What now? \"),\n> > +                             s->s.prompt_color);\n> >               if (*s->s.reset_color_interactive)\n> >                       fputs(s->s.reset_color_interactive, stdout);\n> >               fflush(stdout);\n> > @@ -1618,7 +1646,24 @@ static int patch_update_file(struct add_p_state *s,\n> >               } else if (ch == 'q') {\n> >                       quit = 1;\n> >                       break;\n> > -             } else if (s->answer.buf[0] == 'K') {\n> > +             } else if (s->answer.buf[0] == '>') {\n> > +                     if (permitted & ALLOW_GOTO_NEXT_FILE) {\n> > +                             quit = 0;\n> > +                             break;\n> > +                     } else {\n> > +                             err(s, _(\"No next file\"));\n> > +                             continue;\n> > +                     }\n> > +             } else if (s->answer.buf[0] == '<') {\n> > +                     if (permitted & ALLOW_GOTO_PREVIOUS_FILE) {\n> > +                             quit = 2;\n> > +                             break;\n>\n> What's the magic number \"2\"?  Should \"quit\" become an enum with\n> elements that are more meaningfully named?\n\nOkay, yes an enum would be better.\n\n>\n> > +                     } else {\n> > +                             err(s, _(\"No previous file\"));\n> > +                             continue;\n> > +                     }\n> > +             }\n> > +             else if (s->answer.buf[0] == 'K') {\n> >                       if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n> >                               hunk_index = dec_mod(hunk_index,\n> >                                                    file_diff->hunk_nr);\n> > @@ -1775,33 +1820,6 @@ static int patch_update_file(struct add_p_state *s,\n> >               }\n> >       }\n> >\n> > -     /* Any hunk to be used? */\n> > -     for (i = 0; i < file_diff->hunk_nr; i++)\n> > -             if (file_diff->hunk[i].use == USE_HUNK)\n> > -                     break;\n> > -\n> > -     if (i < file_diff->hunk_nr ||\n> > -         (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n> > -             /* At least one hunk selected: apply */\n> > -             strbuf_reset(&s->buf);\n> > -             reassemble_patch(s, file_diff, 0, &s->buf);\n> > -\n> > -             discard_index(s->s.r->index);\n> > -             if (s->mode->apply_for_checkout)\n> > -                     apply_for_checkout(s, &s->buf,\n> > -                                        s->mode->is_reverse);\n> > -             else {\n> > -                     setup_child_process(s, &cp, \"apply\", NULL);\n> > -                     strvec_pushv(&cp.args, s->mode->apply_args);\n> > -                     if (pipe_command(&cp, s->buf.buf, s->buf.len,\n> > -                                      NULL, 0, NULL, 0))\n> > -                             error(_(\"'git apply' failed\"));\n> > -             }\n> > -             if (repo_read_index(s->s.r) >= 0)\n> > -                     repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n> > -                                                  1, NULL, NULL, NULL);\n> > -     }\n>\n> It is not obvious why the above code needs to be hoisted to the\n> caller.\n\nI explained this below.\n\n>\n> >       putchar('\\n');\n> >       return quit;\n> >  }\n> > @@ -1813,7 +1831,9 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n> >       struct add_p_state s = {\n> >               { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT\n> >       };\n> > -     size_t i, binary_count = 0;\n> > +     size_t i, j, binary_count = 0;\n> > +     size_t patch_update_result = 0;\n>\n> Hmph, I think patch_update_file() returns \"int quit\".  Why do we\n> want overly wide type to store the result, which cannot even express\n> negative number to potentially signal a failure?\n\nSorry, this is a mistake on my part\n\n>\n> > +     struct child_process cp = CHILD_PROCESS_INIT;\n> >\n> >       init_add_i_state(&s.s, r, o);\n> >\n> > @@ -1852,11 +1872,56 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n> >               return -1;\n> >       }\n> >\n> > -     for (i = 0; i < s.file_diff_nr; i++)\n> > -             if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)\n> > +     for (i = 0; i < s.file_diff_nr;) {\n> > +             if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {\n> >                       binary_count++;\n> > -             else if (patch_update_file(&s, s.file_diff + i))\n> > -                     break;\n> > +                     i++;\n> > +                     continue;\n> > +             }\n> > +             else {\n> > +                     patch_update_result = patch_update_file(&s, s.file_diff + i);\n> > +                     if (patch_update_result == 0) {\n> > +                             i++;\n> > +                             continue;\n> > +                     }\n> > +                     if (patch_update_result == 1)\n> > +                             break;\n> > +                     if (patch_update_result == 2) {\n> > +                             i--;\n> > +                             continue;\n> > +                     }\n> > +             }\n> > +     }\n> > +     for (i = 0; i < s.file_diff_nr; i++) {\n> > +\n> > +                     /* Any hunk to be used? */\n> > +             for (j = 0; j < s.file_diff[i].hunk_nr; j++)\n> > +                     if (s.file_diff[i].hunk[j].use == USE_HUNK)\n> > +                             break;\n> > +\n> > +             if (j < s.file_diff[i].hunk_nr ||\n> > +         (!s.file_diff[i].hunk_nr && s.file_diff[i].head.use == USE_HUNK)) {\n> > +                     /* At least one hunk selected: apply */\n> > +                     strbuf_reset(&s.buf);\n> > +                     reassemble_patch(&s, s.file_diff + i, 0, &s.buf);\n> > +\n> > +                     discard_index(s.s.r->index);\n> > +                     if (s.mode->apply_for_checkout)\n> > +                             apply_for_checkout(&s, &s.buf,\n> > +                                             s.mode->is_reverse);\n> > +                     else {\n> > +                             setup_child_process(&s, &cp, \"apply\", NULL);\n> > +                             strvec_pushv(&cp.args, s.mode->apply_args);\n> > +                             if (pipe_command(&cp, s.buf.buf, s.buf.len,\n> > +                                             NULL, 0, NULL, 0))\n> > +                                     error(_(\"'git apply' failed\"));\n> > +                     }\n> > +                     if (repo_read_index(s.s.r) >= 0)\n> > +                             repo_refresh_and_write_index(s.s.r, REFRESH_QUIET, 0,\n> > +                                                             1, NULL, NULL, NULL);\n> > +             }\n> > +\n> > +     }\n>\n> One upside of having \"git apply\" at the end of patch_update_file()\n> is that you can \"^C\" out of \"git add -p\" or your terminal connection\n> can be cut off, after dealing with hunks in a few early files, and\n> these early part of your work that you have already done are already\n> reflected to the working tree files.  By hoisting the logic to the\n> caller, this is making the update all-or-none, which is good in\n> transactional systems, but can make a horrible experience for an\n> interactive use where you make progress while thinking.\n>\n> So I am not yet convinced if this change makes sense---it could be\n> because of the lack of justification for this change.\n\nWhat I observed after adding the '>' and '<' options is that if a user chooses\nto use a hunk A in file 1, and then goes to file 2 with '>', comes back to\nfile 1 with '<', and decides on hunk A to skip it instead, because\npatch_update_file() has\napplied the file with the hunk the user initially decided to use\nbefore proceeding to file\n2 with '>', coming back to redecide and say skip does not apply the\nlatest decision\nand when you check the index, the file with the hunks which the user\ninitially decided to\nuse but changed to skip is present in the index.\n\nBut if the user initially decided to skip a hunk in a file, goes to\nthe next file with '>'\nand back to the first file, changes the decision on the hunk to use,\nit applies the patch\nwith the hunk because the hunk was not initially selected when the\npatch was applied.\nBut if he now goes away and comes back to the file a third time and\nchooses to skip the\nhunk, then quits with 'q', because he had selected to use the hunk the\nsecond time,\nchoosing skip again will not work.\n\nSo basically, initially choosing to use a hunk in a file, going to\nanother file and coming\nback to this file then choosing to skip it does not register the\nlatest skip decision\non that hunk.\n\nThat was why I decided to do it this way.\nI will appreciate a better suggestion from you\n\nThanks\nAbraham.\n"},{"id":"534856","messageId":"CADYq+fbt7zHO=gAsRp=b5MTb=2aFfifCjWnW6u+58iv4dk6bMQ@mail.gmail.com","threadId":"64857","inReplyTo":"CADYq+fYeWh0tLEepOGVa=1i9tXZfWaGfyi6H+xUB7rbdQ=t5aQ@mail.gmail.com","subject":"Re: [PATCH v2 1/1] Allow reworking with a file after deciding on all its hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-01-30T09:22:25Z","receivedAt":"2026-01-30T09:22:24Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Wed, Jan 28, 2026 at 12:26 PM Samuel Abraham\n<abrahamadekunle50@gmail.com> wrote:\n>\n> On Tue, Jan 27, 2026 at 9:48 PM Junio C Hamano <gitster@pobox.com> wrote:\n> >\n> > Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n> >\n> > > diff --git a/add-patch.c b/add-patch.c\n> > > index 173a53241e..edb2fab3fd 100644\n> > > --- a/add-patch.c\n> > > +++ b/add-patch.c\n> > > @@ -1418,6 +1418,8 @@ N_(\"j - go to the next undecided hunk, roll over at the bottom\\n\"\n> > >     \"e - manually edit the current hunk\\n\"\n> > >     \"p - print the current hunk\\n\"\n> > >     \"P - print the current hunk using the pager\\n\"\n> > > +   \"> - go to the next file\\n\"\n> > > +   \"< - go to the previous file\\n\"\n> > >     \"? - print help\\n\");\n> >\n> > As I said earlier, these may have to be optional.  It may give\n> > existing users a jarring experience to be given a prompt after\n> > deciding on all the hunks in a file, when they expect to be on\n> > the next file already.\n>\n> Yes I agree.\n> I will work on making it an optional feature.\n>\n> >\n> > > @@ -1441,6 +1443,17 @@ static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)\n> > >       return false;\n> > >  }\n> > >\n> > > +static size_t get_file_diff_index(struct add_p_state *s, struct file_diff *file_diff) {\n> > > +     size_t idx = 0;\n> > > +     for (size_t i = 0; i < s->file_diff_nr; i++) {\n> > > +             if (s->file_diff + i == file_diff) {\n> > > +                     idx = i;\n> > > +                     break;\n> > > +             }\n> > > +     }\n> > > +     return idx;\n> > > +}\n> >\n> > Yuck.  Can't we lose the need for this function if we change the\n> > interface into patch_update_file so that it takes the index of the\n> > file (i.e., instead of \"&s.file_diff[i]\", pass \"i\")?  There is only\n> > one caller to patch_update_file() which is run_add_p(), so such a\n> > clean-up should be trivial.\n>\n> Ah yes this is definitely a sweet and better option.\n>\n> >\n> > >  static int patch_update_file(struct add_p_state *s,\n> > >                            struct file_diff *file_diff)\n> > >  {\n> > > @@ -1448,9 +1461,10 @@ static int patch_update_file(struct add_p_state *s,\n> > >       ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n> > >       struct hunk *hunk;\n> > >       char ch;\n> > > -     struct child_process cp = CHILD_PROCESS_INIT;\n> >\n> > This is related to the hoisting of the actual patch application to\n> > the caller, but it is not explained why such a change is needed, and\n> > it byitself, even without the \"jump to the next file before deciding\n> > on all the hunks\" feature.  What problem is it solving???\n>\n> I explained this below\n>\n> >\n> > If it is necessary to move the code to run \"git apply\" to the\n> > caller, would it make sense to split this patch into at least two\n> > patches, one to do such a move, possibly another patch to change the\n> > function signature of patch_update_file() so that it takes the file\n> > index instead of file_diff struct, and finally another patch to\n> > allow jumping around the files?\n>\n> Okay yes it would make much sense.\n>\n> >\n> > >       int colored = !!s->colored.len, quit = 0, use_pager = 0;\n> > >       enum prompt_mode_type prompt_mode_type;\n> > > +     size_t file_diff_index = get_file_diff_index(s, file_diff);\n> > > +     int all_decided = 0;\n> > >\n> > >       /* Empty added files have no hunks */\n> > >       if (!file_diff->hunk_nr && !file_diff->added)\n> > > @@ -1467,7 +1481,9 @@ static int patch_update_file(struct add_p_state *s,\n> > >                       ALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,\n> > >                       ALLOW_SEARCH_AND_GOTO = 1 << 4,\n> > >                       ALLOW_SPLIT = 1 << 5,\n> > > -                     ALLOW_EDIT = 1 << 6\n> > > +                     ALLOW_EDIT = 1 << 6,\n> > > +                     ALLOW_GOTO_PREVIOUS_FILE = 1 << 7,\n> > > +                     ALLOW_GOTO_NEXT_FILE = 1 << 8\n> > >               } permitted = 0;\n> > >\n> > >               if (hunk_index >= file_diff->hunk_nr)\n> > > @@ -1499,8 +1515,7 @@ static int patch_update_file(struct add_p_state *s,\n> > >               /* Everything decided? */\n> > >               if (undecided_previous < 0 && undecided_next < 0 &&\n> > >                   hunk->use != UNDECIDED_HUNK)\n> > > -                     break;\n> > > -\n> > > +                             all_decided = 1;\n> > >               strbuf_reset(&s->buf);\n> > >               if (file_diff->hunk_nr) {\n> > >                       if (rendered_hunk_index != hunk_index) {\n> > > @@ -1548,6 +1563,16 @@ static int patch_update_file(struct add_p_state *s,\n> > >                               permitted |= ALLOW_EDIT;\n> > >                               strbuf_addstr(&s->buf, \",e\");\n> > >                       }\n> > > +                     if (file_diff_index >= 0 &&\n> > > +                             file_diff_index < s->file_diff_nr - 1) {\n> > > +                             permitted |= ALLOW_GOTO_NEXT_FILE;\n> > > +                             strbuf_addstr(&s->buf, \",>\");\n> > > +                     }\n> > > +                     if (file_diff_index > 0 &&\n> > > +                             file_diff_index <= s->file_diff_nr - 1) {\n> > > +                             permitted |= ALLOW_GOTO_PREVIOUS_FILE;\n> > > +                             strbuf_addstr(&s->buf, \",<\");\n> > > +                     }\n> >\n> > As can be seen in what patch_update_file() does when the user says\n> > 'J' or 'K', hunks in a file are treated as a ring, and these\n> > commands are enabled as long as there are more than one hunks.\n> >\n> > Perhaps that is more familiar than \"when we hit the floor, we cannot\n> > sink deeper, and when we hit the ceiling, we cannot float more\",\n> > which seems to be what the above implements.\n>\n> Yes I understand this now.\n> It does make sense this way.\n>\n> >\n> > >                       strbuf_addstr(&s->buf, \",p,P\");\n> > >               }\n> > >               if (file_diff->deleted)\n> > > @@ -1566,6 +1591,9 @@ static int patch_update_file(struct add_p_state *s,\n> > >                                               : 1));\n> > >               printf(_(s->mode->prompt_mode[prompt_mode_type]),\n> > >                      s->buf.buf);\n> > > +             if (all_decided)\n> > > +                     printf(_(\"\\n%s All hunks decided. What now? \"),\n> > > +                             s->s.prompt_color);\n> > >               if (*s->s.reset_color_interactive)\n> > >                       fputs(s->s.reset_color_interactive, stdout);\n> > >               fflush(stdout);\n> > > @@ -1618,7 +1646,24 @@ static int patch_update_file(struct add_p_state *s,\n> > >               } else if (ch == 'q') {\n> > >                       quit = 1;\n> > >                       break;\n> > > -             } else if (s->answer.buf[0] == 'K') {\n> > > +             } else if (s->answer.buf[0] == '>') {\n> > > +                     if (permitted & ALLOW_GOTO_NEXT_FILE) {\n> > > +                             quit = 0;\n> > > +                             break;\n> > > +                     } else {\n> > > +                             err(s, _(\"No next file\"));\n> > > +                             continue;\n> > > +                     }\n> > > +             } else if (s->answer.buf[0] == '<') {\n> > > +                     if (permitted & ALLOW_GOTO_PREVIOUS_FILE) {\n> > > +                             quit = 2;\n> > > +                             break;\n> >\n> > What's the magic number \"2\"?  Should \"quit\" become an enum with\n> > elements that are more meaningfully named?\n>\n> Okay, yes an enum would be better.\n>\n> >\n> > > +                     } else {\n> > > +                             err(s, _(\"No previous file\"));\n> > > +                             continue;\n> > > +                     }\n> > > +             }\n> > > +             else if (s->answer.buf[0] == 'K') {\n> > >                       if (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n> > >                               hunk_index = dec_mod(hunk_index,\n> > >                                                    file_diff->hunk_nr);\n> > > @@ -1775,33 +1820,6 @@ static int patch_update_file(struct add_p_state *s,\n> > >               }\n> > >       }\n> > >\n> > > -     /* Any hunk to be used? */\n> > > -     for (i = 0; i < file_diff->hunk_nr; i++)\n> > > -             if (file_diff->hunk[i].use == USE_HUNK)\n> > > -                     break;\n> > > -\n> > > -     if (i < file_diff->hunk_nr ||\n> > > -         (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n> > > -             /* At least one hunk selected: apply */\n> > > -             strbuf_reset(&s->buf);\n> > > -             reassemble_patch(s, file_diff, 0, &s->buf);\n> > > -\n> > > -             discard_index(s->s.r->index);\n> > > -             if (s->mode->apply_for_checkout)\n> > > -                     apply_for_checkout(s, &s->buf,\n> > > -                                        s->mode->is_reverse);\n> > > -             else {\n> > > -                     setup_child_process(s, &cp, \"apply\", NULL);\n> > > -                     strvec_pushv(&cp.args, s->mode->apply_args);\n> > > -                     if (pipe_command(&cp, s->buf.buf, s->buf.len,\n> > > -                                      NULL, 0, NULL, 0))\n> > > -                             error(_(\"'git apply' failed\"));\n> > > -             }\n> > > -             if (repo_read_index(s->s.r) >= 0)\n> > > -                     repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n> > > -                                                  1, NULL, NULL, NULL);\n> > > -     }\n> >\n> > It is not obvious why the above code needs to be hoisted to the\n> > caller.\n>\n> I explained this below.\n>\n> >\n> > >       putchar('\\n');\n> > >       return quit;\n> > >  }\n> > > @@ -1813,7 +1831,9 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n> > >       struct add_p_state s = {\n> > >               { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT\n> > >       };\n> > > -     size_t i, binary_count = 0;\n> > > +     size_t i, j, binary_count = 0;\n> > > +     size_t patch_update_result = 0;\n> >\n> > Hmph, I think patch_update_file() returns \"int quit\".  Why do we\n> > want overly wide type to store the result, which cannot even express\n> > negative number to potentially signal a failure?\n>\n> Sorry, this is a mistake on my part\n>\n> >\n> > > +     struct child_process cp = CHILD_PROCESS_INIT;\n> > >\n> > >       init_add_i_state(&s.s, r, o);\n> > >\n> > > @@ -1852,11 +1872,56 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n> > >               return -1;\n> > >       }\n> > >\n> > > -     for (i = 0; i < s.file_diff_nr; i++)\n> > > -             if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)\n> > > +     for (i = 0; i < s.file_diff_nr;) {\n> > > +             if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {\n> > >                       binary_count++;\n> > > -             else if (patch_update_file(&s, s.file_diff + i))\n> > > -                     break;\n> > > +                     i++;\n> > > +                     continue;\n> > > +             }\n> > > +             else {\n> > > +                     patch_update_result = patch_update_file(&s, s.file_diff + i);\n> > > +                     if (patch_update_result == 0) {\n> > > +                             i++;\n> > > +                             continue;\n> > > +                     }\n> > > +                     if (patch_update_result == 1)\n> > > +                             break;\n> > > +                     if (patch_update_result == 2) {\n> > > +                             i--;\n> > > +                             continue;\n> > > +                     }\n> > > +             }\n> > > +     }\n> > > +     for (i = 0; i < s.file_diff_nr; i++) {\n> > > +\n> > > +                     /* Any hunk to be used? */\n> > > +             for (j = 0; j < s.file_diff[i].hunk_nr; j++)\n> > > +                     if (s.file_diff[i].hunk[j].use == USE_HUNK)\n> > > +                             break;\n> > > +\n> > > +             if (j < s.file_diff[i].hunk_nr ||\n> > > +         (!s.file_diff[i].hunk_nr && s.file_diff[i].head.use == USE_HUNK)) {\n> > > +                     /* At least one hunk selected: apply */\n> > > +                     strbuf_reset(&s.buf);\n> > > +                     reassemble_patch(&s, s.file_diff + i, 0, &s.buf);\n> > > +\n> > > +                     discard_index(s.s.r->index);\n> > > +                     if (s.mode->apply_for_checkout)\n> > > +                             apply_for_checkout(&s, &s.buf,\n> > > +                                             s.mode->is_reverse);\n> > > +                     else {\n> > > +                             setup_child_process(&s, &cp, \"apply\", NULL);\n> > > +                             strvec_pushv(&cp.args, s.mode->apply_args);\n> > > +                             if (pipe_command(&cp, s.buf.buf, s.buf.len,\n> > > +                                             NULL, 0, NULL, 0))\n> > > +                                     error(_(\"'git apply' failed\"));\n> > > +                     }\n> > > +                     if (repo_read_index(s.s.r) >= 0)\n> > > +                             repo_refresh_and_write_index(s.s.r, REFRESH_QUIET, 0,\n> > > +                                                             1, NULL, NULL, NULL);\n> > > +             }\n> > > +\n> > > +     }\n> >\n> > One upside of having \"git apply\" at the end of patch_update_file()\n> > is that you can \"^C\" out of \"git add -p\" or your terminal connection\n> > can be cut off, after dealing with hunks in a few early files, and\n> > these early part of your work that you have already done are already\n> > reflected to the working tree files.  By hoisting the logic to the\n> > caller, this is making the update all-or-none, which is good in\n> > transactional systems, but can make a horrible experience for an\n> > interactive use where you make progress while thinking.\n> >\n> > So I am not yet convinced if this change makes sense---it could be\n> > because of the lack of justification for this change.\n>\n> What I observed after adding the '>' and '<' options is that if a user chooses\n> to use a hunk A in file 1, and then goes to file 2 with '>', comes back to\n> file 1 with '<', and decides on hunk A to skip it instead, because\n> patch_update_file() has\n> applied the file with the hunk the user initially decided to use\n> before proceeding to file\n> 2 with '>', coming back to redecide and say skip does not apply the\n> latest decision\n> and when you check the index, the file with the hunks which the user\n> initially decided to\n> use but changed to skip is present in the index.\n>\n> But if the user initially decided to skip a hunk in a file, goes to\n> the next file with '>'\n> and back to the first file, changes the decision on the hunk to use,\n> it applies the patch\n> with the hunk because the hunk was not initially selected when the\n> patch was applied.\n> But if he now goes away and comes back to the file a third time and\n> chooses to skip the\n> hunk, then quits with 'q', because he had selected to use the hunk the\n> second time,\n> choosing skip again will not work.\n>\n> So basically, initially choosing to use a hunk in a file, going to\n> another file and coming\n> back to this file then choosing to skip it does not register the\n> latest skip decision\n> on that hunk.\n>\n> That was why I decided to do it this way.\n> I will appreciate a better suggestion from you\n>\n> Thanks\n> Abraham.\n\nHello Junio, thank you for your review.\nHere I explain my decision to move the \"git apply\" in patch_update_file()\nto the caller.\n\nDoes it sound like a valid reason to make the move?\nThanks\n\nAbraham\n"},{"id":"534890","messageId":"xmqq8qdf83nu.fsf@gitster.g","threadId":"64857","inReplyTo":"CADYq+fbt7zHO=gAsRp=b5MTb=2aFfifCjWnW6u+58iv4dk6bMQ@mail.gmail.com","subject":"Re: [PATCH v2 1/1] Allow reworking with a file after deciding on all its hunks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-30T16:29:41Z","receivedAt":"2026-01-30T16:29:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Samuel Abraham <abrahamadekunle50@gmail.com> writes:\n\n> Hello Junio, thank you for your review.\n> Here I explain my decision to move the \"git apply\" in patch_update_file()\n> to the caller.\n>\n> Does it sound like a valid reason to make the move?\n\nI am not sure, but as long as this is an optional feature, users can\nchoose not to opt in if they do not like the new \"all or none\"\nsemantics, I guess.\n\nThanks.\n"},{"id":"534902","messageId":"CADYq+faOK=VQarMMyxD9OCTMrM__o0=87Bm0MSxFkSfYV7v7nw@mail.gmail.com","threadId":"64857","inReplyTo":"xmqq8qdf83nu.fsf@gitster.g","subject":"Re: [PATCH v2 1/1] Allow reworking with a file after deciding on all its hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-01-30T17:36:09Z","receivedAt":"2026-01-30T17:36:09Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Fri, Jan 30, 2026 at 5:29 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Samuel Abraham <abrahamadekunle50@gmail.com> writes:\n>\n> > Hello Junio, thank you for your review.\n> > Here I explain my decision to move the \"git apply\" in patch_update_file()\n> > to the caller.\n> >\n> > Does it sound like a valid reason to make the move?\n>\n> I am not sure, but as long as this is an optional feature, users can\n> choose not to opt in if they do not like the new \"all or none\"\n> semantics, I guess.\n>\n> Thanks.\n\nOkay thank you.\nI will work on making it an optional feature\n\nAbraham\n"},{"id":"534923","messageId":"xmqqqzr54mam.fsf@gitster.g","threadId":"64857","inReplyTo":"CADYq+fbt7zHO=gAsRp=b5MTb=2aFfifCjWnW6u+58iv4dk6bMQ@mail.gmail.com","subject":"Re: [PATCH v2 1/1] Allow reworking with a file after deciding on all its hunks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-31T19:25:21Z","receivedAt":"2026-01-31T19:25:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Samuel Abraham <abrahamadekunle50@gmail.com> writes:\n\n>> What I observed after adding the '>' and '<' options is that if a user chooses\n>> to use a hunk A in file 1, and then goes to file 2 with '>', comes back to\n>> file 1 with '<', and decides on hunk A to skip it instead, because\n>> patch_update_file() has\n>> applied the file with the hunk the user initially decided to use\n>> before proceeding to file\n>> 2 with '>', coming back to redecide and say skip does not apply the\n>> latest decision\n>> and when you check the index, the file with the hunks which the user\n>> initially decided to\n>> use but changed to skip is present in the index.\n\nI am not sure if I would like the end result or rather prefer your\n\"all-or-none\", so please do not take this as \"here is a better way\nto implement it\" suggestion.\n\nBut you should be able to keep the current semantics, if you wanted\nto, even if you apply the chosen hunks when you switch files, like\nthe original code has been doing forever since it was written.  You\nknow which hunks you applied, so after applying before moving on to\nthe next file, you can drop these hunks from the list of hunks to be\ndecided for application.  When the user comes back to the current\nfile to decide on other hunks, you know that the already used hunks\nwould get in the way, so why keep them?\n\nHaving said that, I think the all-or-none mode may be handy if one\nmakes the current working tree dirty with many little unrelated and\ninsignificant changes and the only way to make sense is to see the\n\"git diff --cached\" output after adding some and leaving others, at\nleast in the way some people work.  I usually am very incremental\nwhen doing \"git add -p\", in that while using the command in one\nterminal, I run \"git diff --cached\" to see if I added unwanted\nthings by mistake and \"git diff\" to see if I left out necessary\nthings, so I would probably not be using the mode.  But that is just\nmy hunch without using the new interface long enough.\n\nThanks.\n"},{"id":"534964","messageId":"CADYq+fZFuvCRbFf=-XUR8TJsjW_YtjNdiXMzPv0mjMPbWcLO1g@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqqzr54mam.fsf@gitster.g","subject":"Re: [PATCH v2 1/1] Allow reworking with a file after deciding on all its hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-02T11:14:16Z","receivedAt":"2026-02-02T11:14:15Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Sat, Jan 31, 2026 at 8:25 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Samuel Abraham <abrahamadekunle50@gmail.com> writes:\n>\n> >> What I observed after adding the '>' and '<' options is that if a user chooses\n> >> to use a hunk A in file 1, and then goes to file 2 with '>', comes back to\n> >> file 1 with '<', and decides on hunk A to skip it instead, because\n> >> patch_update_file() has\n> >> applied the file with the hunk the user initially decided to use\n> >> before proceeding to file\n> >> 2 with '>', coming back to redecide and say skip does not apply the\n> >> latest decision\n> >> and when you check the index, the file with the hunks which the user\n> >> initially decided to\n> >> use but changed to skip is present in the index.\n>\n> I am not sure if I would like the end result or rather prefer your\n> \"all-or-none\", so please do not take this as \"here is a better way\n> to implement it\" suggestion.\n>\n> But you should be able to keep the current semantics, if you wanted\n> to, even if you apply the chosen hunks when you switch files, like\n> the original code has been doing forever since it was written.  You\n> know which hunks you applied, so after applying before moving on to\n> the next file, you can drop these hunks from the list of hunks to be\n> decided for application.  When the user comes back to the current\n> file to decide on other hunks, you know that the already used hunks\n> would get in the way, so why keep them?\n\nYes thank you so much for suggesting this approach.\n\n>\n> Having said that, I think the all-or-none mode may be handy if one\n> makes the current working tree dirty with many little unrelated and\n> insignificant changes and the only way to make sense is to see the\n> \"git diff --cached\" output after adding some and leaving others, at\n> least in the way some people work.  I usually am very incremental\n> when doing \"git add -p\", in that while using the command in one\n> terminal, I run \"git diff --cached\" to see if I added unwanted\n> things by mistake and \"git diff\" to see if I left out necessary\n> things, so I would probably not be using the mode.  But that is just\n> my hunch without using the new interface long enough.\n\nOkay I think retaining \"git apply\" in patch_update_file() and dropping\nthe hunks the user has already decided on when coming back to the file\nmakes sense.\nBy using this approach, we skip files that have been fully decided and applied,\nonly showing files that;\ni.  have been applied but also have undecided hunks.\nii.  not been applied and still have undecided hunks\nwhen the user navigates with \">\" and \"<\".\n\nThis will allow you to still run git diff--cached to see what has been\nadded while also being able\nto see what has not been added, while navigating around files.\n\nThank you.\nAbraham\n"},{"id":"534987","messageId":"xmqqzf5rys3f.fsf@gitster.g","threadId":"64857","inReplyTo":"CADYq+fZFuvCRbFf=-XUR8TJsjW_YtjNdiXMzPv0mjMPbWcLO1g@mail.gmail.com","subject":"Re: [PATCH v2 1/1] Allow reworking with a file after deciding on all its hunks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-02T17:26:28Z","receivedAt":"2026-02-02T17:26:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Samuel Abraham <abrahamadekunle50@gmail.com> writes:\n\n>> I am not sure if I would like the end result or rather prefer your\n>> \"all-or-none\", so please do not take this as \"here is a better way\n>> to implement it\" suggestion.\n>>\n>> But you should be able to keep the current semantics, if you wanted\n>> to, even if you apply the chosen hunks when you switch files, like\n>> the original code has been doing forever since it was written.  You\n>> know which hunks you applied, so after applying before moving on to\n>> the next file, you can drop these hunks from the list of hunks to be\n>> decided for application.  When the user comes back to the current\n>> file to decide on other hunks, you know that the already used hunks\n>> would get in the way, so why keep them?\n>\n> Yes thank you so much for suggesting this approach.\n\nNot so fast.  I explicitly said I am *NOT* suggesting anything.\n\nAnd thinking about it more, I do not think it makes any sense to do\nanything other than \"all-or-none\" when the command is working in\nyour new \"you can move to different files before you decide on all\nhunks in the current file\" mode (which I think we agreed to make it\nan optional mode).  Why?  After deciding yes, no, no among 5 hunks\nin the first file (leaving the hunks #4 and #5 undecided), you jump\nto the second file, do something there, and imagine that you come\nback.  If we drop the alrady applied hunks like the suggestion,\nwhich I did not make ;-), we'd then give you four hunks (as hunk #1\nhas been already applied), and even though you have already decided\nnot to use hunks #2 and #3, you *can* revisit them with \"J\" or \"K\",\nchange your mind and use them if you wanted to.  But it is too late\nfor the hunk #1.  It looks utterly inconsistent if you cannot change\nyour mind on hunk #1 but can on hunks #2 and #3 and it reduces the\nusefulness of \"you do not have to decide right now and visit other\nfiles before you do so\" mode.\n\nThanks.\n"},{"id":"535044","messageId":"CADYq+faasM8h0FJjop4GJeo_6fw-=_VXRZeqYURDbQFuR0CK1A@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqzf5rys3f.fsf@gitster.g","subject":"Re: [PATCH v2 1/1] Allow reworking with a file after deciding on all its hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-03T09:55:38Z","receivedAt":"2026-02-03T09:55:38Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Mon, Feb 2, 2026 at 6:26 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Samuel Abraham <abrahamadekunle50@gmail.com> writes:\n>\n> >> I am not sure if I would like the end result or rather prefer your\n> >> \"all-or-none\", so please do not take this as \"here is a better way\n> >> to implement it\" suggestion.\n> >>\n> >> But you should be able to keep the current semantics, if you wanted\n> >> to, even if you apply the chosen hunks when you switch files, like\n> >> the original code has been doing forever since it was written.  You\n> >> know which hunks you applied, so after applying before moving on to\n> >> the next file, you can drop these hunks from the list of hunks to be\n> >> decided for application.  When the user comes back to the current\n> >> file to decide on other hunks, you know that the already used hunks\n> >> would get in the way, so why keep them?\n> >\n> > Yes thank you so much for suggesting this approach.\n>\n> Not so fast.  I explicitly said I am *NOT* suggesting anything.\n\nYes you did.\n\n>\n> And thinking about it more, I do not think it makes any sense to do\n> anything other than \"all-or-none\" when the command is working in\n> your new \"you can move to different files before you decide on all\n> hunks in the current file\" mode (which I think we agreed to make it\n> an optional mode).  Why?  After deciding yes, no, no among 5 hunks\n> in the first file (leaving the hunks #4 and #5 undecided), you jump\n> to the second file, do something there, and imagine that you come\n> back.  If we drop the alrady applied hunks like the suggestion,\n> which I did not make ;-),\n\n:D\n\n> we'd then give you four hunks (as hunk #1\n> has been already applied), and even though you have already decided\n> not to use hunks #2 and #3, you *can* revisit them with \"J\" or \"K\",\n> change your mind and use them if you wanted to.  But it is too late\n> for the hunk #1.  It looks utterly inconsistent if you cannot change\n> your mind on hunk #1 but can on hunks #2 and #3 and it reduces the\n> usefulness of \"you do not have to decide right now and visit other\n> files before you do so\" mode.\n>\n> Thanks.\n\nOkay yes that would be very inconsistent.\n\nI briefly thought about this.\nIf a user decides USE on some hunks and goes to the next file, and we\napply the patch, the user comes and decides SKIP on those hunk(s),\ncan't we \"unapply\" those hunks using \"git apply -R\"?\nI have not really thought about the complexities but it seems to be\nsomething that might be complex.\nI just thought to share to hear your thoughts\n\nThanks\nAbraham\n"},{"id":"535351","messageId":"cover.1770390576.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1769522219.git.abrahamadekunle50@gmail.com","subject":"[PATCH v3 0/3] introduce new option `rework-with-file`","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-06T15:52:39Z","receivedAt":"2026-02-06T15:52:37Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"Hello,\nAfter further review from Junio, I have been able to make reworking with a\nfile during hunk selection an optional feature by passing the `rework-with-file`\nflag to the --patch option in the interactive machinery.\n\nWith the option, users can navigate in between files and while deciding on\nhunks as they wish with the '>' and '<' option for going to the next and\nprevious file respectively if there are more than one file.\n\nThe process shows a prompt which allows reworking with the file and changing\nprevious decisions if need be, going to the next or previous file if possible\nor using 'q' to submit and end the process.\n\nPatch 1 implements the new 'rework-with-file' options, Patch 2 makes some changes\nto allow interfile navigation when the option is supplied and Patch 3 modifies\nthe code to allow the patches to be applied only after all decisions have\nbeen made and session ends when this option is enabled.\n\nAbraham Samuel Adekunle (3):\n  interactive -p: add new `--rework-with-file` flag to interactive\n    machinery\n  add-patch: Allow interfile navigation when selecting hunks\n  add-patch: Allow proper 'git apply' when using the --rework-with-file\n    flag\n\n add-interactive.c     |   3 +\n add-interactive.h     |   5 +-\n add-patch.c           | 161 +++++++++++++++++++++++++++++++-----------\n builtin/add.c         |   4 ++\n builtin/checkout.c    |   6 ++\n builtin/reset.c       |   4 ++\n builtin/stash.c       |   8 +++\n t/t9902-completion.sh |   1 +\n 8 files changed, 148 insertions(+), 44 deletions(-)\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"535353","messageId":"c0fa65b429b4a5c33c4a2092e0e8d014a61e4569.1770390576.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1770390576.git.abrahamadekunle50@gmail.com","subject":"[PATCH v3 1/3] interactive -p: add new `--rework-with-file` flag to interactive machinery","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-06T15:54:47Z","receivedAt":"2026-02-06T15:54:47Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"When using the interactive add, reset, stash or checkout machinery, we do\nnot have the option of reworking with a file because the session automatically\nadvances to the next file or ends if we have just one file, immediately all hunks\nin a file are decided on.\n\nIntroduce the flag \"--rework-with-file\" when interactively selecting patches with the\n'--patch' option, which does not auto advance, thereby allowing users the option\nto rework with files.\nThis ensures the current auto-advance method stays as the default method.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-interactive.c     | 3 +++\n add-interactive.h     | 5 +++--\n builtin/add.c         | 4 ++++\n builtin/checkout.c    | 6 ++++++\n builtin/reset.c       | 4 ++++\n builtin/stash.c       | 8 ++++++++\n t/t9902-completion.sh | 1 +\n 7 files changed, 29 insertions(+), 2 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 68fc09547d..4eda115da8 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -64,6 +64,7 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,\n \ts->r = r;\n \ts->context = -1;\n \ts->interhunkcontext = -1;\n+\ts->no_auto_advance = 0;\n \n \ts->use_color_interactive = check_color_config(r, \"color.interactive\");\n \n@@ -124,6 +125,8 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,\n \t\t\tdie(_(\"%s cannot be negative\"), \"--inter-hunk-context\");\n \t\ts->interhunkcontext = add_p_opt->interhunkcontext;\n \t}\n+\tif (add_p_opt->no_auto_advance)\n+\t\ts->no_auto_advance = 1;\n }\n \n void clear_add_i_state(struct add_i_state *s)\ndiff --git a/add-interactive.h b/add-interactive.h\nindex da49502b76..aef2feca56 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -6,9 +6,10 @@\n struct add_p_opt {\n \tint context;\n \tint interhunkcontext;\n+\tint no_auto_advance;\n };\n \n-#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 }\n+#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1, .no_auto_advance = 0 }\n \n struct add_i_state {\n \tstruct repository *r;\n@@ -28,7 +29,7 @@ struct add_i_state {\n \n \tint use_single_key;\n \tchar *interactive_diff_filter, *interactive_diff_algorithm;\n-\tint context, interhunkcontext;\n+\tint context, interhunkcontext, no_auto_advance;\n };\n \n void init_add_i_state(struct add_i_state *s, struct repository *r,\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 32709794b3..408827cf54 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -256,6 +256,8 @@ static struct option builtin_add_options[] = {\n \tOPT_GROUP(\"\"),\n \tOPT_BOOL('i', \"interactive\", &add_interactive, N_(\"interactive picking\")),\n \tOPT_BOOL('p', \"patch\", &patch_interactive, N_(\"select hunks interactively\")),\n+\tOPT_BOOL(0, \"rework-with-file\", &add_p_opt.no_auto_advance,\n+\t\t N_(\"rework with files when selecting hunks interactively\")),\n \tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \tOPT_BOOL('e', \"edit\", &edit_interactive, N_(\"edit current diff and apply\")),\n@@ -418,6 +420,8 @@ int cmd_add(int argc,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--interactive/--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--interactive/--patch\");\n+\t\tif (add_p_opt.no_auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--rework-with-file\", \"--interactive/--patch\");\n \t}\n \n \tif (edit_interactive) {\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 261699e2f5..3e98d06be1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -63,6 +63,7 @@ struct checkout_opts {\n \tint patch_mode;\n \tint patch_context;\n \tint patch_interhunk_context;\n+\tint no_auto_advance;\n \tint quiet;\n \tint merge;\n \tint force;\n@@ -549,6 +550,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tstruct add_p_opt add_p_opt = {\n \t\t\t.context = opts->patch_context,\n \t\t\t.interhunkcontext = opts->patch_interhunk_context,\n+\t\t\t.no_auto_advance = opts->no_auto_advance\n \t\t};\n \t\tconst char *rev = new_branch_info->name;\n \t\tchar rev_oid[GIT_MAX_HEXSZ + 1];\n@@ -1747,6 +1749,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,\n \t\t\t      N_(\"checkout their version for unmerged files\"),\n \t\t\t      3, PARSE_OPT_NONEG),\n \t\tOPT_BOOL('p', \"patch\", &opts->patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"rework-with-file\", &opts->no_auto_advance,\n+\t\t\t N_(\"rework with files when selecting hunks interactively\")),\n \t\tOPT_DIFF_UNIFIED(&opts->patch_context),\n \t\tOPT_DIFF_INTERHUNK_CONTEXT(&opts->patch_interhunk_context),\n \t\tOPT_BOOL(0, \"ignore-skip-worktree-bits\", &opts->ignore_skipworktree,\n@@ -1801,6 +1805,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (opts->patch_interhunk_context != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (opts->no_auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--rework-with-file\", \"--patch\");\n \t}\n \n \tif (opts->show_progress < 0) {\ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex ed35802af1..1e7b93785d 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -371,6 +371,8 @@ int cmd_reset(int argc,\n \t\t\t       PARSE_OPT_OPTARG,\n \t\t\t       option_parse_recurse_submodules_worktree_updater),\n \t\tOPT_BOOL('p', \"patch\", &patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"rework-with-file\", &add_p_opt.no_auto_advance,\n+\t\t\t N_(\"rework with files when selecting hunks interactively\")),\n \t\tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \t\tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \t\tOPT_BOOL('N', \"intent-to-add\", &intent_to_add,\n@@ -443,6 +445,8 @@ int cmd_reset(int argc,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (add_p_opt.no_auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--rework-with-file\", \"--patch\");\n \t}\n \n \t/* git reset tree [--] paths... can be used to\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 948eba06fb..1311707ea6 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1849,6 +1849,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \t\t\t N_(\"stash staged changes only\")),\n \t\tOPT_BOOL('p', \"patch\", &patch_mode,\n \t\t\t N_(\"stash in patch mode\")),\n+\t\tOPT_BOOL(0, \"rework-with-file\", &add_p_opt.no_auto_advance,\n+\t\t\t N_(\"rework with files when selecting hunks interactively\")),\n \t\tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \t\tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n@@ -1911,6 +1913,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (add_p_opt.no_auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--rework-with-file\", \"--patch\");\n \t}\n \n \tif (add_p_opt.context < -1)\n@@ -1952,6 +1956,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,\n \t\t\t N_(\"stash staged changes only\")),\n \t\tOPT_BOOL('p', \"patch\", &patch_mode,\n \t\t\t N_(\"stash in patch mode\")),\n+\t\tOPT_BOOL(0, \"rework-with-file\", &add_p_opt.no_auto_advance,\n+\t\t\t N_(\"rework with files when selecting hunks interactively\")),\n \t\tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \t\tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n@@ -1983,6 +1989,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (add_p_opt.no_auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--rework-with-file\", \"--patch\");\n \t}\n \n \tret = do_push_stash(&ps, stash_msg, quiet, keep_index,\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex 964e1f1569..302534e92d 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2601,6 +2601,7 @@ test_expect_success 'double dash \"git checkout\"' '\n \t--ignore-skip-worktree-bits Z\n \t--ignore-other-worktrees Z\n \t--recurse-submodules Z\n+\t--rework-with-file Z\n \t--progress Z\n \t--guess Z\n \t--no-guess Z\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"535356","messageId":"24692afa3f0a67d3f3eba776cc745287c5d71e94.1770390576.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1770390576.git.abrahamadekunle50@gmail.com","subject":"[PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-06T15:56:14Z","receivedAt":"2026-02-06T15:56:05Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"After deciding on all hunks in a file, the interactive session\nadvances automatically to the next file if there is another,\nor the process ends.\n\nNow using the `--rework-with-file` flag with `--patch` the process does not\nadvance automatically. A user can choose to go to the next file by pressing\n'>' or the previous file by pressing '<', before or after deciding on all\nhunks in the current file.\n\nAfter all hunks have been decided in a file, a prompt appears,\nwhich allow the user to still rework with the file by applying\nthe options available in the permit set for that hunk, and\nafter all the decisions, the user presses 'q' to submit.\n\nThis feature is enabled by passing the `--rework-with-file` flag\nto `--patch` option of the subcommands add, stash, reset,\nand checkout\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-patch.c | 95 ++++++++++++++++++++++++++++++++++++++++++++---------\n 1 file changed, 80 insertions(+), 15 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 173a53241e..2bd839f17e 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1418,6 +1418,8 @@ N_(\"j - go to the next undecided hunk, roll over at the bottom\\n\"\n    \"e - manually edit the current hunk\\n\"\n    \"p - print the current hunk\\n\"\n    \"P - print the current hunk using the pager\\n\"\n+   \"> - go to the next file\\n\"\n+   \"< - go to the previous file\\n\"\n    \"? - print help\\n\");\n \n static size_t dec_mod(size_t a, size_t m)\n@@ -1430,6 +1432,12 @@ static size_t inc_mod(size_t a, size_t m)\n \treturn a < m - 1 ? a + 1 : 0;\n }\n \n+enum patch_update_response {\n+\tNEXT_FILE = 0,\n+\tQUIT,\n+\tPREVIOUS_FILE,\n+};\n+\n static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)\n {\n \tfor (size_t i = 0; i < file_diff->hunk_nr; i++) {\n@@ -1441,7 +1449,7 @@ static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)\n \treturn false;\n }\n \n-static int patch_update_file(struct add_p_state *s,\n+static enum patch_update_response patch_update_file(struct add_p_state *s,\n \t\t\t     struct file_diff *file_diff)\n {\n \tsize_t hunk_index = 0;\n@@ -1449,12 +1457,14 @@ static int patch_update_file(struct add_p_state *s,\n \tstruct hunk *hunk;\n \tchar ch;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tint colored = !!s->colored.len, quit = 0, use_pager = 0;\n+\tint colored = !!s->colored.len, use_pager = 0;\n \tenum prompt_mode_type prompt_mode_type;\n+\tint all_decided = 0;\n+\tenum patch_update_response ret = NEXT_FILE;\n \n \t/* Empty added files have no hunks */\n \tif (!file_diff->hunk_nr && !file_diff->added)\n-\t\treturn 0;\n+\t\treturn NEXT_FILE;\n \n \tstrbuf_reset(&s->buf);\n \trender_diff_header(s, file_diff, colored, &s->buf);\n@@ -1467,7 +1477,9 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\tALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,\n \t\t\tALLOW_SEARCH_AND_GOTO = 1 << 4,\n \t\t\tALLOW_SPLIT = 1 << 5,\n-\t\t\tALLOW_EDIT = 1 << 6\n+\t\t\tALLOW_EDIT = 1 << 6,\n+\t\t\tALLOW_GOTO_PREVIOUS_FILE = 1 << 7,\n+\t\t\tALLOW_GOTO_NEXT_FILE = 1 << 8\n \t\t} permitted = 0;\n \n \t\tif (hunk_index >= file_diff->hunk_nr)\n@@ -1498,9 +1510,12 @@ static int patch_update_file(struct add_p_state *s,\n \n \t\t/* Everything decided? */\n \t\tif (undecided_previous < 0 && undecided_next < 0 &&\n-\t\t    hunk->use != UNDECIDED_HUNK)\n-\t\t\tbreak;\n-\n+\t\t    hunk->use != UNDECIDED_HUNK) {\n+\t\t\t\tif (s->s.no_auto_advance)\n+\t\t\t\t\tall_decided = 1;\n+\t\t\t\telse\n+\t\t\t\t\tbreak;\n+\t\t\t}\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n \t\t\tif (rendered_hunk_index != hunk_index) {\n@@ -1548,6 +1563,14 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\tpermitted |= ALLOW_EDIT;\n \t\t\t\tstrbuf_addstr(&s->buf, \",e\");\n \t\t\t}\n+\t\t\tif (s->s.no_auto_advance && s->file_diff_nr > 1) {\n+\t\t\t\tpermitted |= ALLOW_GOTO_NEXT_FILE;\n+\t\t\t\tstrbuf_addstr(&s->buf, \",>\");\n+\t\t\t}\n+\t\t\tif (s->s.no_auto_advance && s->file_diff_nr > 1) {\n+\t\t\t\tpermitted |= ALLOW_GOTO_PREVIOUS_FILE;\n+\t\t\t\tstrbuf_addstr(&s->buf, \",<\");\n+\t\t\t}\n \t\t\tstrbuf_addstr(&s->buf, \",p,P\");\n \t\t}\n \t\tif (file_diff->deleted)\n@@ -1566,11 +1589,14 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\t\t\t: 1));\n \t\tprintf(_(s->mode->prompt_mode[prompt_mode_type]),\n \t\t       s->buf.buf);\n+\t\tif (s->s.no_auto_advance && all_decided)\n+\t\t\tprintf(_(\"\\n%s All hunks decided. What now? \"),\n+\t\t\t\ts->s.prompt_color);\n \t\tif (*s->s.reset_color_interactive)\n \t\t\tfputs(s->s.reset_color_interactive, stdout);\n \t\tfflush(stdout);\n \t\tif (read_single_character(s) == EOF) {\n-\t\t\tquit = 1;\n+\t\t\tret = QUIT;\n \t\t\tbreak;\n \t\t}\n \n@@ -1616,9 +1642,26 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\thunk->use = SKIP_HUNK;\n \t\t\t}\n \t\t} else if (ch == 'q') {\n-\t\t\tquit = 1;\n+\t\t\tret = QUIT;\n \t\t\tbreak;\n-\t\t} else if (s->answer.buf[0] == 'K') {\n+\t\t} else if (s->s.no_auto_advance && s->answer.buf[0] == '>') {\n+\t\t\tif (permitted & ALLOW_GOTO_NEXT_FILE) {\n+\t\t\t\tret = NEXT_FILE;\n+\t\t\t\tbreak;\n+\t\t\t} else {\n+\t\t\t\terr(s, _(\"No next file\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t} else if (s->s.no_auto_advance && s->answer.buf[0] == '<') {\n+\t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_FILE) {\n+\t\t\t\tret = PREVIOUS_FILE;\n+\t\t\t\tbreak;\n+\t\t\t} else {\n+\t\t\t\terr(s, _(\"No previous file\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\t\telse if (s->answer.buf[0] == 'K') {\n \t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n \t\t\t\thunk_index = dec_mod(hunk_index,\n \t\t\t\t\t\t     file_diff->hunk_nr);\n@@ -1803,7 +1846,7 @@ static int patch_update_file(struct add_p_state *s,\n \t}\n \n \tputchar('\\n');\n-\treturn quit;\n+\treturn ret;\n }\n \n int run_add_p(struct repository *r, enum add_p_mode mode,\n@@ -1814,6 +1857,7 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\t{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT\n \t};\n \tsize_t i, binary_count = 0;\n+\tenum patch_update_response ret;\n \n \tinit_add_i_state(&s.s, r, o);\n \n@@ -1852,11 +1896,32 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\treturn -1;\n \t}\n \n-\tfor (i = 0; i < s.file_diff_nr; i++)\n-\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)\n+\tfor (i = 0; i < s.file_diff_nr;) {\n+\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {\n \t\t\tbinary_count++;\n-\t\telse if (patch_update_file(&s, s.file_diff + i))\n-\t\t\tbreak;\n+\t\t\ti++;\n+\t\t\tcontinue;\n+\t\t}\n+\t\telse {\n+\t\t\tret = patch_update_file(&s, s.file_diff + i);\n+\t\t\tif (ret == NEXT_FILE) {\n+\t\t\t\tif (s.s.no_auto_advance && i == s.file_diff_nr - 1)\n+\t\t\t\t\ti = 0;\n+\t\t\t\telse\n+\t\t\t\t\ti++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (ret == QUIT)\n+\t\t\t\tbreak;\n+\t\t\tif (s.s.no_auto_advance && ret == PREVIOUS_FILE) {\n+\t\t\t\tif (i == 0)\n+\t\t\t\t\ti = s.file_diff_nr - 1;\n+\t\t\t\telse\n+\t\t\t\t\ti--;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+    }\n \n \tif (s.file_diff_nr == 0)\n \t\terr(&s, _(\"No changes.\"));\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"535357","messageId":"10c0a4cb36534f5ed1ebed783b37d03a56007f97.1770390576.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1770390576.git.abrahamadekunle50@gmail.com","subject":"[PATCH v3 3/3] add-patch: Allow proper 'git apply' when using the --rework-with-file flag","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-06T15:57:35Z","receivedAt":"2026-02-06T15:57:28Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"When the flag `--rework-with-file` is used with `--patch`, if the user\nhas decided `USE` on a hunk in a file, goes to another file, and then\nreturns to this file and changes the previous decision on the hunk to\n`SKIP`, because the patch has already been applied, the last decision\nis not registered and the now SKIPPED hunk is still applied.\n\nModify the logic to allow all files to be applied only after the user\nhas finished making hunk decisions and quits. This will ensure the last\ndecision of the user is applied regardless of how the user navigates back\nand forth and decides.\n\nThis change does not affect the default behaviour of applying the auto\nadvancing after deciding on all hunks in a file.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-patch.c | 66 +++++++++++++++++++++++++++++++----------------------\n 1 file changed, 39 insertions(+), 27 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 2bd839f17e..9fb33715c2 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1422,6 +1422,40 @@ N_(\"j - go to the next undecided hunk, roll over at the bottom\\n\"\n    \"< - go to the previous file\\n\"\n    \"? - print help\\n\");\n \n+static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tsize_t j;\n+\n+\t\t/* Any hunk to be used? */\n+\tfor (j = 0; j < file_diff->hunk_nr; j++)\n+\t\tif (file_diff->hunk[j].use == USE_HUNK)\n+\t\t\tbreak;\n+\n+\tif (j < file_diff->hunk_nr ||\n+\t\t(!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n+\t\t/* At least one hunk selected: apply */\n+\t\tstrbuf_reset(&s->buf);\n+\t\treassemble_patch(s, file_diff, 0, &s->buf);\n+\n+\t\tdiscard_index(s->s.r->index);\n+\t\tif (s->mode->apply_for_checkout)\n+\t\t\tapply_for_checkout(s, &s->buf,\n+\t\t\t\t\ts->mode->is_reverse);\n+\t\telse {\n+\t\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n+\t\t\tstrvec_pushv(&cp.args, s->mode->apply_args);\n+\t\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n+\t\t\t\t\tNULL, 0, NULL, 0))\n+\t\t\t\terror(_(\"'git apply' failed\"));\n+\t\t}\n+\t\tif (repo_read_index(s->s.r) >= 0)\n+\t\t\trepo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n+\t\t\t\t\t\t\t1, NULL, NULL, NULL);\n+\t}\n+\n+}\n+\n static size_t dec_mod(size_t a, size_t m)\n {\n \treturn a > 0 ? a - 1 : m - 1;\n@@ -1456,7 +1490,6 @@ static enum patch_update_response patch_update_file(struct add_p_state *s,\n \tssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n \tstruct hunk *hunk;\n \tchar ch;\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint colored = !!s->colored.len, use_pager = 0;\n \tenum prompt_mode_type prompt_mode_type;\n \tint all_decided = 0;\n@@ -1818,32 +1851,8 @@ static enum patch_update_response patch_update_file(struct add_p_state *s,\n \t\t}\n \t}\n \n-\t/* Any hunk to be used? */\n-\tfor (i = 0; i < file_diff->hunk_nr; i++)\n-\t\tif (file_diff->hunk[i].use == USE_HUNK)\n-\t\t\tbreak;\n-\n-\tif (i < file_diff->hunk_nr ||\n-\t    (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n-\t\t/* At least one hunk selected: apply */\n-\t\tstrbuf_reset(&s->buf);\n-\t\treassemble_patch(s, file_diff, 0, &s->buf);\n-\n-\t\tdiscard_index(s->s.r->index);\n-\t\tif (s->mode->apply_for_checkout)\n-\t\t\tapply_for_checkout(s, &s->buf,\n-\t\t\t\t\t   s->mode->is_reverse);\n-\t\telse {\n-\t\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n-\t\t\tstrvec_pushv(&cp.args, s->mode->apply_args);\n-\t\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n-\t\t\t\t\t NULL, 0, NULL, 0))\n-\t\t\t\terror(_(\"'git apply' failed\"));\n-\t\t}\n-\t\tif (repo_read_index(s->s.r) >= 0)\n-\t\t\trepo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n-\t\t\t\t\t\t     1, NULL, NULL, NULL);\n-\t}\n+\tif (!s->s.no_auto_advance)\n+\t\tapply_patch(s, file_diff);\n \n \tputchar('\\n');\n \treturn ret;\n@@ -1922,6 +1931,9 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\t\t}\n \t\t}\n     }\n+\tfor (i = 0; i < s.file_diff_nr; i++)\n+\t\tif (s.s.no_auto_advance)\n+\t\t\tapply_patch(&s, s.file_diff + i);\n \n \tif (s.file_diff_nr == 0)\n \t\terr(&s, _(\"No changes.\"));\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"535376","messageId":"xmqq8qd5g25o.fsf@gitster.g","threadId":"64857","inReplyTo":"c0fa65b429b4a5c33c4a2092e0e8d014a61e4569.1770390576.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v3 1/3] interactive -p: add new `--rework-with-file` flag to interactive machinery","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-06T18:25:23Z","receivedAt":"2026-02-06T18:25:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> When using the interactive add, reset, stash or checkout machinery, we do\n> not have the option of reworking with a file because the session automatically\n> advances to the next file or ends if we have just one file, immediately all hunks\n> in a file are decided on.\n\nThe last part of the sentence after the last comma does not read\nvery well, at least to me.\n\nWe recommend to fold lines in such a way that after a few e-mail\nexchange and quoting it will still stay within 80-column, so a\npractical fill-column value lies somewhere around ~70.  Your lines a\nslightly longer.\n\n> Introduce the flag \"--rework-with-file\" when interactively selecting patches with the\n> '--patch' option, which does not auto advance, thereby allowing users the option\n> to rework with files.\n> This ensures the current auto-advance method stays as the default method.\n\nOK.  There may be suggestions for better option names from others; I\ndo not think of any right now.\n\n> diff --git a/add-interactive.h b/add-interactive.h\n> index da49502b76..aef2feca56 100644\n> --- a/add-interactive.h\n> +++ b/add-interactive.h\n> @@ -6,9 +6,10 @@\n>  struct add_p_opt {\n>  \tint context;\n>  \tint interhunkcontext;\n> +\tint no_auto_advance;\n>  };\n\nWe add a new risk of double-negation confusion, e.g.,\n\n    if (!opt->no_auto_advance)\n\t... do the auto-advance thing ...\n\nwhere it may be easier to follow if it were written\n\n    if (opt->auto_advance)\n\t... do the auto-advance thing ...\n\nWould it make it harder to arrange the code if we made this member\n\"auto_advance\" that defaults to \"true\"?  We have ADD_P_OPT_INIT that\neverybody is supposed to call already, like this\n\n> -#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 }\n> +#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1, .no_auto_advance = 0 }\n\nso I do not imagine it would be too much hassle.\n\n> @@ -28,7 +29,7 @@ struct add_i_state {\n>  \n>  \tint use_single_key;\n>  \tchar *interactive_diff_filter, *interactive_diff_algorithm;\n> -\tint context, interhunkcontext;\n> +\tint context, interhunkcontext, no_auto_advance;\n>  };\n\nLikewise.\n\n> diff --git a/builtin/add.c b/builtin/add.c\n> index 32709794b3..408827cf54 100644\n> --- a/builtin/add.c\n> +++ b/builtin/add.c\n> @@ -256,6 +256,8 @@ static struct option builtin_add_options[] = {\n>  \tOPT_GROUP(\"\"),\n>  \tOPT_BOOL('i', \"interactive\", &add_interactive, N_(\"interactive picking\")),\n>  \tOPT_BOOL('p', \"patch\", &patch_interactive, N_(\"select hunks interactively\")),\n> +\tOPT_BOOL(0, \"rework-with-file\", &add_p_opt.no_auto_advance,\n> +\t\t N_(\"rework with files when selecting hunks interactively\")),\n\nLikewise.\n"},{"id":"535377","messageId":"xmqq4intg1o7.fsf@gitster.g","threadId":"64857","inReplyTo":"24692afa3f0a67d3f3eba776cc745287c5d71e94.1770390576.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-06T18:35:52Z","receivedAt":"2026-02-06T18:35:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> -\t\t} else if (s->answer.buf[0] == 'K') {\n> +\t\t} else if (s->s.no_auto_advance && s->answer.buf[0] == '>') {\n> +\t\t\tif (permitted & ALLOW_GOTO_NEXT_FILE) {\n> +\t\t\t\tret = NEXT_FILE;\n> +...\n> +\t\t\t\tcontinue;\n> +\t\t\t}\n> +\t\t}\n> +\t\telse if (s->answer.buf[0] == 'K') {\n\nThis funny-looking diff is a sign that the coding guideline was\nfollowed in the preimage but not in the postimage, by splitting\nthe \"} else if (condition) {\" into two lines for 'K'.\n"},{"id":"535379","messageId":"xmqqwm0pem83.fsf@gitster.g","threadId":"64857","inReplyTo":"24692afa3f0a67d3f3eba776cc745287c5d71e94.1770390576.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-06T18:54:52Z","receivedAt":"2026-02-06T18:54:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> +\tfor (i = 0; i < s.file_diff_nr;) {\n> +\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {\n>  \t\t\tbinary_count++;\n> +\t\t\ti++;\n> +\t\t\tcontinue;\n> +\t\t}\n\nThis \"continue\" is a commonly seen good trick to avoid having the\nnesting go too deep.  As we know the case where the condition holds\nhave already been dealt with and moved to the next iteration at this\npoint, we can ...\n\n> +\t\telse {\n\n... omit this extra \"else\" block and write what is inside for\neverybody (not just \"those who did not pass the if condition above\",\nwhich is what \"else\" tells us).\n\n> +\t\t\tret = patch_update_file(&s, s.file_diff + i);\n> +\t\t\tif (ret == NEXT_FILE) {\n> +\t\t\t\tif (s.s.no_auto_advance && i == s.file_diff_nr - 1)\n> +\t\t\t\t\ti = 0;\n> +\t\t\t\telse\n> +\t\t\t\t\ti++;\n> +\t\t\t\tcontinue;\n> +\t\t\t}\n> +\t\t\tif (ret == QUIT)\n> +\t\t\t\tbreak;\n> +\t\t\tif (s.s.no_auto_advance && ret == PREVIOUS_FILE) {\n> +\t\t\t\tif (i == 0)\n> +\t\t\t\t\ti = s.file_diff_nr - 1;\n> +\t\t\t\telse\n> +\t\t\t\t\ti--;\n> +\t\t\t\tcontinue;\n> +\t\t\t}\n\nThe asymmetry between next/quit and prev feels curious.\n\nThe patch_update_file() helper returns QUIT when the user tells us\nto (regardless of auto-advance setting), PREVIOUS when '<' is given\nbut that is only possible with auto-advance disabled, and NEXT in\nall other cases.  The check inside the NEXT case for auto-advance is\nto decide if we want to overflow 'i' beyond file_diff_nr to complete\nthe session, or we want to wrap-around back to the first file.\n\nBut ret can be PREV only under auto-advance disabled, so the check\nthere feels totally redundant.\n\nAnd we want to treat the list of files as a ring buffer only when\nauto-advance is set to false.  This may work in practice but the\nlogic feels convoluted.  \n\nThe patch_update_file() knows how many files there are to decide if\nwe want to offer '<' and '>'.  It also knows the file index within\nthe file_diff_nr for the file it is handling.  I wonder if it should\ndo a bit more with its return value to help the caller?  E.g.,\nperhaps it can return the next 'i' if it wants the caller to advance\n(and decide to do the ring-buffer if needed), or if it wants to tell\nthe caller that everything is done by returning some sentinel value\n(e.g., -1)?  Then this part of the caller can just be\n\n\tif ((i = patch_update_file(&s, i)) < 0)\n\t\tbreak; /* all done */\n\nperhaps?\n\nBy the way, I just noticed that the new local variable you added to\npatch_update_file() is called \"ret\" but that is hiding a different\nvariable \"int ret\" that is used to handle the '/' command.  It\nshould be renamed to avoid the name collision.\n\n"},{"id":"535380","messageId":"xmqqqzqxelw5.fsf@gitster.g","threadId":"64857","inReplyTo":"10c0a4cb36534f5ed1ebed783b37d03a56007f97.1770390576.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v3 3/3] add-patch: Allow proper 'git apply' when using the --rework-with-file flag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-06T19:02:02Z","receivedAt":"2026-02-06T19:02:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> Subject: Re: [PATCH v3 3/3] add-patch: Allow proper 'git apply' when using the --rework-with-file flag\n\nStyle.  Downcase \"Allow\".  Applies to [2/3].\n\nAvoid \"proper\" as it is not obvious to everybody what you find\nproper and why you find it proper.  Applies to any value-judgement\nadjective.\n\n    Subject: [PATCH v3 3/3] add-patch: allow all-or-none application of a patch\n\nor something?\n\n> +static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)\n> +{\n> +\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\tsize_t j;\n> +\n> +\t\t/* Any hunk to be used? */\n\nFunny indentaion?\n\n> +\tfor (j = 0; j < file_diff->hunk_nr; j++)\n> +\t\tif (file_diff->hunk[j].use == USE_HUNK)\n> +\t\t\tbreak;\n> +\n> +\tif (j < file_diff->hunk_nr ||\n> +\t\t(!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n> +\t\t/* At least one hunk selected: apply */\n> +\t\tstrbuf_reset(&s->buf);\n> +\t\treassemble_patch(s, file_diff, 0, &s->buf);\n> +\n> +\t\tdiscard_index(s->s.r->index);\n> +\t\tif (s->mode->apply_for_checkout)\n> +\t\t\tapply_for_checkout(s, &s->buf,\n> +\t\t\t\t\ts->mode->is_reverse);\n> +\t\telse {\n> +\t\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n> +\t\t\tstrvec_pushv(&cp.args, s->mode->apply_args);\n> +\t\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n> +\t\t\t\t\tNULL, 0, NULL, 0))\n> +\t\t\t\terror(_(\"'git apply' failed\"));\n> +\t\t}\n> +\t\tif (repo_read_index(s->s.r) >= 0)\n> +\t\t\trepo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n> +\t\t\t\t\t\t\t1, NULL, NULL, NULL);\n> +\t}\n> +\n> +}\n\nI suspect that the extraction of this helper function out of its\noriginal place in patch_update_file() should be done in its own\npatch.\n\nDo we need new tests to cover this new feature?\n"},{"id":"535384","messageId":"xmqqms1lel34.fsf@gitster.g","threadId":"64857","inReplyTo":"cover.1770390576.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v3 0/3] introduce new option `rework-with-file`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-06T19:19:27Z","receivedAt":"2026-02-06T19:19:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> Abraham Samuel Adekunle (3):\n>   interactive -p: add new `--rework-with-file` flag to interactive\n>     machinery\n>   add-patch: Allow interfile navigation when selecting hunks\n>   add-patch: Allow proper 'git apply' when using the --rework-with-file\n>     flag\n\nBy the way, this series is conflicting with your other series that\nhas been in 'next' and is ready to graduate.  Perhaps it is time to\nconsider rebasing.\n"},{"id":"535385","messageId":"xmqqikc9ekzz.fsf@gitster.g","threadId":"64857","inReplyTo":"24692afa3f0a67d3f3eba776cc745287c5d71e94.1770390576.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-06T19:21:20Z","receivedAt":"2026-02-06T19:21:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> @@ -1566,11 +1589,14 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\t\t\t\t: 1));\n>  \t\tprintf(_(s->mode->prompt_mode[prompt_mode_type]),\n>  \t\t       s->buf.buf);\n> +\t\tif (s->s.no_auto_advance && all_decided)\n> +\t\t\tprintf(_(\"\\n%s All hunks decided. What now? \"),\n> +\t\t\t\ts->s.prompt_color);\n\nThis gives an ordinary prompt for the hunk and then another one\nafter it if we notice everything has been decided.  I am wondering\nif it wants to be more like\n\n\tif (!s->auto_advance && all_decided)\n\t\tsay What now?\n\telse\n\t\task the usual\n\n?\n"},{"id":"535389","messageId":"CADYq+fYMDyxgqe=CNJ7yxQZKsJJju=MDDBSXpuqmANoJBoWMyA@mail.gmail.com","threadId":"64857","inReplyTo":"xmqq8qd5g25o.fsf@gitster.g","subject":"Re: [PATCH v3 1/3] interactive -p: add new `--rework-with-file` flag to interactive machinery","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-06T20:21:10Z","receivedAt":"2026-02-06T20:21:09Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Fri, Feb 6, 2026 at 7:25 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > When using the interactive add, reset, stash or checkout machinery, we do\n> > not have the option of reworking with a file because the session automatically\n> > advances to the next file or ends if we have just one file, immediately all hunks\n> > in a file are decided on.\n>\n> The last part of the sentence after the last comma does not read\n> very well, at least to me.\n\nOkay I will reword it.\n\n>\n> We recommend to fold lines in such a way that after a few e-mail\n> exchange and quoting it will still stay within 80-column, so a\n> practical fill-column value lies somewhere around ~70.  Your lines a\n> slightly longer.\n\nOkay thank you.\nI will watch out for this.\n\n>\n> > Introduce the flag \"--rework-with-file\" when interactively selecting patches with the\n> > '--patch' option, which does not auto advance, thereby allowing users the option\n> > to rework with files.\n> > This ensures the current auto-advance method stays as the default method.\n>\n> OK.  There may be suggestions for better option names from others; I\n> do not think of any right now.\n\nOkay\n\n>\n> > diff --git a/add-interactive.h b/add-interactive.h\n> > index da49502b76..aef2feca56 100644\n> > --- a/add-interactive.h\n> > +++ b/add-interactive.h\n> > @@ -6,9 +6,10 @@\n> >  struct add_p_opt {\n> >       int context;\n> >       int interhunkcontext;\n> > +     int no_auto_advance;\n> >  };\n>\n> We add a new risk of double-negation confusion, e.g.,\n>\n>     if (!opt->no_auto_advance)\n>         ... do the auto-advance thing ...\n>\n> where it may be easier to follow if it were written\n>\n>     if (opt->auto_advance)\n>         ... do the auto-advance thing ...\n>\n> Would it make it harder to arrange the code if we made this member\n> \"auto_advance\" that defaults to \"true\"?  We have ADD_P_OPT_INIT that\n> everybody is supposed to call already, like this\n>\n> > -#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 }\n> > +#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1, .no_auto_advance = 0 }\n>\n> so I do not imagine it would be too much hassle.\n\nNo it won't.\n\n>\n> > @@ -28,7 +29,7 @@ struct add_i_state {\n> >\n> >       int use_single_key;\n> >       char *interactive_diff_filter, *interactive_diff_algorithm;\n> > -     int context, interhunkcontext;\n> > +     int context, interhunkcontext, no_auto_advance;\n> >  };\n>\n> Likewise.\n\nNoted.\n\n>\n> > diff --git a/builtin/add.c b/builtin/add.c\n> > index 32709794b3..408827cf54 100644\n> > --- a/builtin/add.c\n> > +++ b/builtin/add.c\n> > @@ -256,6 +256,8 @@ static struct option builtin_add_options[] = {\n> >       OPT_GROUP(\"\"),\n> >       OPT_BOOL('i', \"interactive\", &add_interactive, N_(\"interactive picking\")),\n> >       OPT_BOOL('p', \"patch\", &patch_interactive, N_(\"select hunks interactively\")),\n> > +     OPT_BOOL(0, \"rework-with-file\", &add_p_opt.no_auto_advance,\n> > +              N_(\"rework with files when selecting hunks interactively\")),\n>\n> Likewise.\n\nThanks.\n\nAbraham\n"},{"id":"535390","messageId":"CADYq+fazKnt5KGZdHo+FJODRE2mfXsa1_R13w-zMgcW7Dpi8Gg@mail.gmail.com","threadId":"64857","inReplyTo":"xmqq4intg1o7.fsf@gitster.g","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-06T20:22:35Z","receivedAt":"2026-02-06T20:22:38Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Fri, Feb 6, 2026 at 7:35 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > -             } else if (s->answer.buf[0] == 'K') {\n> > +             } else if (s->s.no_auto_advance && s->answer.buf[0] == '>') {\n> > +                     if (permitted & ALLOW_GOTO_NEXT_FILE) {\n> > +                             ret = NEXT_FILE;\n> > +...\n> > +                             continue;\n> > +                     }\n> > +             }\n> > +             else if (s->answer.buf[0] == 'K') {\n>\n> This funny-looking diff is a sign that the coding guideline was\n> followed in the preimage but not in the postimage, by splitting\n> the \"} else if (condition) {\" into two lines for 'K'.\n\nSorry, I will fix it\n"},{"id":"535391","messageId":"CADYq+famEeYR4qRBMAVsdjOCDj0ccOgXRUA_SGX0VuUhNDEaFA@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqwm0pem83.fsf@gitster.g","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-06T20:32:59Z","receivedAt":"2026-02-06T20:32:59Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Fri, Feb 6, 2026 at 7:54 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > +     for (i = 0; i < s.file_diff_nr;) {\n> > +             if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {\n> >                       binary_count++;\n> > +                     i++;\n> > +                     continue;\n> > +             }\n>\n> This \"continue\" is a commonly seen good trick to avoid having the\n> nesting go too deep.  As we know the case where the condition holds\n> have already been dealt with and moved to the next iteration at this\n> point, we can ...\n>\n> > +             else {\n>\n> ... omit this extra \"else\" block and write what is inside for\n> everybody (not just \"those who did not pass the if condition above\",\n> which is what \"else\" tells us).\n\nYes.\n\n>\n> > +                     ret = patch_update_file(&s, s.file_diff + i);\n> > +                     if (ret == NEXT_FILE) {\n> > +                             if (s.s.no_auto_advance && i == s.file_diff_nr - 1)\n> > +                                     i = 0;\n> > +                             else\n> > +                                     i++;\n> > +                             continue;\n> > +                     }\n> > +                     if (ret == QUIT)\n> > +                             break;\n> > +                     if (s.s.no_auto_advance && ret == PREVIOUS_FILE) {\n> > +                             if (i == 0)\n> > +                                     i = s.file_diff_nr - 1;\n> > +                             else\n> > +                                     i--;\n> > +                             continue;\n> > +                     }\n>\n> The asymmetry between next/quit and prev feels curious.\n>\n> The patch_update_file() helper returns QUIT when the user tells us\n> to (regardless of auto-advance setting), PREVIOUS when '<' is given\n> but that is only possible with auto-advance disabled, and NEXT in\n> all other cases.  The check inside the NEXT case for auto-advance is\n> to decide if we want to overflow 'i' beyond file_diff_nr to complete\n> the session, or we want to wrap-around back to the first file.\n>\n> But ret can be PREV only under auto-advance disabled, so the check\n> there feels totally redundant.\n>\n> And we want to treat the list of files as a ring buffer only when\n> auto-advance is set to false.  This may work in practice but the\n> logic feels convoluted.\n>\n> The patch_update_file() knows how many files there are to decide if\n> we want to offer '<' and '>'.  It also knows the file index within\n> the file_diff_nr for the file it is handling.  I wonder if it should\n> do a bit more with its return value to help the caller?  E.g.,\n> perhaps it can return the next 'i' if it wants the caller to advance\n> (and decide to do the ring-buffer if needed), or if it wants to tell\n> the caller that everything is done by returning some sentinel value\n> (e.g., -1)?  Then this part of the caller can just be\n>\n>         if ((i = patch_update_file(&s, i)) < 0)\n>                 break; /* all done */\n>\n> perhaps?\n\nYes this makes sense.\nThank you\n\n>\n> By the way, I just noticed that the new local variable you added to\n> patch_update_file() is called \"ret\" but that is hiding a different\n> variable \"int ret\" that is used to handle the '/' command.  It\n> should be renamed to avoid the name collision.\n>\n\nOkay thank you\n\nAbraham\n"},{"id":"535392","messageId":"CADYq+fYzJS+=DVBAAEbaHjp=sRBxsxZqQCkVMiJhJPSSpUqu7A@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqikc9ekzz.fsf@gitster.g","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-06T20:37:08Z","receivedAt":"2026-02-06T20:37:08Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Fri, Feb 6, 2026 at 8:21 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > @@ -1566,11 +1589,14 @@ static int patch_update_file(struct add_p_state *s,\n> >                                               : 1));\n> >               printf(_(s->mode->prompt_mode[prompt_mode_type]),\n> >                      s->buf.buf);\n> > +             if (s->s.no_auto_advance && all_decided)\n> > +                     printf(_(\"\\n%s All hunks decided. What now? \"),\n> > +                             s->s.prompt_color);\n>\n> This gives an ordinary prompt for the hunk and then another one\n> after it if we notice everything has been decided.  I am wondering\n> if it wants to be more like\n>\n>         if (!s->auto_advance && all_decided)\n>                 say What now?\n>         else\n>                 ask the usual\n>\n> ?\n\nOkay noted\nThank you\n\nAbraham\n"},{"id":"535393","messageId":"CADYq+fb3zP0KiPSGGnnbHsX86wc0fWGjbv8s4BNaKPsO+T7znA@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqqzqxelw5.fsf@gitster.g","subject":"Re: [PATCH v3 3/3] add-patch: Allow proper 'git apply' when using the --rework-with-file flag","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-06T20:39:19Z","receivedAt":"2026-02-06T20:39:18Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Fri, Feb 6, 2026 at 8:02 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > Subject: Re: [PATCH v3 3/3] add-patch: Allow proper 'git apply' when using the --rework-with-file flag\n>\n> Style.  Downcase \"Allow\".  Applies to [2/3].\n>\n> Avoid \"proper\" as it is not obvious to everybody what you find\n> proper and why you find it proper.  Applies to any value-judgement\n> adjective.\n>\n>     Subject: [PATCH v3 3/3] add-patch: allow all-or-none application of a patch\n>\n> or something?\n\nOkay\n\n>\n> > +static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)\n> > +{\n> > +     struct child_process cp = CHILD_PROCESS_INIT;\n> > +     size_t j;\n> > +\n> > +             /* Any hunk to be used? */\n>\n> Funny indentaion?\n\nSorry\n\n>\n> > +     for (j = 0; j < file_diff->hunk_nr; j++)\n> > +             if (file_diff->hunk[j].use == USE_HUNK)\n> > +                     break;\n> > +\n> > +     if (j < file_diff->hunk_nr ||\n> > +             (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n> > +             /* At least one hunk selected: apply */\n> > +             strbuf_reset(&s->buf);\n> > +             reassemble_patch(s, file_diff, 0, &s->buf);\n> > +\n> > +             discard_index(s->s.r->index);\n> > +             if (s->mode->apply_for_checkout)\n> > +                     apply_for_checkout(s, &s->buf,\n> > +                                     s->mode->is_reverse);\n> > +             else {\n> > +                     setup_child_process(s, &cp, \"apply\", NULL);\n> > +                     strvec_pushv(&cp.args, s->mode->apply_args);\n> > +                     if (pipe_command(&cp, s->buf.buf, s->buf.len,\n> > +                                     NULL, 0, NULL, 0))\n> > +                             error(_(\"'git apply' failed\"));\n> > +             }\n> > +             if (repo_read_index(s->s.r) >= 0)\n> > +                     repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n> > +                                                     1, NULL, NULL, NULL);\n> > +     }\n> > +\n> > +}\n>\n> I suspect that the extraction of this helper function out of its\n> original place in patch_update_file() should be done in its own\n> patch.\n\nOkay\n\n>\n> Do we need new tests to cover this new feature?\n\nYes I will include the tests in the next version.\nThanks\n\nAbraham\n"},{"id":"535394","messageId":"CADYq+fZGnJ0LFfTz3VZbRPfuecZ_i3h5oLn0UfiybwzRAXyzdw@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqms1lel34.fsf@gitster.g","subject":"Re: [PATCH v3 0/3] introduce new option `rework-with-file`","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-06T20:40:51Z","receivedAt":"2026-02-06T20:40:51Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Fri, Feb 6, 2026 at 8:19 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > Abraham Samuel Adekunle (3):\n> >   interactive -p: add new `--rework-with-file` flag to interactive\n> >     machinery\n> >   add-patch: Allow interfile navigation when selecting hunks\n> >   add-patch: Allow proper 'git apply' when using the --rework-with-file\n> >     flag\n>\n> By the way, this series is conflicting with your other series that\n> has been in 'next' and is ready to graduate.  Perhaps it is time to\n> consider rebasing.\n\n:D\nOkay thank you very much.\n\nAbraham\n"},{"id":"535854","messageId":"CADYq+fa81Uki0ZVta80VO=-UG-f+Z8GAyzom-FLNXULartwwXA@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqikc9ekzz.fsf@gitster.g","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-12T10:32:29Z","receivedAt":"2026-02-12T10:32:28Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Fri, Feb 6, 2026 at 8:21 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > @@ -1566,11 +1589,14 @@ static int patch_update_file(struct add_p_state *s,\n> >                                               : 1));\n> >               printf(_(s->mode->prompt_mode[prompt_mode_type]),\n> >                      s->buf.buf);\n> > +             if (s->s.no_auto_advance && all_decided)\n> > +                     printf(_(\"\\n%s All hunks decided. What now? \"),\n> > +                             s->s.prompt_color);\n>\n> This gives an ordinary prompt for the hunk and then another one\n> after it if we notice everything has been decided.  I am wondering\n> if it wants to be more like\n>\n>         if (!s->auto_advance && all_decided)\n>                 say What now?\n>         else\n>                 ask the usual\n>\n> ?\n\nHello Junio\nPlease just a small curiosity.\n\nIf I do it this way, the user will not be able to see the options available\nonce they have decided on all hunks and want to rework the file.\nThe options for a hunk will not be visible if they navigate with say K or J\nand want to change decisions on a hunk.\nThey will always be greeted with What now? without the available options.\n"},{"id":"535877","messageId":"xmqqtsvlq3gr.fsf@gitster.g","threadId":"64857","inReplyTo":"CADYq+fa81Uki0ZVta80VO=-UG-f+Z8GAyzom-FLNXULartwwXA@mail.gmail.com","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-12T17:25:08Z","receivedAt":"2026-02-12T17:25:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Samuel Abraham <abrahamadekunle50@gmail.com> writes:\n\n> On Fri, Feb 6, 2026 at 8:21 PM Junio C Hamano <gitster@pobox.com> wrote:\n>>\n>> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>>\n>> > @@ -1566,11 +1589,14 @@ static int patch_update_file(struct add_p_state *s,\n>> >                                               : 1));\n>> >               printf(_(s->mode->prompt_mode[prompt_mode_type]),\n>> >                      s->buf.buf);\n>> > +             if (s->s.no_auto_advance && all_decided)\n>> > +                     printf(_(\"\\n%s All hunks decided. What now? \"),\n>> > +                             s->s.prompt_color);\n>>\n>> This gives an ordinary prompt for the hunk and then another one\n>> after it if we notice everything has been decided.  I am wondering\n>> if it wants to be more like\n>>\n>>         if (!s->auto_advance && all_decided)\n>>                 say What now?\n>>         else\n>>                 ask the usual\n>>\n>> ?\n>\n> Hello Junio\n> Please just a small curiosity.\n>\n> If I do it this way, the user will not be able to see the options available\n> once they have decided on all hunks and want to rework the file.\n> The options for a hunk will not be visible if they navigate with say K or J\n> and want to change decisions on a hunk.\n> They will always be greeted with What now? without the available options.\n\nAh, OK.\n\nBut then after deciding on all hunks and not telling the prompt to\nmove to another file, the user will keep seeing this extra line of\nprompt?\n\nIt somehow smells like a waste of a whole line just to remind the\nuser that all hunks in the file have now been decided.\n\nThere was a separate topic that added \"(was: [yn])\" to the prompt\nwhen the prompt asks about a hunk that already has been decided on.\nAs we only need a single bit \"all hunks decided\", can we do\nsomething similar, I wonder?  At the beginning of the main prompt,\nwe show which of the N available hunks we are currently at, e.g.,\n\n (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?\n\nPerhaps we can add a third number to indicate how many of the\navailable hunks the user has already decided, or something, that can\nbe used to avoid this wasted line?  Or is it a good thing that we\nare loud in this case using a whole line to remind the user that it\nmay be time to move on?  I dunno.\n\nIn any case, even though I am not 100% sure that this design to\ndevote an extra line for this single bit of information is the best\none, I now understand the need for conveying it.  Thanks.\n"},{"id":"535886","messageId":"CADYq+fab0FKncE8VFJcaHA5VmrTJbrSo79jxA6x+Y5dkZP+2RQ@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqtsvlq3gr.fsf@gitster.g","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-12T21:13:38Z","receivedAt":"2026-02-12T21:13:38Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Thu, Feb 12, 2026 at 6:25 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Samuel Abraham <abrahamadekunle50@gmail.com> writes:\n>\n> > On Fri, Feb 6, 2026 at 8:21 PM Junio C Hamano <gitster@pobox.com> wrote:\n> >>\n> >> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n> >>\n> >> > @@ -1566,11 +1589,14 @@ static int patch_update_file(struct add_p_state *s,\n> >> >                                               : 1));\n> >> >               printf(_(s->mode->prompt_mode[prompt_mode_type]),\n> >> >                      s->buf.buf);\n> >> > +             if (s->s.no_auto_advance && all_decided)\n> >> > +                     printf(_(\"\\n%s All hunks decided. What now? \"),\n> >> > +                             s->s.prompt_color);\n> >>\n> >> This gives an ordinary prompt for the hunk and then another one\n> >> after it if we notice everything has been decided.  I am wondering\n> >> if it wants to be more like\n> >>\n> >>         if (!s->auto_advance && all_decided)\n> >>                 say What now?\n> >>         else\n> >>                 ask the usual\n> >>\n> >> ?\n> >\n> > Hello Junio\n> > Please just a small curiosity.\n> >\n> > If I do it this way, the user will not be able to see the options available\n> > once they have decided on all hunks and want to rework the file.\n> > The options for a hunk will not be visible if they navigate with say K or J\n> > and want to change decisions on a hunk.\n> > They will always be greeted with What now? without the available options.\n>\n> Ah, OK.\n>\n> But then after deciding on all hunks and not telling the prompt to\n> move to another file, the user will keep seeing this extra line of\n> prompt?\n>\n> It somehow smells like a waste of a whole line just to remind the\n> user that all hunks in the file have now been decided.\n>\n> There was a separate topic that added \"(was: [yn])\" to the prompt\n> when the prompt asks about a hunk that already has been decided on.\n> As we only need a single bit \"all hunks decided\", can we do\n> something similar, I wonder?  At the beginning of the main prompt,\n> we show which of the N available hunks we are currently at, e.g.,\n>\n>  (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?\n>\n> Perhaps we can add a third number to indicate how many of the\n> available hunks the user has already decided, or something, that can\n> be used to avoid this wasted line?  Or is it a good thing that we\n> are loud in this case using a whole line to remind the user that it\n> may be time to move on?  I dunno.\n\nI thought of a suggestion where after deciding on all hunks in the\nfile, the user\nwill be able to see the \"what now prompt\", the options for the current hunk and\nalso the previous decision on the hunk since at this point, all the\nhunks would have been decided on.\n\nI tried something like\n\nWhat now? (was: n) [y,n,q,a,d,s,e,>,<,p,P,?]?\n\nThis does not show the number of the hunk we are currently at and the\n\"Stage this hunk\" since the decision had been made initially but the \"whatnow\"\nprompt still provides a chance to change the decision, while showing\nthe previous\ndecision on the hunk by asking \"What now?\" instead.\nThe options have the default [y,n,q,a,d] and the remaining options are populated\nfrom the permit set for the hunk. SO the user can still carry out the\nnormal actions on\nthe hunk.\n\nIn response to your earlier question, if the user decides on all hunks in a\nfile and does not go to the next file, he'll see the prompt above and\nthat is what will keep\nshowing if he remains in the file, no extra line.\nIf he navigates away, the hunk re-renders with the \"what now\" prompt\nwhen he comes\nback.\nIf he had made all decisions in a file and decides to split a\nsplittable hunk, then the normal\nprompt shows for those hunks since they are now undecided.\n\nWhat do you think about this?\nThanks\n\nAbraham\n"},{"id":"535889","messageId":"xmqqh5rlmywp.fsf@gitster.g","threadId":"64857","inReplyTo":"CADYq+fab0FKncE8VFJcaHA5VmrTJbrSo79jxA6x+Y5dkZP+2RQ@mail.gmail.com","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-12T21:31:50Z","receivedAt":"2026-02-12T21:31:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Samuel Abraham <abrahamadekunle50@gmail.com> writes:\n\n>> There was a separate topic that added \"(was: [yn])\" to the prompt\n>> when the prompt asks about a hunk that already has been decided on.\n>> As we only need a single bit \"all hunks decided\", can we do\n>> something similar, I wonder?  At the beginning of the main prompt,\n>> we show which of the N available hunks we are currently at, e.g.,\n>>\n>>  (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?\n>>\n>> Perhaps we can add a third number to indicate how many of the\n>> available hunks the user has already decided, or something, that can\n>> be used to avoid this wasted line?  Or is it a good thing that we\n>> are loud in this case using a whole line to remind the user that it\n>> may be time to move on?  I dunno.\n>\n> I thought of a suggestion where after deciding on all hunks in the\n> file, the user\n> will be able to see the \"what now prompt\", the options for the current hunk and\n> also the previous decision on the hunk since at this point, all the\n> hunks would have been decided on.\n>\n> I tried something like\n>\n> What now? (was: n) [y,n,q,a,d,s,e,>,<,p,P,?]?\n>\n> This does not show the number of the hunk we are currently at and the\n> \"Stage this hunk\" since the decision had been made initially but the \"whatnow\"\n> prompt still provides a chance to change the decision, while showing\n> the previous\n> decision on the hunk by asking \"What now?\" instead.\n> The options have the default [y,n,q,a,d] and the remaining options are populated\n> from the permit set for the hunk. SO the user can still carry out the\n> normal actions on\n> the hunk.\n\nI like the compactness of that myself, but I have to say that the\nend-users may feel lost and utterly confused with the distinction,\nif they are left without being explained why we switch between\n\"Stage this\" (which by the way changes phrasing depending on what\nyou are doing) and \"What now\".\n\nIs it so important to indicate that everything in the hunk has been\ndecided?  They'd lose 'j' 'k' when there no longer remains undecided\nones, and every hunk they revisit with 'J' or 'K' would say (was: X),\nwhich may be a clue enough that they are done with the file, and\nwhen they really really wanted to make sure, perhaps they can type\n'?' and that help can spend a line to say \"Out of 8 hunks, you have\nalready decided to use 3 hunks, and skip 5 hunks\" or something?\n\nI dunno.\n"},{"id":"535893","messageId":"CADYq+fZ3b-bEBt_7B-KvGi9vyCOc6=pxVgx+Wbod-TM4oMaKog@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqh5rlmywp.fsf@gitster.g","subject":"Re: [PATCH v3 2/3] add-patch: Allow interfile navigation when selecting hunks","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-12T22:20:47Z","receivedAt":"2026-02-12T22:20:47Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Thu, Feb 12, 2026 at 10:31 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Samuel Abraham <abrahamadekunle50@gmail.com> writes:\n>\n> >> There was a separate topic that added \"(was: [yn])\" to the prompt\n> >> when the prompt asks about a hunk that already has been decided on.\n> >> As we only need a single bit \"all hunks decided\", can we do\n> >> something similar, I wonder?  At the beginning of the main prompt,\n> >> we show which of the N available hunks we are currently at, e.g.,\n> >>\n> >>  (1/2) Stage this hunk (was: y) [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]?\n> >>\n> >> Perhaps we can add a third number to indicate how many of the\n> >> available hunks the user has already decided, or something, that can\n> >> be used to avoid this wasted line?  Or is it a good thing that we\n> >> are loud in this case using a whole line to remind the user that it\n> >> may be time to move on?  I dunno.\n> >\n> > I thought of a suggestion where after deciding on all hunks in the\n> > file, the user\n> > will be able to see the \"what now prompt\", the options for the current hunk and\n> > also the previous decision on the hunk since at this point, all the\n> > hunks would have been decided on.\n> >\n> > I tried something like\n> >\n> > What now? (was: n) [y,n,q,a,d,s,e,>,<,p,P,?]?\n> >\n> > This does not show the number of the hunk we are currently at and the\n> > \"Stage this hunk\" since the decision had been made initially but the \"whatnow\"\n> > prompt still provides a chance to change the decision, while showing\n> > the previous\n> > decision on the hunk by asking \"What now?\" instead.\n> > The options have the default [y,n,q,a,d] and the remaining options are populated\n> > from the permit set for the hunk. SO the user can still carry out the\n> > normal actions on\n> > the hunk.\n>\n> I like the compactness of that myself, but I have to say that the\n> end-users may feel lost and utterly confused with the distinction,\n> if they are left without being explained why we switch between\n> \"Stage this\" (which by the way changes phrasing depending on what\n> you are doing) and \"What now\".\n\nYes I agree.\n\n>\n> Is it so important to indicate that everything in the hunk has been\n> decided?  They'd lose 'j' 'k' when there no longer remains undecided\n> ones, and every hunk they revisit with 'J' or 'K' would say (was: X),\n> which may be a clue enough that they are done with the file, and\n> when they really really wanted to make sure, perhaps they can type\n> '?' and that help can spend a line to say \"Out of 8 hunks, you have\n> already decided to use 3 hunks, and skip 5 hunks\" or something?\n>\n> I dunno.\n\nYes I also think this works.\nIt is not so important to indicate that everything has been decided.\nThe (was: X) shows that the hunk has been decided on and it\nshould be enough.\nI will add the information to the help_patch section.\nThanks\n\nAbraham\n"},{"id":"535967","messageId":"cover.1771015581.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1770390576.git.abrahamadekunle50@gmail.com","subject":"[PATCH v4 0/4] introduce new option `--auto-advance`","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-13T22:08:51Z","receivedAt":"2026-02-13T22:08:44Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"Hello,\n\nAfter after more reviews and deliberations, I have been able to\nrename the new option name to `--auto-advance`, where the\n--no-auto-advance implements the feature and does not auto advance\nwhile --auto-advance is the default and maintains the current\nbehaviour.\n\nWith the option, users can navigate in between files while deciding\non hunks as they wish with the '>' and '<' option for going to the\nnext and previous file respectively if there are more than one file.\n\nPatch 1 implements the new `--no-auto-advance` options, Patch 2\nmodifies the function `patch_update_file()` to instead take the index\nof the file as parameter instead of the file_diff. Patch 3 moves the\n'git apply' logic into a function so that we can reuse this logic when\nimplementing the all or none application of patches.\nPatch 4 implements the interfile navigation, and adds tests to the\ninteractive test file.\n\nChanges in v4:\n==============\n- Renamed option to `--no-auto-advance` with `auto-advance` being\n  the default option\n- Modified the function signature of 'patch_update_file()' to accept\n  the index of the file diff\n- Moved git apply logic into function for reuse\n- Removed the whatnow prompt. Now the hunks in the file keep\n  showing even after all hunks have been decided\n- Added hunk summary to patch help remainder to show the user the\n  hunk deails\n- Added tests ot t3701-add-interactive.sh\n\nAbraham Samuel Adekunle (4):\n  interactive -p: add new `--auto-advance` flag\n  add-patch: modify patch_update_file() signature\n  add-patch: allow all-or-none application of patches\n  add-patch: allow interfile navigation when selecting hunks\n\n add-interactive.c          |   4 +\n add-interactive.h          |   5 +-\n add-patch.c                | 157 +++++++++++++++++++++++++++----------\n builtin/add.c              |   4 +\n builtin/checkout.c         |   7 ++\n builtin/reset.c            |   4 +\n builtin/stash.c            |   8 ++\n t/t3701-add-interactive.sh | 100 +++++++++++++++++++++++\n t/t9902-completion.sh      |   1 +\n 9 files changed, 246 insertions(+), 44 deletions(-)\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"535968","messageId":"497ca5b43c84dc4d146a18899461cd02564c0268.1771015581.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1771015581.git.abrahamadekunle50@gmail.com","subject":"[PATCH v4 1/4] interactive -p: add new `--auto-advance` flag","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-13T22:09:45Z","receivedAt":"2026-02-13T22:09:41Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"When using the interactive add, reset, stash or checkout machinery,\nwe do not have the option of reworking with a file when selecting\nhunks, because the session automatically advances to the next file\nor ends if we have just one file.\n\nIntroduce the flag `--auto-advance` which auto advances by default,\nwhen interactively selecting patches with the '--patch' option.\nHowever, the `--no-auto-advance` option does not auto advance, thereby\nallowing users the option to rework with files.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-interactive.c     | 4 ++++\n add-interactive.h     | 5 +++--\n builtin/add.c         | 4 ++++\n builtin/checkout.c    | 7 +++++++\n builtin/reset.c       | 4 ++++\n builtin/stash.c       | 8 ++++++++\n t/t9902-completion.sh | 1 +\n 7 files changed, 31 insertions(+), 2 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 95ec5a89f8..c3a36cd11f 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -64,6 +64,7 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,\n \ts->r = r;\n \ts->context = -1;\n \ts->interhunkcontext = -1;\n+\ts->auto_advance = 1;\n \n \ts->use_color_interactive = check_color_config(r, \"color.interactive\");\n \n@@ -124,6 +125,8 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,\n \t\t\tdie(_(\"%s cannot be negative\"), \"--inter-hunk-context\");\n \t\ts->interhunkcontext = add_p_opt->interhunkcontext;\n \t}\n+\tif (!add_p_opt->auto_advance)\n+\t\ts->auto_advance = 0;\n }\n \n void clear_add_i_state(struct add_i_state *s)\n@@ -1017,6 +1020,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,\n \t\tstruct add_p_opt add_p_opt = {\n \t\t\t.context = s->context,\n \t\t\t.interhunkcontext = s->interhunkcontext,\n+\t\t\t.auto_advance = s->auto_advance\n \t\t};\n \t\tstruct strvec args = STRVEC_INIT;\n \t\tstruct pathspec ps_selected = { 0 };\ndiff --git a/add-interactive.h b/add-interactive.h\nindex da49502b76..cea29a6965 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -6,9 +6,10 @@\n struct add_p_opt {\n \tint context;\n \tint interhunkcontext;\n+\tint auto_advance;\n };\n \n-#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 }\n+#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1, .auto_advance = 1 }\n \n struct add_i_state {\n \tstruct repository *r;\n@@ -28,7 +29,7 @@ struct add_i_state {\n \n \tint use_single_key;\n \tchar *interactive_diff_filter, *interactive_diff_algorithm;\n-\tint context, interhunkcontext;\n+\tint context, interhunkcontext, auto_advance;\n };\n \n void init_add_i_state(struct add_i_state *s, struct repository *r,\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 32709794b3..4357f87b7f 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -256,6 +256,8 @@ static struct option builtin_add_options[] = {\n \tOPT_GROUP(\"\"),\n \tOPT_BOOL('i', \"interactive\", &add_interactive, N_(\"interactive picking\")),\n \tOPT_BOOL('p', \"patch\", &patch_interactive, N_(\"select hunks interactively\")),\n+\tOPT_BOOL(0, \"auto-advance\", &add_p_opt.auto_advance,\n+\t\t N_(\"auto advance to the next file when selecting hunks interactively\")),\n \tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \tOPT_BOOL('e', \"edit\", &edit_interactive, N_(\"edit current diff and apply\")),\n@@ -418,6 +420,8 @@ int cmd_add(int argc,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--interactive/--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--interactive/--patch\");\n+\t\tif (!add_p_opt.auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--no-auto-advance\", \"--interactive/--patch\");\n \t}\n \n \tif (edit_interactive) {\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0ba4f03f2e..fad35a9284 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -63,6 +63,7 @@ struct checkout_opts {\n \tint patch_mode;\n \tint patch_context;\n \tint patch_interhunk_context;\n+\tint auto_advance;\n \tint quiet;\n \tint merge;\n \tint force;\n@@ -111,6 +112,7 @@ struct checkout_opts {\n \t.merge = -1, \\\n \t.patch_context = -1, \\\n \t.patch_interhunk_context = -1, \\\n+\t.auto_advance = 1, \\\n }\n \n struct branch_info {\n@@ -549,6 +551,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tstruct add_p_opt add_p_opt = {\n \t\t\t.context = opts->patch_context,\n \t\t\t.interhunkcontext = opts->patch_interhunk_context,\n+\t\t\t.auto_advance = opts->auto_advance\n \t\t};\n \t\tconst char *rev = new_branch_info->name;\n \t\tchar rev_oid[GIT_MAX_HEXSZ + 1];\n@@ -1803,6 +1806,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (opts->patch_interhunk_context != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (!opts->auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--no-auto-advance\", \"--patch\");\n \t}\n \n \tif (opts->show_progress < 0) {\n@@ -2001,6 +2006,8 @@ int cmd_checkout(int argc,\n \t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n \t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n+\t\tOPT_BOOL(0, \"auto-advance\", &opts.auto_advance,\n+\t\t\t N_(\"auto advance to the next file when selecting hunks interactively\")),\n \t\tOPT_END()\n \t};\n \ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex c48d9845f8..88f95f9fc7 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -371,6 +371,8 @@ int cmd_reset(int argc,\n \t\t\t       PARSE_OPT_OPTARG,\n \t\t\t       option_parse_recurse_submodules_worktree_updater),\n \t\tOPT_BOOL('p', \"patch\", &patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"auto-advance\", &add_p_opt.auto_advance,\n+\t\t\t N_(\"auto advance to the next file when selecting hunks interactively\")),\n \t\tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \t\tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \t\tOPT_BOOL('N', \"intent-to-add\", &intent_to_add,\n@@ -443,6 +445,8 @@ int cmd_reset(int argc,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (!add_p_opt.auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--no-auto-advance\", \"--patch\");\n \t}\n \n \t/* git reset tree [--] paths... can be used to\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 193e3ea47a..f98487f4cd 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1849,6 +1849,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \t\t\t N_(\"stash staged changes only\")),\n \t\tOPT_BOOL('p', \"patch\", &patch_mode,\n \t\t\t N_(\"stash in patch mode\")),\n+\t\tOPT_BOOL(0, \"auto-advance\", &add_p_opt.auto_advance,\n+\t\t\t N_(\"auto advance to the next file when selecting hunks interactively\")),\n \t\tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \t\tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n@@ -1911,6 +1913,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (!add_p_opt.auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--no-auto-advance\", \"--patch\");\n \t}\n \n \tif (add_p_opt.context < -1)\n@@ -1952,6 +1956,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,\n \t\t\t N_(\"stash staged changes only\")),\n \t\tOPT_BOOL('p', \"patch\", &patch_mode,\n \t\t\t N_(\"stash in patch mode\")),\n+\t\tOPT_BOOL(0, \"auto-advance\", &add_p_opt.auto_advance,\n+\t\t\t N_(\"auto advance to the next file when selecting hunks interactively\")),\n \t\tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \t\tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n@@ -1983,6 +1989,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (!add_p_opt.auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--no-auto-advance\", \"--patch\");\n \t}\n \n \tret = do_push_stash(&ps, stash_msg, quiet, keep_index,\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex ffb9c8b522..2f9a597ec7 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2601,6 +2601,7 @@ test_expect_success 'double dash \"git checkout\"' '\n \t--ignore-skip-worktree-bits Z\n \t--ignore-other-worktrees Z\n \t--recurse-submodules Z\n+\t--auto-advance Z\n \t--progress Z\n \t--guess Z\n \t--no-guess Z\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"535969","messageId":"906f25e184d744f9d23681600a0d9e440b7f07df.1771015581.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1771015581.git.abrahamadekunle50@gmail.com","subject":"[PATCH v4 2/4] add-patch: modify patch_update_file() signature","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-13T22:10:48Z","receivedAt":"2026-02-13T22:10:41Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"The function `patch_update_file()` takes the `add_p_state` struct\npointer and the current `struct file_diff` pointer and returns an\nint.\n\nWhen using the `--no-auto-advance` flag, we want to be able to request\nthe next or previous file from the caller.\n\nModify the function signature to instead take the index of the\ncurrent `file_diff` and the `add_p_state` struct pointer so that we\ncan compute the `file_diff` from the index while also having\naccess to the file index. This will help us request the next or\nprevious file from the caller.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-patch.c | 35 ++++++++++++++++++++++-------------\n 1 file changed, 22 insertions(+), 13 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex df8f2e6d74..673ea659ff 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1441,20 +1441,21 @@ static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)\n \treturn false;\n }\n \n-static int patch_update_file(struct add_p_state *s,\n-\t\t\t     struct file_diff *file_diff)\n+static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n {\n \tsize_t hunk_index = 0;\n \tssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n \tstruct hunk *hunk;\n \tchar ch;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tint colored = !!s->colored.len, quit = 0, use_pager = 0;\n+\tint colored = !!s->colored.len, use_pager = 0;\n \tenum prompt_mode_type prompt_mode_type;\n+\tstruct file_diff *file_diff = s->file_diff + idx;\n+\tssize_t patch_update_resp = (ssize_t)idx;\n \n \t/* Empty added files have no hunks */\n \tif (!file_diff->hunk_nr && !file_diff->added)\n-\t\treturn 0;\n+\t\treturn patch_update_resp + 1;\n \n \tstrbuf_reset(&s->buf);\n \trender_diff_header(s, file_diff, colored, &s->buf);\n@@ -1499,9 +1500,10 @@ static int patch_update_file(struct add_p_state *s,\n \n \t\t/* Everything decided? */\n \t\tif (undecided_previous < 0 && undecided_next < 0 &&\n-\t\t    hunk->use != UNDECIDED_HUNK)\n-\t\t\tbreak;\n-\n+\t\t    hunk->use != UNDECIDED_HUNK) {\n+\t\t\t\tpatch_update_resp++;\n+\t\t\t\tbreak;\n+\t\t}\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n \t\t\tif (rendered_hunk_index != hunk_index) {\n@@ -1577,7 +1579,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\tfputs(s->s.reset_color_interactive, stdout);\n \t\tfflush(stdout);\n \t\tif (read_single_character(s) == EOF) {\n-\t\t\tquit = 1;\n+\t\t\tpatch_update_resp = -1;\n \t\t\tbreak;\n \t\t}\n \n@@ -1623,7 +1625,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\thunk->use = SKIP_HUNK;\n \t\t\t}\n \t\t} else if (ch == 'q') {\n-\t\t\tquit = 1;\n+\t\t\tpatch_update_resp = -1;\n \t\t\tbreak;\n \t\t} else if (s->answer.buf[0] == 'K') {\n \t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n@@ -1810,7 +1812,7 @@ static int patch_update_file(struct add_p_state *s,\n \t}\n \n \tputchar('\\n');\n-\treturn quit;\n+\treturn patch_update_resp;\n }\n \n int run_add_p(struct repository *r, enum add_p_mode mode,\n@@ -1821,6 +1823,7 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\t{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT\n \t};\n \tsize_t i, binary_count = 0;\n+\tssize_t patch_update_resp;\n \n \tinit_add_i_state(&s.s, r, o);\n \n@@ -1859,11 +1862,17 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\treturn -1;\n \t}\n \n-\tfor (i = 0; i < s.file_diff_nr; i++)\n-\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)\n+\tfor (i = 0; i < s.file_diff_nr;) {\n+\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {\n \t\t\tbinary_count++;\n-\t\telse if (patch_update_file(&s, s.file_diff + i))\n+\t\t\ti++;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tpatch_update_resp = patch_update_file(&s, i);\n+\t\tif (patch_update_resp < 0)\n \t\t\tbreak;\n+\t\ti = (size_t)patch_update_resp;\n+    }\n \n \tif (s.file_diff_nr == 0)\n \t\terr(&s, _(\"No changes.\"));\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"535970","messageId":"aed0a80d8e55e4331677844bd84635b758572959.1771015581.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1771015581.git.abrahamadekunle50@gmail.com","subject":"[PATCH v4 3/4] add-patch: allow all-or-none application of patches","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-13T22:11:43Z","receivedAt":"2026-02-13T22:11:35Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"When the flag `--no-auto-advance` is used with `--patch`,\nif the user has decided `USE` on a hunk in a file, goes to another\nfile, and then returns to this file and changes the previous\ndecision on the hunk to `SKIP`, because the patch has already\nbeen applied, the last decision is not registered and the now\nSKIPPED hunk is still applied.\n\nMove the logic for applying patches into a function so that we can\nreuse this logic to implement the all or non application of the patches\nafter the user is done with the hunk selection.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-patch.c | 62 ++++++++++++++++++++++++++++++-----------------------\n 1 file changed, 35 insertions(+), 27 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 673ea659ff..7d4f17e432 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1420,6 +1420,40 @@ N_(\"j - go to the next undecided hunk, roll over at the bottom\\n\"\n    \"P - print the current hunk using the pager\\n\"\n    \"? - print help\\n\");\n \n+static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tsize_t j;\n+\n+\t/* Any hunk to be used? */\n+\tfor (j = 0; j < file_diff->hunk_nr; j++)\n+\t\tif (file_diff->hunk[j].use == USE_HUNK)\n+\t\t\tbreak;\n+\n+\tif (j < file_diff->hunk_nr ||\n+\t\t(!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n+\t\t/* At least one hunk selected: apply */\n+\t\tstrbuf_reset(&s->buf);\n+\t\treassemble_patch(s, file_diff, 0, &s->buf);\n+\n+\t\tdiscard_index(s->s.r->index);\n+\t\tif (s->mode->apply_for_checkout)\n+\t\t\tapply_for_checkout(s, &s->buf,\n+\t\t\t\t\ts->mode->is_reverse);\n+\t\telse {\n+\t\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n+\t\t\tstrvec_pushv(&cp.args, s->mode->apply_args);\n+\t\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n+\t\t\t\t\tNULL, 0, NULL, 0))\n+\t\t\t\terror(_(\"'git apply' failed\"));\n+\t\t}\n+\t\tif (repo_read_index(s->s.r) >= 0)\n+\t\t\trepo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n+\t\t\t\t\t\t\t1, NULL, NULL, NULL);\n+\t}\n+\n+}\n+\n static size_t dec_mod(size_t a, size_t m)\n {\n \treturn a > 0 ? a - 1 : m - 1;\n@@ -1447,7 +1481,6 @@ static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n \tssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n \tstruct hunk *hunk;\n \tchar ch;\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint colored = !!s->colored.len, use_pager = 0;\n \tenum prompt_mode_type prompt_mode_type;\n \tstruct file_diff *file_diff = s->file_diff + idx;\n@@ -1784,32 +1817,7 @@ static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t}\n \t}\n \n-\t/* Any hunk to be used? */\n-\tfor (i = 0; i < file_diff->hunk_nr; i++)\n-\t\tif (file_diff->hunk[i].use == USE_HUNK)\n-\t\t\tbreak;\n-\n-\tif (i < file_diff->hunk_nr ||\n-\t    (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n-\t\t/* At least one hunk selected: apply */\n-\t\tstrbuf_reset(&s->buf);\n-\t\treassemble_patch(s, file_diff, 0, &s->buf);\n-\n-\t\tdiscard_index(s->s.r->index);\n-\t\tif (s->mode->apply_for_checkout)\n-\t\t\tapply_for_checkout(s, &s->buf,\n-\t\t\t\t\t   s->mode->is_reverse);\n-\t\telse {\n-\t\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n-\t\t\tstrvec_pushv(&cp.args, s->mode->apply_args);\n-\t\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n-\t\t\t\t\t NULL, 0, NULL, 0))\n-\t\t\t\terror(_(\"'git apply' failed\"));\n-\t\t}\n-\t\tif (repo_read_index(s->s.r) >= 0)\n-\t\t\trepo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n-\t\t\t\t\t\t     1, NULL, NULL, NULL);\n-\t}\n+\tapply_patch(s, file_diff);\n \n \tputchar('\\n');\n \treturn patch_update_resp;\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"535971","messageId":"900d39c1b66d1a71fe0abb40d88f328e40b049e4.1771015581.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1771015581.git.abrahamadekunle50@gmail.com","subject":"[PATCH v4 4/4] add-patch: allow interfile navigation when selecting hunks","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-13T22:12:32Z","receivedAt":"2026-02-13T22:12:29Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"After deciding on all hunks in a file, the interactive session\nadvances automatically to the next file if there is another,\nor the process ends.\n\nNow using the `--no-auto-advance` flag with `--patch`, the process\ndoes not advance automatically. A user can choose to go to the next\nfile by pressing '>' or the previous file by pressing '<', before or\nafter deciding on all hunks in the current file.\n\nAfter all hunks have been decided in a file, the user can still\nrework with the file by applying the options available in the permit\nset for that hunk, and after all the decisions, the user presses 'q'\nto submit.\nAfter all hunks have been decided, the user can press '?' which will\nshow the hunk selection summary in the help patch remainder text\nincluding the total hunks, number of hunks marked for use and number\nof hunks marked for skip.\n\nThis feature is enabled by passing the `--no-auto-advance` flag\nto `--patch` option of the subcommands add, stash, reset,\nand checkout.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-patch.c                |  66 ++++++++++++++++++++++--\n t/t3701-add-interactive.sh | 100 +++++++++++++++++++++++++++++++++++++\n 2 files changed, 161 insertions(+), 5 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 7d4f17e432..6b9ae4da30 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1418,7 +1418,10 @@ N_(\"j - go to the next undecided hunk, roll over at the bottom\\n\"\n    \"e - manually edit the current hunk\\n\"\n    \"p - print the current hunk\\n\"\n    \"P - print the current hunk using the pager\\n\"\n-   \"? - print help\\n\");\n+   \"> - go to the next file, roll over at the bottom\\n\"\n+   \"< - go to the previous file, roll over at the top\\n\"\n+   \"? - print help\\n\"\n+   \"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\\n\");\n \n static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)\n {\n@@ -1483,6 +1486,7 @@ static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n \tchar ch;\n \tint colored = !!s->colored.len, use_pager = 0;\n \tenum prompt_mode_type prompt_mode_type;\n+\tint all_decided = 0;\n \tstruct file_diff *file_diff = s->file_diff + idx;\n \tssize_t patch_update_resp = (ssize_t)idx;\n \n@@ -1502,7 +1506,9 @@ static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t\tALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,\n \t\t\tALLOW_SEARCH_AND_GOTO = 1 << 4,\n \t\t\tALLOW_SPLIT = 1 << 5,\n-\t\t\tALLOW_EDIT = 1 << 6\n+\t\t\tALLOW_EDIT = 1 << 6,\n+\t\t\tALLOW_GOTO_PREVIOUS_FILE = 1 << 7,\n+\t\t\tALLOW_GOTO_NEXT_FILE = 1 << 8\n \t\t} permitted = 0;\n \n \t\tif (hunk_index >= file_diff->hunk_nr)\n@@ -1534,8 +1540,12 @@ static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t/* Everything decided? */\n \t\tif (undecided_previous < 0 && undecided_next < 0 &&\n \t\t    hunk->use != UNDECIDED_HUNK) {\n-\t\t\t\tpatch_update_resp++;\n-\t\t\t\tbreak;\n+\t\t\t\tif (!s->s.auto_advance)\n+\t\t\t\t\tall_decided = 1;\n+\t\t\t\telse {\n+\t\t\t\t\tpatch_update_resp++;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n \t\t}\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n@@ -1584,6 +1594,14 @@ static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t\t\tpermitted |= ALLOW_EDIT;\n \t\t\t\tstrbuf_addstr(&s->buf, \",e\");\n \t\t\t}\n+\t\t\tif (!s->s.auto_advance && s->file_diff_nr > 1) {\n+\t\t\t\tpermitted |= ALLOW_GOTO_NEXT_FILE;\n+\t\t\t\tstrbuf_addstr(&s->buf, \",>\");\n+\t\t\t}\n+\t\t\tif (!s->s.auto_advance && s->file_diff_nr > 1) {\n+\t\t\t\tpermitted |= ALLOW_GOTO_PREVIOUS_FILE;\n+\t\t\t\tstrbuf_addstr(&s->buf, \",<\");\n+\t\t\t}\n \t\t\tstrbuf_addstr(&s->buf, \",p,P\");\n \t\t}\n \t\tif (file_diff->deleted)\n@@ -1660,6 +1678,28 @@ static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t} else if (ch == 'q') {\n \t\t\tpatch_update_resp = -1;\n \t\t\tbreak;\n+\t\t} else if (!s->s.auto_advance && s->answer.buf[0] == '>') {\n+\t\t\tif (permitted & ALLOW_GOTO_NEXT_FILE) {\n+\t\t\t\tif (patch_update_resp == s->file_diff_nr - 1)\n+\t\t\t\t\tpatch_update_resp = 0;\n+\t\t\t\telse\n+\t\t\t\t\tpatch_update_resp++;\n+\t\t\t\tbreak;\n+\t\t\t} else {\n+\t\t\t\terr(s, _(\"No next file\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t} else if (!s->s.auto_advance && s->answer.buf[0] == '<') {\n+\t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_FILE) {\n+\t\t\t\tif (patch_update_resp == 0)\n+\t\t\t\t\tpatch_update_resp = s->file_diff_nr - 1;\n+\t\t\t\telse\n+\t\t\t\t\tpatch_update_resp--;\n+\t\t\t\tbreak;\n+\t\t\t} else {\n+\t\t\t\terr(s, _(\"No previous file\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t} else if (s->answer.buf[0] == 'K') {\n \t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n \t\t\t\thunk_index = dec_mod(hunk_index,\n@@ -1805,6 +1845,18 @@ static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t\t\t * commands shown in the prompt that are not\n \t\t\t\t * always available.\n \t\t\t\t */\n+\t\t\t\tif (all_decided && !strncmp(p, \"HUNKS SUMMARY\", 13)) {\n+\t\t\t\t\tint total = file_diff->hunk_nr, used = 0, skipped = 0;\n+\n+\t\t\t\t\tfor (i = 0; i < file_diff->hunk_nr; i++) {\n+\t\t\t\t\t\tif (file_diff->hunk[i].use == USE_HUNK)\n+\t\t\t\t\t\t\tused += 1;\n+\t\t\t\t\t\tif (file_diff->hunk[i].use == SKIP_HUNK)\n+\t\t\t\t\t\t\tskipped += 1;\n+\t\t\t\t\t}\n+\t\t\t\t\tcolor_fprintf_ln(stdout, s->s.help_color, _(p),\n+\t\t\t\t\t\t\t total, used, skipped);\n+\t\t\t\t}\n \t\t\t\tif (*p != '?' && !strchr(s->buf.buf, *p))\n \t\t\t\t\tcontinue;\n \n@@ -1817,7 +1869,8 @@ static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t}\n \t}\n \n-\tapply_patch(s, file_diff);\n+\tif (s->s.auto_advance)\n+\t\tapply_patch(s, file_diff);\n \n \tputchar('\\n');\n \treturn patch_update_resp;\n@@ -1881,6 +1934,9 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\t\tbreak;\n \t\ti = (size_t)patch_update_resp;\n     }\n+\tif (!s.s.auto_advance)\n+\t\tfor (i = 0; i < s.file_diff_nr; i++)\n+\t\t\tapply_patch(&s, s.file_diff + i);\n \n \tif (s.file_diff_nr == 0)\n \t\terr(&s, _(\"No changes.\"));\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 5ce9c6dd60..6e120a4001 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -1441,5 +1441,105 @@ test_expect_success 'EOF quits' '\n \ttest_grep file out &&\n \ttest_grep ! file2 out\n '\n+for cmd in add checkout reset \"stash save\" \"stash push\"\n+do\n+\ttest_expect_success \"$cmd rejects invalid --no-auto-advance options\" '\n+\t\ttest_must_fail git $cmd --no-auto-advance 2>actual &&\n+\t\ttest_grep -E  \"requires .*--(interactive|patch)\" actual\n+\t'\n+done\n+\n+test_expect_success 'manual advance (\">\") moves to next file with --no-auto-advance' '\n+\tgit reset --hard &&\n+\techo line1 >first-file &&\n+\techo line2 >second-file &&\n+\tgit add -A &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\techo change_first >>first-file &&\n+\techo change_second >>second-file &&\n+\n+\tprintf \">\\nq\\n\" | git add -p --no-auto-advance >output.test 2>&1 &&\n+\ttest_grep  -E \"(a|b)/second-file\" output.test\n+'\n+\n+test_expect_success 'select n on a hunk, go to another file, come back and change to y stages' '\n+\tgit reset --hard &&\n+\techo one >f1 &&\n+\techo one >f2 &&\n+\tgit add -A &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\techo change1 >>f1 &&\n+\techo change2 >>f2 &&\n+\n+\tprintf \"n\\n>\\n<\\ny\\nq\\n\" | git add -p --no-auto-advance >output.staged 2>&1 &&\n+\tgit diff --cached --name-only >staged &&\n+\ttest_grep -E \"(a/f1)\" output.staged\n+'\n+\n+test_expect_success 'select y on a hunk, go to another file, come back and change to n does not stage' '\n+\tgit reset --hard &&\n+\techo one >f1 &&\n+\techo one >f2 &&\n+\tgit add -A &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\techo change1 >>f1 &&\n+\techo change2 >>f2 &&\n+\n+\tprintf \"y\\n>\\n<\\nn\\nq\\n\" | git add -p --no-auto-advance >output.unstaged 2>&1 &&\n+\tgit diff --cached --name-only >staged &&\n+\ttest_must_be_empty staged\n+'\n+\n+test_expect_success 'deciding all hunks in a file does not auto advance' '\n+\tgit reset --hard &&\n+\techo line >stay &&\n+\techo line >other &&\n+\tgit add -A &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\techo change >>stay &&\n+\techo change >>other &&\n+\ttest_write_lines y | git add -p --no-auto-advance >raw-output 2>&1 &&\n+\ttest_grep \"(1/1) Stage this hunk (was: y)\" raw-output &&\n+\ttest_grep ! \"diff --git a/stay b/stay\" raw-output\n+'\n+test_expect_success 'HUNKS SUMMARY does not show in help text when there are undecided hunks' '\n+\tgit reset --hard &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >f &&\n+\tgit add f &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\ttest_write_lines 1 X 3 4 Y 6 7 Z 9 >f &&\n+\ttest_write_lines s y n | git add -p --no-auto-advance >raw-nostat 2>&1 &&\n+\ttest_grep ! \"HUNKS SUMMARY - Hunks: \" raw-nostat\n+'\n+\n+test_expect_success 'help text shows HUNK SUMMARY when all hunks have been decided' '\n+\tgit reset --hard &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >f2 &&\n+\tgit add f2 &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\ttest_write_lines 1 X 3 4 Y 6 7 Z 9 >f2 &&\n+\tprintf \"s\\ny\\nn\\ny\\n?\\n\" | git add -p --no-auto-advance >raw-stat 2>&1 &&\n+\ttest_grep \"HUNKS SUMMARY - Hunks: 3, USE: 2, SKIP: 1\" raw-stat\n+'\n+\n+test_expect_success 'selective staging across multiple files with --no-advance' '\n+\tgit reset --hard &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >a.file &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >b.file &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >c.file &&\n+\tgit add -A &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\ttest_write_lines 1 A2 3 4 A5 6 7 8 9 >a.file &&\n+\ttest_write_lines 1 2 B3 4 5 6 7 B8 9 >b.file &&\n+\ttest_write_lines C1 2 3 4 5 C6 7 8 9 >c.file &&\n+\tprintf \"s\\ny\\nn\\n>\\ns\\nn\\ny\\n>\\ns\\ny\\ny\\nq\\n\" | git add -p --no-auto-advance >output.index 2>&1 &&\n+\tgit diff --cached >staged.diff &&\n+\ttest_grep \"+A2\" staged.diff &&\n+\ttest_grep ! \"+A5\" staged.diff &&\n+\ttest_grep \"+B8\" staged.diff &&\n+\ttest_grep ! \"+B3\" staged.diff &&\n+\ttest_grep \"+C1\" staged.diff &&\n+\ttest_grep \"+C6\" staged.diff\n+'\n \n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"535974","messageId":"xmqq4inkjld4.fsf@gitster.g","threadId":"64857","inReplyTo":"497ca5b43c84dc4d146a18899461cd02564c0268.1771015581.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v4 1/4] interactive -p: add new `--auto-advance` flag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-13T23:04:55Z","receivedAt":"2026-02-13T23:04:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> When using the interactive add, reset, stash or checkout machinery,\n> we do not have the option of reworking with a file when selecting\n> hunks, because the session automatically advances to the next file\n> or ends if we have just one file.\n>\n> Introduce the flag `--auto-advance` which auto advances by default,\n> when interactively selecting patches with the '--patch' option.\n> However, the `--no-auto-advance` option does not auto advance, thereby\n> allowing users the option to rework with files.\n>\n> Signed-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n> ---\n>  add-interactive.c     | 4 ++++\n>  add-interactive.h     | 5 +++--\n>  builtin/add.c         | 4 ++++\n>  builtin/checkout.c    | 7 +++++++\n>  builtin/reset.c       | 4 ++++\n>  builtin/stash.c       | 8 ++++++++\n>  t/t9902-completion.sh | 1 +\n>  7 files changed, 31 insertions(+), 2 deletions(-)\n>\n> diff --git a/add-interactive.c b/add-interactive.c\n> index 95ec5a89f8..c3a36cd11f 100644\n> --- a/add-interactive.c\n> +++ b/add-interactive.c\n> @@ -64,6 +64,7 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,\n>  \ts->r = r;\n>  \ts->context = -1;\n>  \ts->interhunkcontext = -1;\n> +\ts->auto_advance = 1;\n>  \n>  \ts->use_color_interactive = check_color_config(r, \"color.interactive\");\n>  \n> @@ -124,6 +125,8 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,\n>  \t\t\tdie(_(\"%s cannot be negative\"), \"--inter-hunk-context\");\n>  \t\ts->interhunkcontext = add_p_opt->interhunkcontext;\n>  \t}\n> +\tif (!add_p_opt->auto_advance)\n> +\t\ts->auto_advance = 0;\n>  }\n\nI am confused.  Why do we need above two hunks in this function?\nWouldn't it suffice to do\n\n\ts->auto_advance = add_p_opt->auto_advance;\n\nin the first hunk, instead of assigning 1 to it?\n\n>  struct add_i_state {\n>  \tstruct repository *r;\n> @@ -28,7 +29,7 @@ struct add_i_state {\n>  \n>  \tint use_single_key;\n>  \tchar *interactive_diff_filter, *interactive_diff_algorithm;\n> -\tint context, interhunkcontext;\n> +\tint context, interhunkcontext, auto_advance;\n\nPlease don't do this.\n\nThe original is already bad to have two members on the same line,\nbut is tolerated as they represent somewhat related concepts.  The\nauto_advance member has nothing to do with these two.\n\n\n"},{"id":"535975","messageId":"xmqqms1ci5g8.fsf@gitster.g","threadId":"64857","inReplyTo":"906f25e184d744f9d23681600a0d9e440b7f07df.1771015581.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v4 2/4] add-patch: modify patch_update_file() signature","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-13T23:33:59Z","receivedAt":"2026-02-13T23:34:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> -static int patch_update_file(struct add_p_state *s,\n> -\t\t\t     struct file_diff *file_diff)\n> +static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n\nWhy ssize_t?  Are we going to handle that many hunks that we do not\nexpect to fit in a platform natural \"int\" type?  If we are not doing\nanything about \"idx\" being more than half the type, which apparently\nis the case ...\n\n>  {\n>  \tsize_t hunk_index = 0;\n>  \tssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n>  \tstruct hunk *hunk;\n>  \tchar ch;\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n> -\tint colored = !!s->colored.len, quit = 0, use_pager = 0;\n> +\tint colored = !!s->colored.len, use_pager = 0;\n>  \tenum prompt_mode_type prompt_mode_type;\n> +\tstruct file_diff *file_diff = s->file_diff + idx;\n> +\tssize_t patch_update_resp = (ssize_t)idx;\n\n... with the cast that is not checked here, wouldn't it make sense\nto just use the platform natural \"int\" everywhere?  Your code is not\n\"safe\" either way.  I do not think we expect to handle 2 billion\nhunks, so even on 32-bit platforms, platform natural \"int\" should be\nplenty.  Instead of religiously using size_t and ssize_t to count\nthings without extra care, I'd rather see us check the error\ncondition for real, if that is what we really care about (and that\ncan still be done while leaving the codebase cleaner by sticking to\nthe platform natural \"int\").\n\nEnough ranting.  Anyway.\n\nIf we really are bothered that we cannot handle 3 billion hunks, we\ncould avoid losing half the number range by returning\ns->file_diff.file_diff_nr (which is one more than there are elements\nin s->file_diff[] array) or ((size_t)-1).  That would allow us to\nreturn size_t from here.  I care about this a bit more than \"why use\nsize_t when int is perfectly fine\", because some platforms that are\nnot quite POSIX can have ssize_t that is not as wide as size_t.\n"},{"id":"536004","messageId":"CADYq+faXoK7FQqG6gs1yiXR3i1FBScNTG5npGp4G=Yc+FEVexQ@mail.gmail.com","threadId":"64857","inReplyTo":"xmqq4inkjld4.fsf@gitster.g","subject":"Re: [PATCH v4 1/4] interactive -p: add new `--auto-advance` flag","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-14T09:16:04Z","receivedAt":"2026-02-14T09:16:05Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Sat, Feb 14, 2026 at 12:04 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > When using the interactive add, reset, stash or checkout machinery,\n> > we do not have the option of reworking with a file when selecting\n> > hunks, because the session automatically advances to the next file\n> > or ends if we have just one file.\n> >\n> > Introduce the flag `--auto-advance` which auto advances by default,\n> > when interactively selecting patches with the '--patch' option.\n> > However, the `--no-auto-advance` option does not auto advance, thereby\n> > allowing users the option to rework with files.\n> >\n> > Signed-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n> > ---\n> >  add-interactive.c     | 4 ++++\n> >  add-interactive.h     | 5 +++--\n> >  builtin/add.c         | 4 ++++\n> >  builtin/checkout.c    | 7 +++++++\n> >  builtin/reset.c       | 4 ++++\n> >  builtin/stash.c       | 8 ++++++++\n> >  t/t9902-completion.sh | 1 +\n> >  7 files changed, 31 insertions(+), 2 deletions(-)\n> >\n> > diff --git a/add-interactive.c b/add-interactive.c\n> > index 95ec5a89f8..c3a36cd11f 100644\n> > --- a/add-interactive.c\n> > +++ b/add-interactive.c\n> > @@ -64,6 +64,7 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,\n> >       s->r = r;\n> >       s->context = -1;\n> >       s->interhunkcontext = -1;\n> > +     s->auto_advance = 1;\n> >\n> >       s->use_color_interactive = check_color_config(r, \"color.interactive\");\n> >\n> > @@ -124,6 +125,8 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,\n> >                       die(_(\"%s cannot be negative\"), \"--inter-hunk-context\");\n> >               s->interhunkcontext = add_p_opt->interhunkcontext;\n> >       }\n> > +     if (!add_p_opt->auto_advance)\n> > +             s->auto_advance = 0;\n> >  }\n>\n> I am confused.  Why do we need above two hunks in this function?\n> Wouldn't it suffice to do\n>\n>         s->auto_advance = add_p_opt->auto_advance;\n>\n> in the first hunk, instead of assigning 1 to it?\n\nYes thank you\n\n>\n> >  struct add_i_state {\n> >       struct repository *r;\n> > @@ -28,7 +29,7 @@ struct add_i_state {\n> >\n> >       int use_single_key;\n> >       char *interactive_diff_filter, *interactive_diff_algorithm;\n> > -     int context, interhunkcontext;\n> > +     int context, interhunkcontext, auto_advance;\n>\n> Please don't do this.\n>\n> The original is already bad to have two members on the same line,\n> but is tolerated as they represent somewhat related concepts.  The\n> auto_advance member has nothing to do with these two.\n>\n\nOkay\n"},{"id":"536009","messageId":"CADYq+fa=-V9_gTpPRUvCDwFDShrUuxBqojOM+JSo_AvfvAJR7Q@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqms1ci5g8.fsf@gitster.g","subject":"Re: [PATCH v4 2/4] add-patch: modify patch_update_file() signature","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-14T10:14:08Z","receivedAt":"2026-02-14T10:14:09Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Sat, Feb 14, 2026 at 12:34 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > -static int patch_update_file(struct add_p_state *s,\n> > -                          struct file_diff *file_diff)\n> > +static ssize_t patch_update_file(struct add_p_state *s, size_t idx)\n>\n> Why ssize_t?  Are we going to handle that many hunks that we do not\n> expect to fit in a platform natural \"int\" type?  If we are not doing\n> anything about \"idx\" being more than half the type, which apparently\n> is the case ...\n>\n> >  {\n> >       size_t hunk_index = 0;\n> >       ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n> >       struct hunk *hunk;\n> >       char ch;\n> >       struct child_process cp = CHILD_PROCESS_INIT;\n> > -     int colored = !!s->colored.len, quit = 0, use_pager = 0;\n> > +     int colored = !!s->colored.len, use_pager = 0;\n> >       enum prompt_mode_type prompt_mode_type;\n> > +     struct file_diff *file_diff = s->file_diff + idx;\n> > +     ssize_t patch_update_resp = (ssize_t)idx;\n>\n> ... with the cast that is not checked here, wouldn't it make sense\n> to just use the platform natural \"int\" everywhere?  Your code is not\n> \"safe\" either way.  I do not think we expect to handle 2 billion\n> hunks, so even on 32-bit platforms, platform natural \"int\" should be\n> plenty.  Instead of religiously using size_t and ssize_t to count\n> things without extra care, I'd rather see us check the error\n> condition for real, if that is what we really care about (and that\n> can still be done while leaving the codebase cleaner by sticking to\n> the platform natural \"int\").\n>\n> Enough ranting.  Anyway.\n>\n> If we really are bothered that we cannot handle 3 billion hunks, we\n> could avoid losing half the number range by returning\n> s->file_diff.file_diff_nr (which is one more than there are elements\n> in s->file_diff[] array) or ((size_t)-1).  That would allow us to\n> return size_t from here.  I care about this a bit more than \"why use\n> size_t when int is perfectly fine\", because some platforms that are\n> not quite POSIX can have ssize_t that is not as wide as size_t.\n\nHello Junio.\nThank you for the review.\n\nI wanted to be able to return a negative value while also making sure to\nreturn a type of the same size as \"i\" since that is what we use to\nupdate the caller\nfor the next or previous \"i\".\nBut I know better now to think about platforms that are not quite POSIX.\nI will return the s->file_diff_nr and check for that instead.\n\nThanks\nAbraham\n"},{"id":"536011","messageId":"cover.1771066252.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1771015581.git.abrahamadekunle50@gmail.com","subject":"[PATCH v5 0/4] introduce new option `--auto-advance`","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-14T11:01:28Z","receivedAt":"2026-02-14T11:01:21Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"Hello,\n\nAfter after more reviews and deliberations, I have been able to\nrename the new option name to `--auto-advance`, where the\n--no-auto-advance implements the feature and does not auto advance\nwhile --auto-advance is the default and maintains the current\nbehaviour.\n\nWith the option, users can navigate in between files while deciding\non hunks as they wish with the '>' and '<' option for going to the\nnext and previous file respectively if there are more than one file.\n\nPatch 1 implements the new `--no-auto-advance` options, Patch 2\nmodifies the function `patch_update_file()` to instead take the index\nof the file as parameter instead of the file_diff. Patch 3 moves the\n'git apply' logic into a function so that we can reuse this logic when\nimplementing the all or none application of patches.\nPatch 4 implements the interfile navigation, and adds tests to the\ninteractive test file.\n\nChanges in v5:\n==============\n\n- Moved 'auto_advance' member in struct add_i_state to its own line\n- Removed redundant lodic to set s->auto-advance in init_add_i_state()\n- Modified patch_update_file() to return size_t\n- Modified the logic which checks when to quit in run_add_p() to check\n  for s->file_diff_nr instead of a negative value.\n\nAbraham Samuel Adekunle (4):\n  interactive -p: add new `--auto-advance` flag\n  add-patch: modify patch_update_file() signature\n  add-patch: allow all-or-none application of patches\n  add-patch: allow interfile navigation when selecting hunks\n\n add-interactive.c          |   2 +\n add-interactive.h          |   4 +-\n add-patch.c                | 154 +++++++++++++++++++++++++++----------\n builtin/add.c              |   4 +\n builtin/checkout.c         |   7 ++\n builtin/reset.c            |   4 +\n builtin/stash.c            |   8 ++\n t/t3701-add-interactive.sh | 100 ++++++++++++++++++++++++\n t/t9902-completion.sh      |   1 +\n 9 files changed, 241 insertions(+), 43 deletions(-)\n\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"536012","messageId":"1a201beef9704ce3a1e2fefbc5cb5c82ab820c51.1771066252.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1771066252.git.abrahamadekunle50@gmail.com","subject":"[PATCH v5 1/4] interactive -p: add new `--auto-advance` flag","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-14T11:03:54Z","receivedAt":"2026-02-14T11:03:48Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"When using the interactive add, reset, stash or checkout machinery,\nwe do not have the option of reworking with a file when selecting\nhunks, because the session automatically advances to the next file\nor ends if we have just one file.\n\nIntroduce the flag `--auto-advance` which auto advances by default,\nwhen interactively selecting patches with the '--patch' option.\nHowever, the `--no-auto-advance` option does not auto advance, thereby\nallowing users the option to rework with files.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-interactive.c     | 2 ++\n add-interactive.h     | 4 +++-\n builtin/add.c         | 4 ++++\n builtin/checkout.c    | 7 +++++++\n builtin/reset.c       | 4 ++++\n builtin/stash.c       | 8 ++++++++\n t/t9902-completion.sh | 1 +\n 7 files changed, 29 insertions(+), 1 deletion(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 95ec5a89f8..1580639682 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -64,6 +64,7 @@ void init_add_i_state(struct add_i_state *s, struct repository *r,\n \ts->r = r;\n \ts->context = -1;\n \ts->interhunkcontext = -1;\n+\ts->auto_advance = add_p_opt->auto_advance;\n \n \ts->use_color_interactive = check_color_config(r, \"color.interactive\");\n \n@@ -1017,6 +1018,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,\n \t\tstruct add_p_opt add_p_opt = {\n \t\t\t.context = s->context,\n \t\t\t.interhunkcontext = s->interhunkcontext,\n+\t\t\t.auto_advance = s->auto_advance\n \t\t};\n \t\tstruct strvec args = STRVEC_INIT;\n \t\tstruct pathspec ps_selected = { 0 };\ndiff --git a/add-interactive.h b/add-interactive.h\nindex da49502b76..7843397775 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -6,9 +6,10 @@\n struct add_p_opt {\n \tint context;\n \tint interhunkcontext;\n+\tint auto_advance;\n };\n \n-#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 }\n+#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1, .auto_advance = 1 }\n \n struct add_i_state {\n \tstruct repository *r;\n@@ -29,6 +30,7 @@ struct add_i_state {\n \tint use_single_key;\n \tchar *interactive_diff_filter, *interactive_diff_algorithm;\n \tint context, interhunkcontext;\n+\tint auto_advance;\n };\n \n void init_add_i_state(struct add_i_state *s, struct repository *r,\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 32709794b3..4357f87b7f 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -256,6 +256,8 @@ static struct option builtin_add_options[] = {\n \tOPT_GROUP(\"\"),\n \tOPT_BOOL('i', \"interactive\", &add_interactive, N_(\"interactive picking\")),\n \tOPT_BOOL('p', \"patch\", &patch_interactive, N_(\"select hunks interactively\")),\n+\tOPT_BOOL(0, \"auto-advance\", &add_p_opt.auto_advance,\n+\t\t N_(\"auto advance to the next file when selecting hunks interactively\")),\n \tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \tOPT_BOOL('e', \"edit\", &edit_interactive, N_(\"edit current diff and apply\")),\n@@ -418,6 +420,8 @@ int cmd_add(int argc,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--interactive/--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--interactive/--patch\");\n+\t\tif (!add_p_opt.auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--no-auto-advance\", \"--interactive/--patch\");\n \t}\n \n \tif (edit_interactive) {\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 0ba4f03f2e..fad35a9284 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -63,6 +63,7 @@ struct checkout_opts {\n \tint patch_mode;\n \tint patch_context;\n \tint patch_interhunk_context;\n+\tint auto_advance;\n \tint quiet;\n \tint merge;\n \tint force;\n@@ -111,6 +112,7 @@ struct checkout_opts {\n \t.merge = -1, \\\n \t.patch_context = -1, \\\n \t.patch_interhunk_context = -1, \\\n+\t.auto_advance = 1, \\\n }\n \n struct branch_info {\n@@ -549,6 +551,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tstruct add_p_opt add_p_opt = {\n \t\t\t.context = opts->patch_context,\n \t\t\t.interhunkcontext = opts->patch_interhunk_context,\n+\t\t\t.auto_advance = opts->auto_advance\n \t\t};\n \t\tconst char *rev = new_branch_info->name;\n \t\tchar rev_oid[GIT_MAX_HEXSZ + 1];\n@@ -1803,6 +1806,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (opts->patch_interhunk_context != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (!opts->auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--no-auto-advance\", \"--patch\");\n \t}\n \n \tif (opts->show_progress < 0) {\n@@ -2001,6 +2006,8 @@ int cmd_checkout(int argc,\n \t\tOPT_BOOL(0, \"guess\", &opts.dwim_new_local_branch,\n \t\t\t N_(\"second guess 'git checkout <no-such-branch>' (default)\")),\n \t\tOPT_BOOL(0, \"overlay\", &opts.overlay_mode, N_(\"use overlay mode (default)\")),\n+\t\tOPT_BOOL(0, \"auto-advance\", &opts.auto_advance,\n+\t\t\t N_(\"auto advance to the next file when selecting hunks interactively\")),\n \t\tOPT_END()\n \t};\n \ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex c48d9845f8..88f95f9fc7 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -371,6 +371,8 @@ int cmd_reset(int argc,\n \t\t\t       PARSE_OPT_OPTARG,\n \t\t\t       option_parse_recurse_submodules_worktree_updater),\n \t\tOPT_BOOL('p', \"patch\", &patch_mode, N_(\"select hunks interactively\")),\n+\t\tOPT_BOOL(0, \"auto-advance\", &add_p_opt.auto_advance,\n+\t\t\t N_(\"auto advance to the next file when selecting hunks interactively\")),\n \t\tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \t\tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \t\tOPT_BOOL('N', \"intent-to-add\", &intent_to_add,\n@@ -443,6 +445,8 @@ int cmd_reset(int argc,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (!add_p_opt.auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--no-auto-advance\", \"--patch\");\n \t}\n \n \t/* git reset tree [--] paths... can be used to\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 193e3ea47a..f98487f4cd 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1849,6 +1849,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \t\t\t N_(\"stash staged changes only\")),\n \t\tOPT_BOOL('p', \"patch\", &patch_mode,\n \t\t\t N_(\"stash in patch mode\")),\n+\t\tOPT_BOOL(0, \"auto-advance\", &add_p_opt.auto_advance,\n+\t\t\t N_(\"auto advance to the next file when selecting hunks interactively\")),\n \t\tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \t\tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n@@ -1911,6 +1913,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (!add_p_opt.auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--no-auto-advance\", \"--patch\");\n \t}\n \n \tif (add_p_opt.context < -1)\n@@ -1952,6 +1956,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,\n \t\t\t N_(\"stash staged changes only\")),\n \t\tOPT_BOOL('p', \"patch\", &patch_mode,\n \t\t\t N_(\"stash in patch mode\")),\n+\t\tOPT_BOOL(0, \"auto-advance\", &add_p_opt.auto_advance,\n+\t\t\t N_(\"auto advance to the next file when selecting hunks interactively\")),\n \t\tOPT_DIFF_UNIFIED(&add_p_opt.context),\n \t\tOPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext),\n \t\tOPT__QUIET(&quiet, N_(\"quiet mode\")),\n@@ -1983,6 +1989,8 @@ static int save_stash(int argc, const char **argv, const char *prefix,\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--unified\", \"--patch\");\n \t\tif (add_p_opt.interhunkcontext != -1)\n \t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--inter-hunk-context\", \"--patch\");\n+\t\tif (!add_p_opt.auto_advance)\n+\t\t\tdie(_(\"the option '%s' requires '%s'\"), \"--no-auto-advance\", \"--patch\");\n \t}\n \n \tret = do_push_stash(&ps, stash_msg, quiet, keep_index,\ndiff --git a/t/t9902-completion.sh b/t/t9902-completion.sh\nindex ffb9c8b522..2f9a597ec7 100755\n--- a/t/t9902-completion.sh\n+++ b/t/t9902-completion.sh\n@@ -2601,6 +2601,7 @@ test_expect_success 'double dash \"git checkout\"' '\n \t--ignore-skip-worktree-bits Z\n \t--ignore-other-worktrees Z\n \t--recurse-submodules Z\n+\t--auto-advance Z\n \t--progress Z\n \t--guess Z\n \t--no-guess Z\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"536013","messageId":"a3affdec45ee6ba9b9b7d133d70599c8f490bb05.1771066252.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1771066252.git.abrahamadekunle50@gmail.com","subject":"[PATCH v5 2/4] add-patch: modify patch_update_file() signature","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-14T11:04:44Z","receivedAt":"2026-02-14T11:04:48Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"The function `patch_update_file()` takes the add_p_state struct\npointer and the current `struct file_diff` pointer and returns an\nint.\n\nWhen using the `--no-auto-advance` flag, we want to be able to request\nthe next or previous file from the caller.\n\nModify the function signature to instead take the index of the\ncurrent `file_diff` and the `add_p_state` struct pointer so that we\ncan compute the `file_diff` from the index while also having\naccess to the file index. This will help us request the next or\nprevious file from the caller.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-patch.c | 32 +++++++++++++++++++-------------\n 1 file changed, 19 insertions(+), 13 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex df8f2e6d74..8e21ea1246 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1441,20 +1441,21 @@ static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx)\n \treturn false;\n }\n \n-static int patch_update_file(struct add_p_state *s,\n-\t\t\t     struct file_diff *file_diff)\n+static size_t patch_update_file(struct add_p_state *s, size_t idx)\n {\n \tsize_t hunk_index = 0;\n \tssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n \tstruct hunk *hunk;\n \tchar ch;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tint colored = !!s->colored.len, quit = 0, use_pager = 0;\n+\tint colored = !!s->colored.len, use_pager = 0;\n \tenum prompt_mode_type prompt_mode_type;\n+\tstruct file_diff *file_diff = s->file_diff + idx;\n+\tsize_t patch_update_resp = idx;\n \n \t/* Empty added files have no hunks */\n \tif (!file_diff->hunk_nr && !file_diff->added)\n-\t\treturn 0;\n+\t\treturn patch_update_resp + 1;\n \n \tstrbuf_reset(&s->buf);\n \trender_diff_header(s, file_diff, colored, &s->buf);\n@@ -1499,9 +1500,10 @@ static int patch_update_file(struct add_p_state *s,\n \n \t\t/* Everything decided? */\n \t\tif (undecided_previous < 0 && undecided_next < 0 &&\n-\t\t    hunk->use != UNDECIDED_HUNK)\n-\t\t\tbreak;\n-\n+\t\t    hunk->use != UNDECIDED_HUNK) {\n+\t\t\t\tpatch_update_resp++;\n+\t\t\t\tbreak;\n+\t\t}\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n \t\t\tif (rendered_hunk_index != hunk_index) {\n@@ -1577,7 +1579,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\tfputs(s->s.reset_color_interactive, stdout);\n \t\tfflush(stdout);\n \t\tif (read_single_character(s) == EOF) {\n-\t\t\tquit = 1;\n+\t\t\tpatch_update_resp = s->file_diff_nr;\n \t\t\tbreak;\n \t\t}\n \n@@ -1623,7 +1625,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\thunk->use = SKIP_HUNK;\n \t\t\t}\n \t\t} else if (ch == 'q') {\n-\t\t\tquit = 1;\n+\t\t\tpatch_update_resp = s->file_diff_nr;\n \t\t\tbreak;\n \t\t} else if (s->answer.buf[0] == 'K') {\n \t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n@@ -1810,7 +1812,7 @@ static int patch_update_file(struct add_p_state *s,\n \t}\n \n \tputchar('\\n');\n-\treturn quit;\n+\treturn patch_update_resp;\n }\n \n int run_add_p(struct repository *r, enum add_p_mode mode,\n@@ -1859,11 +1861,15 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\treturn -1;\n \t}\n \n-\tfor (i = 0; i < s.file_diff_nr; i++)\n-\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr)\n+\tfor (i = 0; i < s.file_diff_nr;) {\n+\t\tif (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) {\n \t\t\tbinary_count++;\n-\t\telse if (patch_update_file(&s, s.file_diff + i))\n+\t\t\ti++;\n+\t\t\tcontinue;\n+\t\t}\n+\t\t if ((i = patch_update_file(&s, i)) == s.file_diff_nr)\n \t\t\tbreak;\n+    }\n \n \tif (s.file_diff_nr == 0)\n \t\terr(&s, _(\"No changes.\"));\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"536014","messageId":"1ad4524023e246744b3d268db8a476b605d44223.1771066252.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1771066252.git.abrahamadekunle50@gmail.com","subject":"[PATCH v5 3/4] add-patch: allow all-or-none application of patches","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-14T11:06:06Z","receivedAt":"2026-02-14T11:06:03Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"When the flag `--no-auto-advance` is used with `--patch`,\nif the user has decided `USE` on a hunk in a file, goes to another\nfile, and then returns to this file and changes the previous\ndecision on the hunk to `SKIP`, because the patch has already\nbeen applied, the last decision is not registered and the now\nSKIPPED hunk is still applied.\n\nMove the logic for applying patches into a function so that we can\nreuse this logic to implement the all or non application of the patches\nafter the user is done with the hunk selection.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-patch.c | 62 ++++++++++++++++++++++++++++++-----------------------\n 1 file changed, 35 insertions(+), 27 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 8e21ea1246..07526e7fb6 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1420,6 +1420,40 @@ N_(\"j - go to the next undecided hunk, roll over at the bottom\\n\"\n    \"P - print the current hunk using the pager\\n\"\n    \"? - print help\\n\");\n \n+static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)\n+{\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tsize_t j;\n+\n+\t/* Any hunk to be used? */\n+\tfor (j = 0; j < file_diff->hunk_nr; j++)\n+\t\tif (file_diff->hunk[j].use == USE_HUNK)\n+\t\t\tbreak;\n+\n+\tif (j < file_diff->hunk_nr ||\n+\t\t(!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n+\t\t/* At least one hunk selected: apply */\n+\t\tstrbuf_reset(&s->buf);\n+\t\treassemble_patch(s, file_diff, 0, &s->buf);\n+\n+\t\tdiscard_index(s->s.r->index);\n+\t\tif (s->mode->apply_for_checkout)\n+\t\t\tapply_for_checkout(s, &s->buf,\n+\t\t\t\t\ts->mode->is_reverse);\n+\t\telse {\n+\t\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n+\t\t\tstrvec_pushv(&cp.args, s->mode->apply_args);\n+\t\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n+\t\t\t\t\tNULL, 0, NULL, 0))\n+\t\t\t\terror(_(\"'git apply' failed\"));\n+\t\t}\n+\t\tif (repo_read_index(s->s.r) >= 0)\n+\t\t\trepo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n+\t\t\t\t\t\t\t1, NULL, NULL, NULL);\n+\t}\n+\n+}\n+\n static size_t dec_mod(size_t a, size_t m)\n {\n \treturn a > 0 ? a - 1 : m - 1;\n@@ -1447,7 +1481,6 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx)\n \tssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;\n \tstruct hunk *hunk;\n \tchar ch;\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint colored = !!s->colored.len, use_pager = 0;\n \tenum prompt_mode_type prompt_mode_type;\n \tstruct file_diff *file_diff = s->file_diff + idx;\n@@ -1784,32 +1817,7 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t}\n \t}\n \n-\t/* Any hunk to be used? */\n-\tfor (i = 0; i < file_diff->hunk_nr; i++)\n-\t\tif (file_diff->hunk[i].use == USE_HUNK)\n-\t\t\tbreak;\n-\n-\tif (i < file_diff->hunk_nr ||\n-\t    (!file_diff->hunk_nr && file_diff->head.use == USE_HUNK)) {\n-\t\t/* At least one hunk selected: apply */\n-\t\tstrbuf_reset(&s->buf);\n-\t\treassemble_patch(s, file_diff, 0, &s->buf);\n-\n-\t\tdiscard_index(s->s.r->index);\n-\t\tif (s->mode->apply_for_checkout)\n-\t\t\tapply_for_checkout(s, &s->buf,\n-\t\t\t\t\t   s->mode->is_reverse);\n-\t\telse {\n-\t\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n-\t\t\tstrvec_pushv(&cp.args, s->mode->apply_args);\n-\t\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n-\t\t\t\t\t NULL, 0, NULL, 0))\n-\t\t\t\terror(_(\"'git apply' failed\"));\n-\t\t}\n-\t\tif (repo_read_index(s->s.r) >= 0)\n-\t\t\trepo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,\n-\t\t\t\t\t\t     1, NULL, NULL, NULL);\n-\t}\n+\tapply_patch(s, file_diff);\n \n \tputchar('\\n');\n \treturn patch_update_resp;\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"536015","messageId":"193fba4e336897a57a26e77c6eac74a05abc79c0.1771066252.git.abrahamadekunle50@gmail.com","threadId":"64857","inReplyTo":"cover.1771066252.git.abrahamadekunle50@gmail.com","subject":"[PATCH v5 4/4] add-patch: allow interfile navigation when selecting hunks","fromName":"Abraham Samuel Adekunle","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-14T11:06:55Z","receivedAt":"2026-02-14T11:06:47Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"After deciding on all hunks in a file, the interactive session\nadvances automatically to the next file if there is another,\nor the process ends.\n\nNow using the `--no-auto-advance` flag with `--patch`, the process\ndoes not advance automatically. A user can choose to go to the next\nfile by pressing '>' or the previous file by pressing '<', before or\nafter deciding on all hunks in the current file.\n\nAfter all hunks have been decided in a file, the user can still\nrework with the file by applying the options available in the permit\nset for that hunk, and after all the decisions, the user presses 'q'\nto submit.\nAfter all hunks have been decided, the user can press '?' which will\nshow the hunk selection summary in the help patch remainder text\nincluding the total hunks, number of hunks marked for use and number\nof hunks marked for skip.\n\nThis feature is enabled by passing the `--no-auto-advance` flag\nto `--patch` option of the subcommands add, stash, reset,\nand checkout.\n\nSigned-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com>\n---\n add-patch.c                |  66 ++++++++++++++++++++++--\n t/t3701-add-interactive.sh | 100 +++++++++++++++++++++++++++++++++++++\n 2 files changed, 161 insertions(+), 5 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex 07526e7fb6..b3fb08f416 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1418,7 +1418,10 @@ N_(\"j - go to the next undecided hunk, roll over at the bottom\\n\"\n    \"e - manually edit the current hunk\\n\"\n    \"p - print the current hunk\\n\"\n    \"P - print the current hunk using the pager\\n\"\n-   \"? - print help\\n\");\n+   \"> - go to the next file, roll over at the bottom\\n\"\n+   \"< - go to the previous file, roll over at the top\\n\"\n+   \"? - print help\\n\"\n+   \"HUNKS SUMMARY - Hunks: %d, USE: %d, SKIP: %d\\n\");\n \n static void apply_patch(struct add_p_state *s, struct file_diff *file_diff)\n {\n@@ -1483,6 +1486,7 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx)\n \tchar ch;\n \tint colored = !!s->colored.len, use_pager = 0;\n \tenum prompt_mode_type prompt_mode_type;\n+\tint all_decided = 0;\n \tstruct file_diff *file_diff = s->file_diff + idx;\n \tsize_t patch_update_resp = idx;\n \n@@ -1502,7 +1506,9 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t\tALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,\n \t\t\tALLOW_SEARCH_AND_GOTO = 1 << 4,\n \t\t\tALLOW_SPLIT = 1 << 5,\n-\t\t\tALLOW_EDIT = 1 << 6\n+\t\t\tALLOW_EDIT = 1 << 6,\n+\t\t\tALLOW_GOTO_PREVIOUS_FILE = 1 << 7,\n+\t\t\tALLOW_GOTO_NEXT_FILE = 1 << 8\n \t\t} permitted = 0;\n \n \t\tif (hunk_index >= file_diff->hunk_nr)\n@@ -1534,8 +1540,12 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t/* Everything decided? */\n \t\tif (undecided_previous < 0 && undecided_next < 0 &&\n \t\t    hunk->use != UNDECIDED_HUNK) {\n-\t\t\t\tpatch_update_resp++;\n-\t\t\t\tbreak;\n+\t\t\t\tif (!s->s.auto_advance)\n+\t\t\t\t\tall_decided = 1;\n+\t\t\t\telse {\n+\t\t\t\t\tpatch_update_resp++;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n \t\t}\n \t\tstrbuf_reset(&s->buf);\n \t\tif (file_diff->hunk_nr) {\n@@ -1584,6 +1594,14 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t\t\tpermitted |= ALLOW_EDIT;\n \t\t\t\tstrbuf_addstr(&s->buf, \",e\");\n \t\t\t}\n+\t\t\tif (!s->s.auto_advance && s->file_diff_nr > 1) {\n+\t\t\t\tpermitted |= ALLOW_GOTO_NEXT_FILE;\n+\t\t\t\tstrbuf_addstr(&s->buf, \",>\");\n+\t\t\t}\n+\t\t\tif (!s->s.auto_advance && s->file_diff_nr > 1) {\n+\t\t\t\tpermitted |= ALLOW_GOTO_PREVIOUS_FILE;\n+\t\t\t\tstrbuf_addstr(&s->buf, \",<\");\n+\t\t\t}\n \t\t\tstrbuf_addstr(&s->buf, \",p,P\");\n \t\t}\n \t\tif (file_diff->deleted)\n@@ -1660,6 +1678,28 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t} else if (ch == 'q') {\n \t\t\tpatch_update_resp = s->file_diff_nr;\n \t\t\tbreak;\n+\t\t} else if (!s->s.auto_advance && s->answer.buf[0] == '>') {\n+\t\t\tif (permitted & ALLOW_GOTO_NEXT_FILE) {\n+\t\t\t\tif (patch_update_resp == s->file_diff_nr - 1)\n+\t\t\t\t\tpatch_update_resp = 0;\n+\t\t\t\telse\n+\t\t\t\t\tpatch_update_resp++;\n+\t\t\t\tbreak;\n+\t\t\t} else {\n+\t\t\t\terr(s, _(\"No next file\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t} else if (!s->s.auto_advance && s->answer.buf[0] == '<') {\n+\t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_FILE) {\n+\t\t\t\tif (patch_update_resp == 0)\n+\t\t\t\t\tpatch_update_resp = s->file_diff_nr - 1;\n+\t\t\t\telse\n+\t\t\t\t\tpatch_update_resp--;\n+\t\t\t\tbreak;\n+\t\t\t} else {\n+\t\t\t\terr(s, _(\"No previous file\"));\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t} else if (s->answer.buf[0] == 'K') {\n \t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n \t\t\t\thunk_index = dec_mod(hunk_index,\n@@ -1805,6 +1845,18 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t\t\t * commands shown in the prompt that are not\n \t\t\t\t * always available.\n \t\t\t\t */\n+\t\t\t\tif (all_decided && !strncmp(p, \"HUNKS SUMMARY\", 13)) {\n+\t\t\t\t\tint total = file_diff->hunk_nr, used = 0, skipped = 0;\n+\n+\t\t\t\t\tfor (i = 0; i < file_diff->hunk_nr; i++) {\n+\t\t\t\t\t\tif (file_diff->hunk[i].use == USE_HUNK)\n+\t\t\t\t\t\t\tused += 1;\n+\t\t\t\t\t\tif (file_diff->hunk[i].use == SKIP_HUNK)\n+\t\t\t\t\t\t\tskipped += 1;\n+\t\t\t\t\t}\n+\t\t\t\t\tcolor_fprintf_ln(stdout, s->s.help_color, _(p),\n+\t\t\t\t\t\t\t total, used, skipped);\n+\t\t\t\t}\n \t\t\t\tif (*p != '?' && !strchr(s->buf.buf, *p))\n \t\t\t\t\tcontinue;\n \n@@ -1817,7 +1869,8 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx)\n \t\t}\n \t}\n \n-\tapply_patch(s, file_diff);\n+\tif (s->s.auto_advance)\n+\t\tapply_patch(s, file_diff);\n \n \tputchar('\\n');\n \treturn patch_update_resp;\n@@ -1878,6 +1931,9 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\t if ((i = patch_update_file(&s, i)) == s.file_diff_nr)\n \t\t\tbreak;\n     }\n+\tif (!s.s.auto_advance)\n+\t\tfor (i = 0; i < s.file_diff_nr; i++)\n+\t\t\tapply_patch(&s, s.file_diff + i);\n \n \tif (s.file_diff_nr == 0)\n \t\terr(&s, _(\"No changes.\"));\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 5ce9c6dd60..6e120a4001 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -1441,5 +1441,105 @@ test_expect_success 'EOF quits' '\n \ttest_grep file out &&\n \ttest_grep ! file2 out\n '\n+for cmd in add checkout reset \"stash save\" \"stash push\"\n+do\n+\ttest_expect_success \"$cmd rejects invalid --no-auto-advance options\" '\n+\t\ttest_must_fail git $cmd --no-auto-advance 2>actual &&\n+\t\ttest_grep -E  \"requires .*--(interactive|patch)\" actual\n+\t'\n+done\n+\n+test_expect_success 'manual advance (\">\") moves to next file with --no-auto-advance' '\n+\tgit reset --hard &&\n+\techo line1 >first-file &&\n+\techo line2 >second-file &&\n+\tgit add -A &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\techo change_first >>first-file &&\n+\techo change_second >>second-file &&\n+\n+\tprintf \">\\nq\\n\" | git add -p --no-auto-advance >output.test 2>&1 &&\n+\ttest_grep  -E \"(a|b)/second-file\" output.test\n+'\n+\n+test_expect_success 'select n on a hunk, go to another file, come back and change to y stages' '\n+\tgit reset --hard &&\n+\techo one >f1 &&\n+\techo one >f2 &&\n+\tgit add -A &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\techo change1 >>f1 &&\n+\techo change2 >>f2 &&\n+\n+\tprintf \"n\\n>\\n<\\ny\\nq\\n\" | git add -p --no-auto-advance >output.staged 2>&1 &&\n+\tgit diff --cached --name-only >staged &&\n+\ttest_grep -E \"(a/f1)\" output.staged\n+'\n+\n+test_expect_success 'select y on a hunk, go to another file, come back and change to n does not stage' '\n+\tgit reset --hard &&\n+\techo one >f1 &&\n+\techo one >f2 &&\n+\tgit add -A &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\techo change1 >>f1 &&\n+\techo change2 >>f2 &&\n+\n+\tprintf \"y\\n>\\n<\\nn\\nq\\n\" | git add -p --no-auto-advance >output.unstaged 2>&1 &&\n+\tgit diff --cached --name-only >staged &&\n+\ttest_must_be_empty staged\n+'\n+\n+test_expect_success 'deciding all hunks in a file does not auto advance' '\n+\tgit reset --hard &&\n+\techo line >stay &&\n+\techo line >other &&\n+\tgit add -A &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\techo change >>stay &&\n+\techo change >>other &&\n+\ttest_write_lines y | git add -p --no-auto-advance >raw-output 2>&1 &&\n+\ttest_grep \"(1/1) Stage this hunk (was: y)\" raw-output &&\n+\ttest_grep ! \"diff --git a/stay b/stay\" raw-output\n+'\n+test_expect_success 'HUNKS SUMMARY does not show in help text when there are undecided hunks' '\n+\tgit reset --hard &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >f &&\n+\tgit add f &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\ttest_write_lines 1 X 3 4 Y 6 7 Z 9 >f &&\n+\ttest_write_lines s y n | git add -p --no-auto-advance >raw-nostat 2>&1 &&\n+\ttest_grep ! \"HUNKS SUMMARY - Hunks: \" raw-nostat\n+'\n+\n+test_expect_success 'help text shows HUNK SUMMARY when all hunks have been decided' '\n+\tgit reset --hard &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >f2 &&\n+\tgit add f2 &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\ttest_write_lines 1 X 3 4 Y 6 7 Z 9 >f2 &&\n+\tprintf \"s\\ny\\nn\\ny\\n?\\n\" | git add -p --no-auto-advance >raw-stat 2>&1 &&\n+\ttest_grep \"HUNKS SUMMARY - Hunks: 3, USE: 2, SKIP: 1\" raw-stat\n+'\n+\n+test_expect_success 'selective staging across multiple files with --no-advance' '\n+\tgit reset --hard &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >a.file &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >b.file &&\n+\ttest_write_lines 1 2 3 4 5 6 7 8 9 >c.file &&\n+\tgit add -A &&\n+\tgit commit -m initial >/dev/null 2>&1 &&\n+\ttest_write_lines 1 A2 3 4 A5 6 7 8 9 >a.file &&\n+\ttest_write_lines 1 2 B3 4 5 6 7 B8 9 >b.file &&\n+\ttest_write_lines C1 2 3 4 5 C6 7 8 9 >c.file &&\n+\tprintf \"s\\ny\\nn\\n>\\ns\\nn\\ny\\n>\\ns\\ny\\ny\\nq\\n\" | git add -p --no-auto-advance >output.index 2>&1 &&\n+\tgit diff --cached >staged.diff &&\n+\ttest_grep \"+A2\" staged.diff &&\n+\ttest_grep ! \"+A5\" staged.diff &&\n+\ttest_grep \"+B8\" staged.diff &&\n+\ttest_grep ! \"+B3\" staged.diff &&\n+\ttest_grep \"+C1\" staged.diff &&\n+\ttest_grep \"+C6\" staged.diff\n+'\n \n test_done\n-- \n2.39.5 (Apple Git-154)\n\n"},{"id":"536556","messageId":"xmqqwm07uju7.fsf@gitster.g","threadId":"64857","inReplyTo":"cover.1771066252.git.abrahamadekunle50@gmail.com","subject":"Re: [PATCH v5 0/4] introduce new option `--auto-advance`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-02-20T22:32:48Z","receivedAt":"2026-02-20T22:32:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n\n> After after more reviews and deliberations, I have been able to\n> rename the new option name to `--auto-advance`, where the\n> --no-auto-advance implements the feature and does not auto advance\n> while --auto-advance is the default and maintains the current\n> behaviour.\n\nHaven't seen any reviews on this latest round, and I think what we\nhave here may be good enough.  Shall we merge it down to 'next'?\nAny comments?\n\nThanks.\n"},{"id":"536579","messageId":"CADYq+fZcR6Mry-j4XFF2d1SAVPtpQ7Hmc-XkYiuQAfDjqgaBgQ@mail.gmail.com","threadId":"64857","inReplyTo":"xmqqwm07uju7.fsf@gitster.g","subject":"Re: [PATCH v5 0/4] introduce new option `--auto-advance`","fromName":"Samuel Abraham","fromEmail":"abrahamadekunle50@gmail.com","sentAt":"2026-02-21T09:06:59Z","receivedAt":"2026-02-21T09:06:59Z","isPatch":true,"sender":{"key":"abrahamadekunle50@gmail.com","avatar":"https://avatars.githubusercontent.com/u/110066922?v=4"},"body":"On Fri, Feb 20, 2026 at 11:32 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> writes:\n>\n> > After after more reviews and deliberations, I have been able to\n> > rename the new option name to `--auto-advance`, where the\n> > --no-auto-advance implements the feature and does not auto advance\n> > while --auto-advance is the default and maintains the current\n> > behaviour.\n>\n> Haven't seen any reviews on this latest round, and I think what we\n> have here may be good enough.  Shall we merge it down to 'next'?\n> Any comments?\n>\nOkay.\nThank you Junio, Phillip and everyone for the patience and guidance\nin completing this patch series and also the one for my GSoC microproject.\nIt has been a great learning process.\nI appreciate it.\nThanks\n\nAbraham\n"}]}