{"thread":{"id":"54057","subject":"[PATCH 0/2] add p in C tweaks","startedAt":"2020-08-17T13:23:15Z","lastAt":"2020-08-19T10:00:05Z","messageCount":8,"participants":["Phillip Wood via GitGitGadget","Junio C Hamano","Johannes Schindelin","Phillip Wood"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"403817","messageId":"pull.702.git.1597670589.gitgitgadget@gmail.com","threadId":"54057","inReplyTo":null,"subject":"[PATCH 0/2] add p in C tweaks","fromName":"Phillip Wood via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T13:23:06Z","receivedAt":"2020-08-17T13:23:15Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"A code cleanup and small bug fix for the C version of add -p\n\ndscho has pointed out that the bug fix in the second patch gets lost in the\nother changes and suggested adding the last member of the enum (which fixes\nthe bug with handling 'e') as a separate patch. I'm unsure as it feels odd\nto split up the introduction of the flags - I'd be interested to hear what\nothers think.\n\nPhillip Wood (2):\n  add -p: use ALLOC_GROW_BY instead of ALLOW_GROW\n  add -p: fix checking of user input\n\n add-patch.c | 67 +++++++++++++++++++++++++++++++++--------------------\n 1 file changed, 42 insertions(+), 25 deletions(-)\n\n\nbase-commit: 47ae905ffb98cc4d4fd90083da6bc8dab55d9ecc\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-702%2Fphillipwood%2Fwip%2Fadd-p-fixes-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-702/phillipwood/wip/add-p-fixes-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/702\n-- \ngitgitgadget\n"},{"id":"403818","messageId":"ae6ace43af9b25ee8f67b01ed1ec50064c8b006f.1597670589.git.gitgitgadget@gmail.com","threadId":"54057","inReplyTo":"pull.702.git.1597670589.gitgitgadget@gmail.com","subject":"[PATCH 1/2] add -p: use ALLOC_GROW_BY instead of ALLOW_GROW","fromName":"Phillip Wood via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T13:23:07Z","receivedAt":"2020-08-17T13:23:17Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nThis simplifies the code slightly, especially the third case where\nhunk_nr was incremented a few lines before ALLOC_GROW().\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n add-patch.c | 13 ++++---------\n 1 file changed, 4 insertions(+), 9 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex f899389e2c..a15fa407be 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -457,11 +457,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n \t\t\teol = pend;\n \n \t\tif (starts_with(p, \"diff \")) {\n-\t\t\ts->file_diff_nr++;\n-\t\t\tALLOC_GROW(s->file_diff, s->file_diff_nr,\n+\t\t\tALLOC_GROW_BY(s->file_diff, s->file_diff_nr, 1,\n \t\t\t\t   file_diff_alloc);\n \t\t\tfile_diff = s->file_diff + s->file_diff_nr - 1;\n-\t\t\tmemset(file_diff, 0, sizeof(*file_diff));\n \t\t\thunk = &file_diff->head;\n \t\t\thunk->start = p - plain->buf;\n \t\t\tif (colored_p)\n@@ -483,11 +481,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n \t\t\t\t */\n \t\t\t\thunk->splittable_into++;\n \n-\t\t\tfile_diff->hunk_nr++;\n-\t\t\tALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,\n+\t\t\tALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,\n \t\t\t\t   file_diff->hunk_alloc);\n \t\t\thunk = file_diff->hunk + file_diff->hunk_nr - 1;\n-\t\t\tmemset(hunk, 0, sizeof(*hunk));\n \n \t\t\thunk->start = p - plain->buf;\n \t\t\tif (colored)\n@@ -511,7 +507,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n \t\t\tif (file_diff->mode_change)\n \t\t\t\tBUG(\"double mode change?\\n\\n%.*s\",\n \t\t\t\t    (int)(eol - plain->buf), plain->buf);\n-\t\t\tif (file_diff->hunk_nr++)\n+\t\t\tif (file_diff->hunk_nr)\n \t\t\t\tBUG(\"mode change in the middle?\\n\\n%.*s\",\n \t\t\t\t    (int)(eol - plain->buf), plain->buf);\n \n@@ -520,9 +516,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n \t\t\t * is _part of_ the header \"hunk\".\n \t\t\t */\n \t\t\tfile_diff->mode_change = 1;\n-\t\t\tALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,\n+\t\t\tALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,\n \t\t\t\t   file_diff->hunk_alloc);\n-\t\t\tmemset(file_diff->hunk, 0, sizeof(struct hunk));\n \t\t\tfile_diff->hunk->start = p - plain->buf;\n \t\t\tif (colored_p)\n \t\t\t\tfile_diff->hunk->colored_start =\n-- \ngitgitgadget\n\n"},{"id":"403819","messageId":"752e13fb9fd9fd7930d83a0915dbbc0274a99908.1597670589.git.gitgitgadget@gmail.com","threadId":"54057","inReplyTo":"pull.702.git.1597670589.gitgitgadget@gmail.com","subject":"[PATCH 2/2] add -p: fix checking of user input","fromName":"Phillip Wood via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-08-17T13:23:08Z","receivedAt":"2020-08-17T13:23:18Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nWhen a file has been deleted the C version of add -p allows the user\nto edit a hunk even though 'e' is not in the list of allowed\nresponses. (I think 'e' is disallowed because if the file is edited it\nis no longer a deletion and we're not set up to rewrite the diff\nheader).\n\nThe invalid response was allowed because the test that determines\nwhether to display 'e' was not duplicated correctly in the code that\nprocesses the user's choice. Fix this by using flags that are set when\nconstructing the prompt and checked when processing the user's choice\nrather than repeating the check itself.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n add-patch.c | 54 +++++++++++++++++++++++++++++++++++++----------------\n 1 file changed, 38 insertions(+), 16 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex a15fa407be..907c05b3c1 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -1352,6 +1352,15 @@ static int patch_update_file(struct add_p_state *s,\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint colored = !!s->colored.len, quit = 0;\n \tenum prompt_mode_type prompt_mode_type;\n+\tenum {\n+\t\tALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,\n+\t\tALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,\n+\t\tALLOW_GOTO_NEXT_HUNK = 1 << 2,\n+\t\tALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,\n+\t\tALLOW_SEARCH_AND_GOTO = 1 << 4,\n+\t\tALLOW_SPLIT = 1 << 5,\n+\t\tALLOW_EDIT = 1 << 6\n+\t} permitted = 0;\n \n \tif (!file_diff->hunk_nr)\n \t\treturn 0;\n@@ -1388,22 +1397,35 @@ static int patch_update_file(struct add_p_state *s,\n \t\tfputs(s->buf.buf, stdout);\n \n \t\tstrbuf_reset(&s->buf);\n-\t\tif (undecided_previous >= 0)\n+\t\tif (undecided_previous >= 0) {\n+\t\t\tpermitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;\n \t\t\tstrbuf_addstr(&s->buf, \",k\");\n-\t\tif (hunk_index)\n+\t\t}\n+\t\tif (hunk_index) {\n+\t\t\tpermitted |= ALLOW_GOTO_PREVIOUS_HUNK;\n \t\t\tstrbuf_addstr(&s->buf, \",K\");\n-\t\tif (undecided_next >= 0)\n+\t\t}\n+\t\tif (undecided_next >= 0) {\n+\t\t\tpermitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;\n \t\t\tstrbuf_addstr(&s->buf, \",j\");\n-\t\tif (hunk_index + 1 < file_diff->hunk_nr)\n+\t\t}\n+\t\tif (hunk_index + 1 < file_diff->hunk_nr) {\n+\t\t\tpermitted |= ALLOW_GOTO_NEXT_HUNK;\n \t\t\tstrbuf_addstr(&s->buf, \",J\");\n-\t\tif (file_diff->hunk_nr > 1)\n+\t\t}\n+\t\tif (file_diff->hunk_nr > 1) {\n+\t\t\tpermitted |= ALLOW_SEARCH_AND_GOTO;\n \t\t\tstrbuf_addstr(&s->buf, \",g,/\");\n-\t\tif (hunk->splittable_into > 1)\n+\t\t}\n+\t\tif (hunk->splittable_into > 1) {\n+\t\t\tpermitted |= ALLOW_SPLIT;\n \t\t\tstrbuf_addstr(&s->buf, \",s\");\n+\t\t}\n \t\tif (hunk_index + 1 > file_diff->mode_change &&\n-\t\t    !file_diff->deleted)\n+\t\t    !file_diff->deleted) {\n+\t\t\tpermitted |= ALLOW_EDIT;\n \t\t\tstrbuf_addstr(&s->buf, \",e\");\n-\n+\t\t}\n \t\tif (file_diff->deleted)\n \t\t\tprompt_mode_type = PROMPT_DELETION;\n \t\telse if (file_diff->added)\n@@ -1452,22 +1474,22 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\tbreak;\n \t\t\t}\n \t\t} else if (s->answer.buf[0] == 'K') {\n-\t\t\tif (hunk_index)\n+\t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n \t\t\t\thunk_index--;\n \t\t\telse\n \t\t\t\terr(s, _(\"No previous hunk\"));\n \t\t} else if (s->answer.buf[0] == 'J') {\n-\t\t\tif (hunk_index + 1 < file_diff->hunk_nr)\n+\t\t\tif (permitted & ALLOW_GOTO_NEXT_HUNK)\n \t\t\t\thunk_index++;\n \t\t\telse\n \t\t\t\terr(s, _(\"No next hunk\"));\n \t\t} else if (s->answer.buf[0] == 'k') {\n-\t\t\tif (undecided_previous >= 0)\n+\t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)\n \t\t\t\thunk_index = undecided_previous;\n \t\t\telse\n \t\t\t\terr(s, _(\"No previous hunk\"));\n \t\t} else if (s->answer.buf[0] == 'j') {\n-\t\t\tif (undecided_next >= 0)\n+\t\t\tif (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)\n \t\t\t\thunk_index = undecided_next;\n \t\t\telse\n \t\t\t\terr(s, _(\"No next hunk\"));\n@@ -1475,7 +1497,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\tchar *pend;\n \t\t\tunsigned long response;\n \n-\t\t\tif (file_diff->hunk_nr < 2) {\n+\t\t\tif (!(permitted & ALLOW_SEARCH_AND_GOTO)) {\n \t\t\t\terr(s, _(\"No other hunks to goto\"));\n \t\t\t\tcontinue;\n \t\t\t}\n@@ -1512,7 +1534,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\tregex_t regex;\n \t\t\tint ret;\n \n-\t\t\tif (file_diff->hunk_nr < 2) {\n+\t\t\tif (!(permitted & ALLOW_SEARCH_AND_GOTO)) {\n \t\t\t\terr(s, _(\"No other hunks to search\"));\n \t\t\t\tcontinue;\n \t\t\t}\n@@ -1557,7 +1579,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\thunk_index = i;\n \t\t} else if (s->answer.buf[0] == 's') {\n \t\t\tsize_t splittable_into = hunk->splittable_into;\n-\t\t\tif (splittable_into < 2)\n+\t\t\tif (!(permitted & ALLOW_SPLIT))\n \t\t\t\terr(s, _(\"Sorry, cannot split this hunk\"));\n \t\t\telse if (!split_hunk(s, file_diff,\n \t\t\t\t\t     hunk - file_diff->hunk))\n@@ -1565,7 +1587,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t\t\t\t _(\"Split into %d hunks.\"),\n \t\t\t\t\t\t (int)splittable_into);\n \t\t} else if (s->answer.buf[0] == 'e') {\n-\t\t\tif (hunk_index + 1 == file_diff->mode_change)\n+\t\t\tif (!(permitted & ALLOW_EDIT))\n \t\t\t\terr(s, _(\"Sorry, cannot edit this hunk\"));\n \t\t\telse if (edit_hunk_loop(s, file_diff, hunk) >= 0) {\n \t\t\t\thunk->use = USE_HUNK;\n-- \ngitgitgadget\n"},{"id":"403866","messageId":"xmqqy2md15pe.fsf@gitster.c.googlers.com","threadId":"54057","inReplyTo":"ae6ace43af9b25ee8f67b01ed1ec50064c8b006f.1597670589.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/2] add -p: use ALLOC_GROW_BY instead of ALLOW_GROW","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-08-17T18:35:09Z","receivedAt":"2020-08-17T18:35:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Phillip Wood via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> This simplifies the code slightly, especially the third case where\n> hunk_nr was incremented a few lines before ALLOC_GROW().\n\nOK, we lose memset()s that appear to be separate but is a part of\nadding more elements.  Makes sense.\n\n>\n> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> ---\n>  add-patch.c | 13 ++++---------\n>  1 file changed, 4 insertions(+), 9 deletions(-)\n>\n> diff --git a/add-patch.c b/add-patch.c\n> index f899389e2c..a15fa407be 100644\n> --- a/add-patch.c\n> +++ b/add-patch.c\n> @@ -457,11 +457,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n>  \t\t\teol = pend;\n>  \n>  \t\tif (starts_with(p, \"diff \")) {\n> -\t\t\ts->file_diff_nr++;\n> -\t\t\tALLOC_GROW(s->file_diff, s->file_diff_nr,\n> +\t\t\tALLOC_GROW_BY(s->file_diff, s->file_diff_nr, 1,\n>  \t\t\t\t   file_diff_alloc);\n>  \t\t\tfile_diff = s->file_diff + s->file_diff_nr - 1;\n> -\t\t\tmemset(file_diff, 0, sizeof(*file_diff));\n>  \t\t\thunk = &file_diff->head;\n>  \t\t\thunk->start = p - plain->buf;\n>  \t\t\tif (colored_p)\n> @@ -483,11 +481,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n>  \t\t\t\t */\n>  \t\t\t\thunk->splittable_into++;\n>  \n> -\t\t\tfile_diff->hunk_nr++;\n> -\t\t\tALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,\n> +\t\t\tALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,\n>  \t\t\t\t   file_diff->hunk_alloc);\n>  \t\t\thunk = file_diff->hunk + file_diff->hunk_nr - 1;\n> -\t\t\tmemset(hunk, 0, sizeof(*hunk));\n>  \n>  \t\t\thunk->start = p - plain->buf;\n>  \t\t\tif (colored)\n> @@ -511,7 +507,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n>  \t\t\tif (file_diff->mode_change)\n>  \t\t\t\tBUG(\"double mode change?\\n\\n%.*s\",\n>  \t\t\t\t    (int)(eol - plain->buf), plain->buf);\n> -\t\t\tif (file_diff->hunk_nr++)\n> +\t\t\tif (file_diff->hunk_nr)\n>  \t\t\t\tBUG(\"mode change in the middle?\\n\\n%.*s\",\n>  \t\t\t\t    (int)(eol - plain->buf), plain->buf);\n>  \n> @@ -520,9 +516,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n>  \t\t\t * is _part of_ the header \"hunk\".\n>  \t\t\t */\n>  \t\t\tfile_diff->mode_change = 1;\n> -\t\t\tALLOC_GROW(file_diff->hunk, file_diff->hunk_nr,\n> +\t\t\tALLOC_GROW_BY(file_diff->hunk, file_diff->hunk_nr, 1,\n>  \t\t\t\t   file_diff->hunk_alloc);\n> -\t\t\tmemset(file_diff->hunk, 0, sizeof(struct hunk));\n>  \t\t\tfile_diff->hunk->start = p - plain->buf;\n>  \t\t\tif (colored_p)\n>  \t\t\t\tfile_diff->hunk->colored_start =\n"},{"id":"403931","messageId":"nycvar.QRO.7.76.6.2008180837540.56@tvgsbejvaqbjf.bet","threadId":"54057","inReplyTo":"752e13fb9fd9fd7930d83a0915dbbc0274a99908.1597670589.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/2] add -p: fix checking of user input","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2020-08-18T06:38:35Z","receivedAt":"2020-08-18T12:29:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Mon, 17 Aug 2020, Phillip Wood via GitGitGadget wrote:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> When a file has been deleted the C version of add -p allows the user\n> to edit a hunk even though 'e' is not in the list of allowed\n> responses. (I think 'e' is disallowed because if the file is edited it\n> is no longer a deletion and we're not set up to rewrite the diff\n> header).\n>\n> The invalid response was allowed because the test that determines\n> whether to display 'e' was not duplicated correctly in the code that\n> processes the user's choice. Fix this by using flags that are set when\n> constructing the prompt and checked when processing the user's choice\n> rather than repeating the check itself.\n>\n> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nAs I had mentioned on the PR, I would much rather have a bug-for-bug\nconversion to use the `enum`, and the fix for the `edit` case as a\nseparate patch on top.\n\nThank you,\nDscho\n\n> ---\n>  add-patch.c | 54 +++++++++++++++++++++++++++++++++++++----------------\n>  1 file changed, 38 insertions(+), 16 deletions(-)\n>\n> diff --git a/add-patch.c b/add-patch.c\n> index a15fa407be..907c05b3c1 100644\n> --- a/add-patch.c\n> +++ b/add-patch.c\n> @@ -1352,6 +1352,15 @@ static int patch_update_file(struct add_p_state *s,\n>  \tstruct child_process cp = CHILD_PROCESS_INIT;\n>  \tint colored = !!s->colored.len, quit = 0;\n>  \tenum prompt_mode_type prompt_mode_type;\n> +\tenum {\n> +\t\tALLOW_GOTO_PREVIOUS_HUNK = 1 << 0,\n> +\t\tALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK = 1 << 1,\n> +\t\tALLOW_GOTO_NEXT_HUNK = 1 << 2,\n> +\t\tALLOW_GOTO_NEXT_UNDECIDED_HUNK = 1 << 3,\n> +\t\tALLOW_SEARCH_AND_GOTO = 1 << 4,\n> +\t\tALLOW_SPLIT = 1 << 5,\n> +\t\tALLOW_EDIT = 1 << 6\n> +\t} permitted = 0;\n>\n>  \tif (!file_diff->hunk_nr)\n>  \t\treturn 0;\n> @@ -1388,22 +1397,35 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\tfputs(s->buf.buf, stdout);\n>\n>  \t\tstrbuf_reset(&s->buf);\n> -\t\tif (undecided_previous >= 0)\n> +\t\tif (undecided_previous >= 0) {\n> +\t\t\tpermitted |= ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK;\n>  \t\t\tstrbuf_addstr(&s->buf, \",k\");\n> -\t\tif (hunk_index)\n> +\t\t}\n> +\t\tif (hunk_index) {\n> +\t\t\tpermitted |= ALLOW_GOTO_PREVIOUS_HUNK;\n>  \t\t\tstrbuf_addstr(&s->buf, \",K\");\n> -\t\tif (undecided_next >= 0)\n> +\t\t}\n> +\t\tif (undecided_next >= 0) {\n> +\t\t\tpermitted |= ALLOW_GOTO_NEXT_UNDECIDED_HUNK;\n>  \t\t\tstrbuf_addstr(&s->buf, \",j\");\n> -\t\tif (hunk_index + 1 < file_diff->hunk_nr)\n> +\t\t}\n> +\t\tif (hunk_index + 1 < file_diff->hunk_nr) {\n> +\t\t\tpermitted |= ALLOW_GOTO_NEXT_HUNK;\n>  \t\t\tstrbuf_addstr(&s->buf, \",J\");\n> -\t\tif (file_diff->hunk_nr > 1)\n> +\t\t}\n> +\t\tif (file_diff->hunk_nr > 1) {\n> +\t\t\tpermitted |= ALLOW_SEARCH_AND_GOTO;\n>  \t\t\tstrbuf_addstr(&s->buf, \",g,/\");\n> -\t\tif (hunk->splittable_into > 1)\n> +\t\t}\n> +\t\tif (hunk->splittable_into > 1) {\n> +\t\t\tpermitted |= ALLOW_SPLIT;\n>  \t\t\tstrbuf_addstr(&s->buf, \",s\");\n> +\t\t}\n>  \t\tif (hunk_index + 1 > file_diff->mode_change &&\n> -\t\t    !file_diff->deleted)\n> +\t\t    !file_diff->deleted) {\n> +\t\t\tpermitted |= ALLOW_EDIT;\n>  \t\t\tstrbuf_addstr(&s->buf, \",e\");\n> -\n> +\t\t}\n>  \t\tif (file_diff->deleted)\n>  \t\t\tprompt_mode_type = PROMPT_DELETION;\n>  \t\telse if (file_diff->added)\n> @@ -1452,22 +1474,22 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\t\tbreak;\n>  \t\t\t}\n>  \t\t} else if (s->answer.buf[0] == 'K') {\n> -\t\t\tif (hunk_index)\n> +\t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_HUNK)\n>  \t\t\t\thunk_index--;\n>  \t\t\telse\n>  \t\t\t\terr(s, _(\"No previous hunk\"));\n>  \t\t} else if (s->answer.buf[0] == 'J') {\n> -\t\t\tif (hunk_index + 1 < file_diff->hunk_nr)\n> +\t\t\tif (permitted & ALLOW_GOTO_NEXT_HUNK)\n>  \t\t\t\thunk_index++;\n>  \t\t\telse\n>  \t\t\t\terr(s, _(\"No next hunk\"));\n>  \t\t} else if (s->answer.buf[0] == 'k') {\n> -\t\t\tif (undecided_previous >= 0)\n> +\t\t\tif (permitted & ALLOW_GOTO_PREVIOUS_UNDECIDED_HUNK)\n>  \t\t\t\thunk_index = undecided_previous;\n>  \t\t\telse\n>  \t\t\t\terr(s, _(\"No previous hunk\"));\n>  \t\t} else if (s->answer.buf[0] == 'j') {\n> -\t\t\tif (undecided_next >= 0)\n> +\t\t\tif (permitted & ALLOW_GOTO_NEXT_UNDECIDED_HUNK)\n>  \t\t\t\thunk_index = undecided_next;\n>  \t\t\telse\n>  \t\t\t\terr(s, _(\"No next hunk\"));\n> @@ -1475,7 +1497,7 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\tchar *pend;\n>  \t\t\tunsigned long response;\n>\n> -\t\t\tif (file_diff->hunk_nr < 2) {\n> +\t\t\tif (!(permitted & ALLOW_SEARCH_AND_GOTO)) {\n>  \t\t\t\terr(s, _(\"No other hunks to goto\"));\n>  \t\t\t\tcontinue;\n>  \t\t\t}\n> @@ -1512,7 +1534,7 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\tregex_t regex;\n>  \t\t\tint ret;\n>\n> -\t\t\tif (file_diff->hunk_nr < 2) {\n> +\t\t\tif (!(permitted & ALLOW_SEARCH_AND_GOTO)) {\n>  \t\t\t\terr(s, _(\"No other hunks to search\"));\n>  \t\t\t\tcontinue;\n>  \t\t\t}\n> @@ -1557,7 +1579,7 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\thunk_index = i;\n>  \t\t} else if (s->answer.buf[0] == 's') {\n>  \t\t\tsize_t splittable_into = hunk->splittable_into;\n> -\t\t\tif (splittable_into < 2)\n> +\t\t\tif (!(permitted & ALLOW_SPLIT))\n>  \t\t\t\terr(s, _(\"Sorry, cannot split this hunk\"));\n>  \t\t\telse if (!split_hunk(s, file_diff,\n>  \t\t\t\t\t     hunk - file_diff->hunk))\n> @@ -1565,7 +1587,7 @@ static int patch_update_file(struct add_p_state *s,\n>  \t\t\t\t\t\t _(\"Split into %d hunks.\"),\n>  \t\t\t\t\t\t (int)splittable_into);\n>  \t\t} else if (s->answer.buf[0] == 'e') {\n> -\t\t\tif (hunk_index + 1 == file_diff->mode_change)\n> +\t\t\tif (!(permitted & ALLOW_EDIT))\n>  \t\t\t\terr(s, _(\"Sorry, cannot edit this hunk\"));\n>  \t\t\telse if (edit_hunk_loop(s, file_diff, hunk) >= 0) {\n>  \t\t\t\thunk->use = USE_HUNK;\n> --\n> gitgitgadget\n>\n"},{"id":"403932","messageId":"nycvar.QRO.7.76.6.2008180838430.56@tvgsbejvaqbjf.bet","threadId":"54057","inReplyTo":"pull.702.git.1597670589.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/2] add p in C tweaks","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2020-08-18T06:39:07Z","receivedAt":"2020-08-18T12:30:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Mon, 17 Aug 2020, Phillip Wood via GitGitGadget wrote:\n\n> A code cleanup and small bug fix for the C version of add -p\n>\n> dscho has pointed out that the bug fix in the second patch gets lost in the\n> other changes and suggested adding the last member of the enum (which fixes\n> the bug with handling 'e') as a separate patch. I'm unsure as it feels odd\n> to split up the introduction of the flags - I'd be interested to hear what\n> others think.\n\nYep, apart from this issue I am fine with the patches as-are.\n\nThanks,\nDscho\n\n>\n> Phillip Wood (2):\n>   add -p: use ALLOC_GROW_BY instead of ALLOW_GROW\n>   add -p: fix checking of user input\n>\n>  add-patch.c | 67 +++++++++++++++++++++++++++++++++--------------------\n>  1 file changed, 42 insertions(+), 25 deletions(-)\n>\n>\n> base-commit: 47ae905ffb98cc4d4fd90083da6bc8dab55d9ecc\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-702%2Fphillipwood%2Fwip%2Fadd-p-fixes-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-702/phillipwood/wip/add-p-fixes-v1\n> Pull-Request: https://github.com/gitgitgadget/git/pull/702\n> --\n> gitgitgadget\n>\n"},{"id":"403966","messageId":"xmqqmu2ryc0l.fsf@gitster.c.googlers.com","threadId":"54057","inReplyTo":"pull.702.git.1597670589.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/2] add p in C tweaks","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-08-18T19:44:42Z","receivedAt":"2020-08-18T19:44:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Phillip Wood via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> A code cleanup and small bug fix for the C version of add -p\n>\n> dscho has pointed out that the bug fix in the second patch gets lost in the\n> other changes and suggested adding the last member of the enum (which fixes\n> the bug with handling 'e') as a separate patch. I'm unsure as it feels odd\n> to split up the introduction of the flags - I'd be interested to hear what\n> others think.\n\nEssentially, the original was doing:\n\n    - In early part of patch_update_file(), decide what option to show\n      in s->buf using \"if (undecided_previous >= 0)\" etc. boolean\n      expression that is tailored for each command;\n\n    - In later part of patch_update_file(), after getting an answer\n      to the end user, try to use the same boolean expression that\n      is tailored for each command to see if the given command is\n      acceptable.\n\nand the bug was that each pair of boolean expressions that are\nsupposed to be identical were duplicated in two places, and one pair\nwas not identical by mistake.\n\nYour [2/2] fixes it by turning the above to\n\n    - In early part of patch_update_file(), decide what option to show\n      in s->buf using \"if (undecided_previous >= 0)\" etc. boolean\n      expression that is tailored for each command, *AND* record the\n      fact that the command is allowed in the permitted bitmask.\n\n    - In later part of patch_update_file(), after getting an answer\n      to the end user, consult the permitted bitmask computed\n      earlier to see if the given command is acceptable.\n\nSince there no longer is duplicated boolean expressions that are\nsupposed to be the same but different by a bug, once this conversion\nis made, it is impossible to have the bug.  For that reason, I do\nnot think the suggested split makes much sense.\n\nA much saner split, if we have to split this step into two, would be\nto first fix the bug keeping the code structure of the original,\ni.e. the later part guards the 'e' command with\n\n\tif (hunk_index + 1 == file_diff->mode_change)\n\nbut the earlier part also required !file_diff->deleted, i.e. the\ncondition should have been\n\n\tif (hunk_index + 1 > file_diff->mode_change && !file_diff->deleted)\n\nSo without introducing enum and permitted bitmask, you can fix the\nbug in place, replacing the incorrect boolean condition in the later\npart that guards the 'e' command with a corrected one.  That would\nbe a minimum fix that can become your new [2/2], whose theme is \"to\nfix the bug with minumum change\".\n\nOn top of that, you can convert the function again to reach the\nfinal shape that writes each boolean condition only once and records\nthe permitted commands in the bitmask.  That can be your new [3/2],\nwhose these is \"to make it impossible to introduce the bug previous\nstep fixed\".\n\n\n> Phillip Wood (2):\n>   add -p: use ALLOC_GROW_BY instead of ALLOW_GROW\n>   add -p: fix checking of user input\n>\n>  add-patch.c | 67 +++++++++++++++++++++++++++++++++--------------------\n>  1 file changed, 42 insertions(+), 25 deletions(-)\n>\n>\n> base-commit: 47ae905ffb98cc4d4fd90083da6bc8dab55d9ecc\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-702%2Fphillipwood%2Fwip%2Fadd-p-fixes-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-702/phillipwood/wip/add-p-fixes-v1\n> Pull-Request: https://github.com/gitgitgadget/git/pull/702\n"},{"id":"403997","messageId":"744e464f-6378-ef68-01c1-3b8bf63c54a4@gmail.com","threadId":"54057","inReplyTo":"xmqqmu2ryc0l.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 0/2] add p in C tweaks","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-08-19T09:59:58Z","receivedAt":"2020-08-19T10:00:05Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 18/08/2020 20:44, Junio C Hamano wrote:\n> \"Phillip Wood via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n>> A code cleanup and small bug fix for the C version of add -p\n>>\n>> dscho has pointed out that the bug fix in the second patch gets lost in the\n>> other changes and suggested adding the last member of the enum (which fixes\n>> the bug with handling 'e') as a separate patch. I'm unsure as it feels odd\n>> to split up the introduction of the flags - I'd be interested to hear what\n>> others think.\n> \n> Essentially, the original was doing:\n> \n>     - In early part of patch_update_file(), decide what option to show\n>       in s->buf using \"if (undecided_previous >= 0)\" etc. boolean\n>       expression that is tailored for each command;\n> \n>     - In later part of patch_update_file(), after getting an answer\n>       to the end user, try to use the same boolean expression that\n>       is tailored for each command to see if the given command is\n>       acceptable.\n> \n> and the bug was that each pair of boolean expressions that are\n> supposed to be identical were duplicated in two places, and one pair\n> was not identical by mistake.\n> \n> Your [2/2] fixes it by turning the above to\n> \n>     - In early part of patch_update_file(), decide what option to show\n>       in s->buf using \"if (undecided_previous >= 0)\" etc. boolean\n>       expression that is tailored for each command, *AND* record the\n>       fact that the command is allowed in the permitted bitmask.\n> \n>     - In later part of patch_update_file(), after getting an answer\n>       to the end user, consult the permitted bitmask computed\n>       earlier to see if the given command is acceptable.\n> \n> Since there no longer is duplicated boolean expressions that are\n> supposed to be the same but different by a bug, once this conversion\n> is made, it is impossible to have the bug.  For that reason, I do\n> not think the suggested split makes much sense.\n> \n> A much saner split, if we have to split this step into two, would be\n> to first fix the bug keeping the code structure of the original,\n> i.e. the later part guards the 'e' command with\n> \n> \tif (hunk_index + 1 == file_diff->mode_change)\n> \n> but the earlier part also required !file_diff->deleted, i.e. the\n> condition should have been\n> \n> \tif (hunk_index + 1 > file_diff->mode_change && !file_diff->deleted)\n> \n> So without introducing enum and permitted bitmask, you can fix the\n> bug in place, replacing the incorrect boolean condition in the later\n> part that guards the 'e' command with a corrected one.  That would\n> be a minimum fix that can become your new [2/2], whose theme is \"to\n> fix the bug with minumum change\".\n> \n> On top of that, you can convert the function again to reach the\n> final shape that writes each boolean condition only once and records\n> the permitted commands in the bitmask.  That can be your new [3/2],\n> whose these is \"to make it impossible to introduce the bug previous\n> step fixed\".\n\nThanks that makes much more sense to me\n\nBest Wishes\n\nPhillip\n\n>> Phillip Wood (2):\n>>   add -p: use ALLOC_GROW_BY instead of ALLOW_GROW\n>>   add -p: fix checking of user input\n>>\n>>  add-patch.c | 67 +++++++++++++++++++++++++++++++++--------------------\n>>  1 file changed, 42 insertions(+), 25 deletions(-)\n>>\n>>\n>> base-commit: 47ae905ffb98cc4d4fd90083da6bc8dab55d9ecc\n>> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-702%2Fphillipwood%2Fwip%2Fadd-p-fixes-v1\n>> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-702/phillipwood/wip/add-p-fixes-v1\n>> Pull-Request: https://github.com/gitgitgadget/git/pull/702\n\n"}]}