{"thread":{"id":"52473","subject":"[PATCH 0/7] stash/reset/checkout -p: optionally use the add --patch backend written in pure C","startedAt":"2019-12-17T10:41:10Z","lastAt":"2019-12-21T21:57:31Z","messageCount":23,"participants":["Johannes Schindelin via GitGitGadget","Junio C Hamano","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"388341","messageId":"pull.174.git.1576579264.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":null,"subject":"[PATCH 0/7] stash/reset/checkout -p: optionally use the add --patch backend written in pure C","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-17T10:40:57Z","receivedAt":"2019-12-17T10:41:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"At this stage on the journey to a fully built-in git add, we already have\neverything we need, including the --interactive and --patch options, as long\nas the add.interactive.useBuiltin setting is set to true. This config\nsetting is kind of a feature flag that is currently turned off by default,\nand will be for a while, until we get confident enough that the built-in\nversion does the job, and retire the Perl script.\n\nHowever, the internal add--interactive helper is also used to back the \n--patch option of git stash, git reset, git checkout and git worktree.\n\nThis patch series (based on js/add-p-in-c) brings them \"online\".\n\nJohannes Schindelin (7):\n  built-in add -p: prepare for patch modes other than \"stage\"\n  built-in add -p: implement the \"stash\" and \"reset\" patch modes\n  legacy stash -p: respect the add.interactive.usebuiltin setting\n  built-in stash: use the built-in `git add -p` if so configured\n  built-in add -p: implement the \"checkout\" patch modes\n  built-in add -p: implement the \"worktree\" patch modes\n  commit --interactive: make it work with the built-in `add -i`\n\n add-interactive.c   |   2 +-\n add-interactive.h   |  12 +-\n add-patch.c         | 353 ++++++++++++++++++++++++++++++++++++++++----\n builtin/add.c       |  35 ++++-\n builtin/commit.c    |   8 +-\n builtin/stash.c     |  25 ++--\n git-legacy-stash.sh |   2 +-\n 7 files changed, 388 insertions(+), 49 deletions(-)\n\n\nbase-commit: 2e4083198d1508206488af4c82093ceb6cf20f4e\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-174%2Fdscho%2Fother-command-p-in-c-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-174/dscho/other-command-p-in-c-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/174\n-- \ngitgitgadget\n"},{"id":"388342","messageId":"cddc2cb9de645ecc49a74e94a73bcdf843035024.1576579264.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.git.1576579264.gitgitgadget@gmail.com","subject":"[PATCH 3/7] legacy stash -p: respect the add.interactive.usebuiltin setting","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-17T10:41:00Z","receivedAt":"2019-12-17T10:41:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAs `git add` traditionally did not expose the `--patch=<mode>` modes via\ncommand-line options, the scripted version of `git stash` had to call\n`git add--interactive` directly.\n\nBut this prevents the built-in `add -p` from kicking in, as\n`add--interactive` is the scripted version (which does not have a\n\"fall-back\" to the built-in version).\n\nSo let's introduce support for internal switch for `git add` that the\nscripted `git stash` can use to call the appropriate backend (scripted\nor built-in, depending on `add.interactive.useBuiltin`).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/add.c       | 14 ++++++++++++++\n git-legacy-stash.sh |  2 +-\n 2 files changed, 15 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 583d8aab0d..f238e8b623 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -29,6 +29,7 @@ static const char * const builtin_add_usage[] = {\n static int patch_interactive, add_interactive, edit_interactive;\n static int take_worktree_changes;\n static int add_renormalize;\n+static int legacy_stash_p; /* support for the scripted `git stash` */\n \n struct update_callback_data {\n \tint flags;\n@@ -335,6 +336,8 @@ static struct option builtin_add_options[] = {\n \t\t   N_(\"override the executable bit of the listed files\")),\n \tOPT_HIDDEN_BOOL(0, \"warn-embedded-repo\", &warn_on_embedded_repo,\n \t\t\tN_(\"warn when adding an embedded repository\")),\n+\tOPT_HIDDEN_BOOL(0, \"legacy-stash-p\", &legacy_stash_p,\n+\t\t\tN_(\"backend for `git stash -p`\")),\n \tOPT_END(),\n };\n \n@@ -431,6 +434,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\tadd_interactive = 1;\n \tif (add_interactive)\n \t\texit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));\n+\tif (legacy_stash_p) {\n+\t\tstruct pathspec pathspec;\n+\n+\t\tparse_pathspec(&pathspec, 0,\n+\t\t\tPATHSPEC_PREFER_FULL |\n+\t\t\tPATHSPEC_SYMLINK_LEADING_PATH |\n+\t\t\tPATHSPEC_PREFIX_ORIGIN,\n+\t\t\tprefix, argv);\n+\n+\t\treturn run_add_interactive(NULL, \"--patch=stash\", &pathspec);\n+\t}\n \n \tif (edit_interactive)\n \t\treturn(edit_patch(argc, argv, prefix));\ndiff --git a/git-legacy-stash.sh b/git-legacy-stash.sh\nindex 07ad4a5459..ed039dfcbb 100755\n--- a/git-legacy-stash.sh\n+++ b/git-legacy-stash.sh\n@@ -206,7 +206,7 @@ create_stash () {\n \n \t\t# find out what the user wants\n \t\tGIT_INDEX_FILE=\"$TMP-index\" \\\n-\t\t\tgit add--interactive --patch=stash -- \"$@\" &&\n+\t\t\tgit add --legacy-stash-p -- \"$@\" &&\n \n \t\t# state of the working tree\n \t\tw_tree=$(GIT_INDEX_FILE=\"$TMP-index\" git write-tree) ||\n-- \ngitgitgadget\n\n"},{"id":"388343","messageId":"8f6139f94debb9b3bfb9c9e8b8e18e3c2fb46a06.1576579264.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.git.1576579264.gitgitgadget@gmail.com","subject":"[PATCH 1/7] built-in add -p: prepare for patch modes other than \"stage\"","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-17T10:40:58Z","receivedAt":"2019-12-17T10:41:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe Perl script backing `git add -p` is used not only for that command,\nbut also for `git stash -p`, `git reset -p` and `git checkout -p`.\n\nIn preparation for teaching the C version of `git add -p` to support\nalso the latter commands, let's abstract away what is \"stage\" specific\ninto a dedicated data structure describing the differences between the\npatch modes.\n\nFinally, please note that the Perl version tries to make sure that the\ndiffs are only generated for the modified files. This is not actually\nnecessary, as the calls to Git's diff machinery already perform that\nwork, and perform it well. This makes it unnecessary to port the\n`FILTER` field of the `%patch_modes` struct, as well as the\n`get_diff_reference()` function.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n add-interactive.c |  2 +-\n add-interactive.h |  8 ++++-\n add-patch.c       | 88 +++++++++++++++++++++++++++++++++--------------\n builtin/add.c     | 10 ++++--\n 4 files changed, 79 insertions(+), 29 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 6a5048c83e..0e753d2acc 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -924,7 +924,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,\n \t\tparse_pathspec(&ps_selected,\n \t\t\t       PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,\n \t\t\t       PATHSPEC_LITERAL_PATH, \"\", args.argv);\n-\t\tres = run_add_p(s->r, &ps_selected);\n+\t\tres = run_add_p(s->r, ADD_P_STAGE, NULL, &ps_selected);\n \t\targv_array_clear(&args);\n \t\tclear_pathspec(&ps_selected);\n \t}\ndiff --git a/add-interactive.h b/add-interactive.h\nindex 062dc3646c..3defa2ff3d 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -22,6 +22,12 @@ void init_add_i_state(struct add_i_state *s, struct repository *r);\n struct repository;\n struct pathspec;\n int run_add_i(struct repository *r, const struct pathspec *ps);\n-int run_add_p(struct repository *r, const struct pathspec *ps);\n+\n+enum add_p_mode {\n+\tADD_P_STAGE,\n+};\n+\n+int run_add_p(struct repository *r, enum add_p_mode mode,\n+\t      const char *revision, const struct pathspec *ps);\n \n #endif\ndiff --git a/add-patch.c b/add-patch.c\nindex 2c46fe5b33..8a691f07da 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -11,10 +11,33 @@ enum prompt_mode_type {\n \tPROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK\n };\n \n-static const char *prompt_mode[] = {\n-\tN_(\"Stage mode change [y,n,a,q,d%s,?]? \"),\n-\tN_(\"Stage deletion [y,n,a,q,d%s,?]? \"),\n-\tN_(\"Stage this hunk [y,n,a,q,d%s,?]? \")\n+struct patch_mode {\n+\tconst char *diff[4], *apply[4], *apply_check[4];\n+\tunsigned is_reverse:1, apply_for_checkout:1;\n+\tconst char *prompt_mode[PROMPT_HUNK + 1];\n+\tconst char *edit_hunk_hint, *help_patch_text;\n+};\n+\n+static struct patch_mode patch_mode_stage = {\n+\t.diff = { \"diff-files\", NULL },\n+\t.apply = { \"--cached\", NULL },\n+\t.apply_check = { \"--cached\", NULL },\n+\t.is_reverse = 0,\n+\t.prompt_mode = {\n+\t\tN_(\"Stage mode change [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Stage deletion [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Stage this hunk [y,n,q,a,d%s,?]? \")\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for staging.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - stage this hunk\\n\"\n+\t\t   \"n - do not stage this hunk\\n\"\n+\t\t   \"q - quit; do not stage this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - stage this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not stage this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\")\n };\n \n struct hunk_header {\n@@ -47,6 +70,10 @@ struct add_p_state {\n \t\tunsigned deleted:1, mode_change:1,binary:1;\n \t} *file_diff;\n \tsize_t file_diff_nr;\n+\n+\t/* patch mode */\n+\tstruct patch_mode *mode;\n+\tconst char *revision;\n };\n \n static void err(struct add_p_state *s, const char *fmt, ...)\n@@ -162,9 +189,18 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n \tstruct hunk *hunk = NULL;\n \tint res;\n \n+\targv_array_pushv(&args, s->mode->diff);\n+\tif (s->revision) {\n+\t\tstruct object_id oid;\n+\t\targv_array_push(&args,\n+\t\t\t\t/* could be on an unborn branch */\n+\t\t\t\t!strcmp(\"HEAD\", s->revision) &&\n+\t\t\t\tget_oid(\"HEAD\", &oid) ?\n+\t\t\t\tempty_tree_oid_hex() : s->revision);\n+\t}\n+\tcolor_arg_index = args.argc;\n \t/* Use `--no-color` explicitly, just in case `diff.color = always`. */\n-\targv_array_pushl(&args, \"diff-files\", \"-p\", \"--no-color\", \"--\", NULL);\n-\tcolor_arg_index = args.argc - 2;\n+\targv_array_pushl(&args, \"--no-color\", \"-p\", \"--\", NULL);\n \tfor (i = 0; i < ps->nr; i++)\n \t\targv_array_push(&args, ps->items[i].original);\n \n@@ -382,7 +418,10 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,\n \t\t\t\t- header->colored_extra_start;\n \t\t}\n \n-\t\tnew_offset += delta;\n+\t\tif (s->mode->is_reverse)\n+\t\t\told_offset -= delta;\n+\t\telse\n+\t\t\tnew_offset += delta;\n \n \t\tstrbuf_addf(out, \"@@ -%lu,%lu +%lu,%lu @@\",\n \t\t\t    old_offset, header->old_count,\n@@ -805,11 +844,10 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)\n \t\t\t\t\"(context).\\n\"\n \t\t\t\t\"To remove '%c' lines, delete them.\\n\"\n \t\t\t\t\"Lines starting with %c will be removed.\\n\"),\n-\t\t\t      '-', '+', comment_line_char);\n-\tstrbuf_commented_addf(&s->buf,\n-\t\t\t      _(\"If the patch applies cleanly, the edited hunk \"\n-\t\t\t\t\"will immediately be\\n\"\n-\t\t\t\t\"marked for staging.\\n\"));\n+\t\t\t      s->mode->is_reverse ? '+' : '-',\n+\t\t\t      s->mode->is_reverse ? '-' : '+',\n+\t\t\t      comment_line_char);\n+\tstrbuf_commented_addf(&s->buf, \"%s\", _(s->mode->edit_hunk_hint));\n \t/*\n \t * TRANSLATORS: 'it' refers to the patch mentioned in the previous\n \t * messages.\n@@ -890,7 +928,8 @@ static int run_apply_check(struct add_p_state *s,\n \treassemble_patch(s, file_diff, 1, &s->buf);\n \n \tsetup_child_process(s, &cp,\n-\t\t\t    \"apply\", \"--cached\", \"--check\", NULL);\n+\t\t\t    \"apply\", \"--check\", NULL);\n+\targv_array_pushv(&cp.args, s->mode->apply_check);\n \tif (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))\n \t\treturn error(_(\"'git apply --cached' failed\"));\n \n@@ -1005,13 +1044,6 @@ static size_t display_hunks(struct add_p_state *s,\n \treturn end_index;\n }\n \n-static const char help_patch_text[] =\n-N_(\"y - stage this hunk\\n\"\n-   \"n - do not stage this hunk\\n\"\n-   \"q - quit; do not stage this hunk or any of the remaining ones\\n\"\n-   \"a - stage this and all the remaining hunks\\n\"\n-   \"d - do not stage this hunk nor any of the remaining hunks\\n\");\n-\n static const char help_patch_remainder[] =\n N_(\"j - leave this hunk undecided, see next undecided hunk\\n\"\n    \"J - leave this hunk undecided, see next hunk\\n\"\n@@ -1097,7 +1129,8 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t      (uintmax_t)hunk_index + 1,\n \t\t\t      (uintmax_t)file_diff->hunk_nr);\n \t\tcolor_fprintf(stdout, s->s.prompt_color,\n-\t\t\t      _(prompt_mode[prompt_mode_type]), s->buf.buf);\n+\t\t\t      _(s->mode->prompt_mode[prompt_mode_type]),\n+\t\t\t      s->buf.buf);\n \t\tfflush(stdout);\n \t\tif (strbuf_getline(&s->answer, stdin) == EOF)\n \t\t\tbreak;\n@@ -1254,7 +1287,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\tconst char *p = _(help_patch_remainder), *eol = p;\n \n \t\t\tcolor_fprintf(stdout, s->s.help_color, \"%s\",\n-\t\t\t\t      _(help_patch_text));\n+\t\t\t\t      _(s->mode->help_patch_text));\n \n \t\t\t/*\n \t\t\t * Show only those lines of the remainder that are\n@@ -1288,10 +1321,11 @@ static int patch_update_file(struct add_p_state *s,\n \t\treassemble_patch(s, file_diff, 0, &s->buf);\n \n \t\tdiscard_index(s->s.r->index);\n-\t\tsetup_child_process(s, &cp, \"apply\", \"--cached\", NULL);\n+\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n+\t\targv_array_pushv(&cp.args, s->mode->apply);\n \t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n \t\t\t\t NULL, 0, NULL, 0))\n-\t\t\terror(_(\"'git apply --cached' failed\"));\n+\t\t\terror(_(\"'git apply' failed\"));\n \t\tif (!repo_read_index(s->s.r))\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@@ -1301,7 +1335,8 @@ static int patch_update_file(struct add_p_state *s,\n \treturn quit;\n }\n \n-int run_add_p(struct repository *r, const struct pathspec *ps)\n+int run_add_p(struct repository *r, enum add_p_mode mode,\n+\t      const char *revision, const struct pathspec *ps)\n {\n \tstruct add_p_state s = {\n \t\t{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT\n@@ -1310,6 +1345,9 @@ int run_add_p(struct repository *r, const struct pathspec *ps)\n \n \tinit_add_i_state(&s.s, r);\n \n+\ts.mode = &patch_mode_stage;\n+\ts.revision = revision;\n+\n \tif (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||\n \t    repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,\n \t\t\t\t\t NULL, NULL, NULL) < 0 ||\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 1deb59a642..05b727a426 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -194,12 +194,18 @@ int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\t\t    &use_builtin_add_i);\n \n \tif (use_builtin_add_i == 1) {\n+\t\tenum add_p_mode mode;\n+\n \t\tif (!patch_mode)\n \t\t\treturn !!run_add_i(the_repository, pathspec);\n-\t\tif (strcmp(patch_mode, \"--patch\"))\n+\n+\t\tif (!strcmp(patch_mode, \"--patch\"))\n+\t\t\tmode = ADD_P_STAGE;\n+\t\telse\n \t\t\tdie(\"'%s' not yet supported in the built-in add -p\",\n \t\t\t    patch_mode);\n-\t\treturn !!run_add_p(the_repository, pathspec);\n+\n+\t\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n \t}\n \n \targv_array_push(&argv, \"add--interactive\");\n-- \ngitgitgadget\n\n"},{"id":"388345","messageId":"846cf16e77f4af39310902129be40fda664ace72.1576579264.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.git.1576579264.gitgitgadget@gmail.com","subject":"[PATCH 2/7] built-in add -p: implement the \"stash\" and \"reset\" patch modes","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-17T10:40:59Z","receivedAt":"2019-12-17T10:41:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe `git stash` and `git reset` commands support a `--patch` option, and\nboth simply hand off to `git add -p` to perform that work. Let's teach\nthe built-in version of that command to be able to perform that work, too.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n add-interactive.h |  2 ++\n add-patch.c       | 85 ++++++++++++++++++++++++++++++++++++++++++++---\n builtin/add.c     |  4 +++\n 3 files changed, 87 insertions(+), 4 deletions(-)\n\ndiff --git a/add-interactive.h b/add-interactive.h\nindex 3defa2ff3d..c278f3e26f 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -25,6 +25,8 @@ int run_add_i(struct repository *r, const struct pathspec *ps);\n \n enum add_p_mode {\n \tADD_P_STAGE,\n+\tADD_P_STASH,\n+\tADD_P_RESET,\n };\n \n int run_add_p(struct repository *r, enum add_p_mode mode,\ndiff --git a/add-patch.c b/add-patch.c\nindex 8a691f07da..694e8e6624 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -13,7 +13,7 @@ enum prompt_mode_type {\n \n struct patch_mode {\n \tconst char *diff[4], *apply[4], *apply_check[4];\n-\tunsigned is_reverse:1, apply_for_checkout:1;\n+\tunsigned is_reverse:1, index_only:1, apply_for_checkout:1;\n \tconst char *prompt_mode[PROMPT_HUNK + 1];\n \tconst char *edit_hunk_hint, *help_patch_text;\n };\n@@ -40,6 +40,74 @@ static struct patch_mode patch_mode_stage = {\n \t\t\t\"the file\\n\")\n };\n \n+static struct patch_mode patch_mode_stash = {\n+\t.diff = { \"diff-index\", \"HEAD\", NULL },\n+\t.apply = { \"--cached\", NULL },\n+\t.apply_check = { \"--cached\", NULL },\n+\t.is_reverse = 0,\n+\t.prompt_mode = {\n+\t\tN_(\"Stash mode change [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Stash deletion [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Stash this hunk [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for stashing.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - stash this hunk\\n\"\n+\t\t   \"n - do not stash this hunk\\n\"\n+\t\t   \"q - quit; do not stash this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - stash this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not stash this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n+static struct patch_mode patch_mode_reset_head = {\n+\t.diff = { \"diff-index\", \"--cached\", NULL },\n+\t.apply = { \"-R\", \"--cached\", NULL },\n+\t.apply_check = { \"-R\", \"--cached\", NULL },\n+\t.is_reverse = 1,\n+\t.index_only = 1,\n+\t.prompt_mode = {\n+\t\tN_(\"Unstage mode change [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Unstage deletion [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Unstage this hunk [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for unstaging.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - unstage this hunk\\n\"\n+\t\t   \"n - do not unstage this hunk\\n\"\n+\t\t   \"q - quit; do not unstage this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - unstage this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not unstage this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n+static struct patch_mode patch_mode_reset_nothead = {\n+\t.diff = { \"diff-index\", \"-R\", \"--cached\", NULL },\n+\t.apply = { \"--cached\", NULL },\n+\t.apply_check = { \"--cached\", NULL },\n+\t.is_reverse = 0,\n+\t.index_only = 1,\n+\t.prompt_mode = {\n+\t\tN_(\"Apply mode change to index [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply deletion to index [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply this hunk to index [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for applying.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - apply this hunk to index\\n\"\n+\t\t   \"n - do not apply this hunk to index\\n\"\n+\t\t   \"q - quit; do not apply this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - apply this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not apply this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n struct hunk_header {\n \tunsigned long old_offset, old_count, new_offset, new_count;\n \t/*\n@@ -1345,12 +1413,21 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \n \tinit_add_i_state(&s.s, r);\n \n-\ts.mode = &patch_mode_stage;\n+\tif (mode == ADD_P_STASH)\n+\t\ts.mode = &patch_mode_stash;\n+\telse if (mode == ADD_P_RESET) {\n+\t\tif (!revision || !strcmp(revision, \"HEAD\"))\n+\t\t\ts.mode = &patch_mode_reset_head;\n+\t\telse\n+\t\t\ts.mode = &patch_mode_reset_nothead;\n+\t} else\n+\t\ts.mode = &patch_mode_stage;\n \ts.revision = revision;\n \n \tif (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||\n-\t    repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,\n-\t\t\t\t\t NULL, NULL, NULL) < 0 ||\n+\t    (!s.mode->index_only &&\n+\t     repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,\n+\t\t\t\t\t  NULL, NULL, NULL) < 0) ||\n \t    parse_diff(&s, ps) < 0) {\n \t\tstrbuf_release(&s.plain);\n \t\tstrbuf_release(&s.colored);\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 05b727a426..583d8aab0d 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -201,6 +201,10 @@ int run_add_interactive(const char *revision, const char *patch_mode,\n \n \t\tif (!strcmp(patch_mode, \"--patch\"))\n \t\t\tmode = ADD_P_STAGE;\n+\t\telse if (!strcmp(patch_mode, \"--patch=stash\"))\n+\t\t\tmode = ADD_P_STASH;\n+\t\telse if (!strcmp(patch_mode, \"--patch=reset\"))\n+\t\t\tmode = ADD_P_RESET;\n \t\telse\n \t\t\tdie(\"'%s' not yet supported in the built-in add -p\",\n \t\t\t    patch_mode);\n-- \ngitgitgadget\n\n"},{"id":"388344","messageId":"40c00302f67ed927317fb5955e13690328e2f4e8.1576579264.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.git.1576579264.gitgitgadget@gmail.com","subject":"[PATCH 4/7] built-in stash: use the built-in `git add -p` if so configured","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-17T10:41:01Z","receivedAt":"2019-12-17T10:41:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe scripted version of `git stash` called directly into the Perl script\n`git-add--interactive.perl`, and this was faithfully converted to C.\n\nHowever, we have a much better way to do this now: call the internal API\ndirectly, which will now incidentally also respect the\n`add.interactive.useBuiltin` setting. Let's just do this.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/stash.c | 25 ++++++++++++++-----------\n 1 file changed, 14 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 4e806176b0..2dafd97766 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -999,9 +999,9 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,\n {\n \tint ret = 0;\n \tstruct child_process cp_read_tree = CHILD_PROCESS_INIT;\n-\tstruct child_process cp_add_i = CHILD_PROCESS_INIT;\n \tstruct child_process cp_diff_tree = CHILD_PROCESS_INIT;\n \tstruct index_state istate = { NULL };\n+\tchar *old_index_env = NULL, *old_repo_index_file;\n \n \tremove_path(stash_index_path.buf);\n \n@@ -1015,16 +1015,19 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,\n \t}\n \n \t/* Find out what the user wants. */\n-\tcp_add_i.git_cmd = 1;\n-\targv_array_pushl(&cp_add_i.args, \"add--interactive\", \"--patch=stash\",\n-\t\t\t \"--\", NULL);\n-\tadd_pathspecs(&cp_add_i.args, ps);\n-\targv_array_pushf(&cp_add_i.env_array, \"GIT_INDEX_FILE=%s\",\n-\t\t\t stash_index_path.buf);\n-\tif (run_command(&cp_add_i)) {\n-\t\tret = -1;\n-\t\tgoto done;\n-\t}\n+\told_repo_index_file = the_repository->index_file;\n+\tthe_repository->index_file = stash_index_path.buf;\n+\told_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));\n+\tsetenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);\n+\n+\tret = run_add_interactive(NULL, \"--patch=stash\", ps);\n+\n+\tthe_repository->index_file = old_repo_index_file;\n+\tif (old_index_env && *old_index_env)\n+\t\tsetenv(INDEX_ENVIRONMENT, old_index_env, 1);\n+\telse\n+\t\tunsetenv(INDEX_ENVIRONMENT);\n+\tFREE_AND_NULL(old_index_env);\n \n \t/* State of the working tree. */\n \tif (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,\n-- \ngitgitgadget\n\n"},{"id":"388346","messageId":"3c55f106c7e5f246d4bc55256249cd0bf7183dd8.1576579264.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.git.1576579264.gitgitgadget@gmail.com","subject":"[PATCH 5/7] built-in add -p: implement the \"checkout\" patch modes","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-17T10:41:02Z","receivedAt":"2019-12-17T10:41:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis patch teaches the built-in `git add -p` machinery all the tricks it\nneeds to know in order to act as the work horse for `git checkout -p`.\n\nApart from the minor changes (slightly reworded messages, different\n`diff` and `apply --check` invocations), it requires a new function to\nactually apply the changes, as `git checkout -p` is a bit special in\nthat respect: when the desired changes do not apply to the index, but\napply to the work tree, Git does not fail straight away, but asks the\nuser whether to apply the changes to the worktree at least.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n add-interactive.h |   1 +\n add-patch.c       | 139 ++++++++++++++++++++++++++++++++++++++++++++--\n builtin/add.c     |   5 +-\n 3 files changed, 138 insertions(+), 7 deletions(-)\n\ndiff --git a/add-interactive.h b/add-interactive.h\nindex c278f3e26f..f865f1e8ca 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -27,6 +27,7 @@ enum add_p_mode {\n \tADD_P_STAGE,\n \tADD_P_STASH,\n \tADD_P_RESET,\n+\tADD_P_CHECKOUT,\n };\n \n int run_add_p(struct repository *r, enum add_p_mode mode,\ndiff --git a/add-patch.c b/add-patch.c\nindex 694e8e6624..dea99a79a4 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -108,6 +108,72 @@ static struct patch_mode patch_mode_reset_nothead = {\n \t\t\t\"the file\\n\"),\n };\n \n+static struct patch_mode patch_mode_checkout_index = {\n+\t.diff = { \"diff-files\", NULL },\n+\t.apply = { \"-R\", NULL },\n+\t.apply_check = { \"-R\", NULL },\n+\t.is_reverse = 1,\n+\t.prompt_mode = {\n+\t\tN_(\"Discard mode change from worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard deletion from worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard this hunk from worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for discarding.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - discard this hunk from worktree\\n\"\n+\t\t   \"n - do not discard this hunk from worktree\\n\"\n+\t\t   \"q - quit; do not discard this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - discard this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not discard this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n+static struct patch_mode patch_mode_checkout_head = {\n+\t.diff = { \"diff-index\", NULL },\n+\t.apply_for_checkout = 1,\n+\t.apply_check = { \"-R\", NULL },\n+\t.is_reverse = 1,\n+\t.prompt_mode = {\n+\t\tN_(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard deletion from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard this hunk from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for discarding.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - discard this hunk from index and worktree\\n\"\n+\t\t   \"n - do not discard this hunk from index and worktree\\n\"\n+\t\t   \"q - quit; do not discard this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - discard this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not discard this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n+static struct patch_mode patch_mode_checkout_nothead = {\n+\t.diff = { \"diff-index\", \"-R\", NULL },\n+\t.apply_for_checkout = 1,\n+\t.apply_check = { NULL },\n+\t.is_reverse = 0,\n+\t.prompt_mode = {\n+\t\tN_(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply this hunk to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for applying.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - apply this hunk to index and worktree\\n\"\n+\t\t   \"n - do not apply this hunk to index and worktree\\n\"\n+\t\t   \"q - quit; do not apply this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - apply this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not apply this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n struct hunk_header {\n \tunsigned long old_offset, old_count, new_offset, new_count;\n \t/*\n@@ -1064,6 +1130,57 @@ static int edit_hunk_loop(struct add_p_state *s,\n \t}\n }\n \n+static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,\n+\t\t\t      int is_reverse)\n+{\n+\tconst char *reverse = is_reverse ? \"-R\" : NULL;\n+\tstruct child_process check_index = CHILD_PROCESS_INIT;\n+\tstruct child_process check_worktree = CHILD_PROCESS_INIT;\n+\tstruct child_process apply_index = CHILD_PROCESS_INIT;\n+\tstruct child_process apply_worktree = CHILD_PROCESS_INIT;\n+\tint applies_index, applies_worktree;\n+\n+\tsetup_child_process(s, &check_index,\n+\t\t\t    \"apply\", \"--cached\", \"--check\", reverse, NULL);\n+\tapplies_index = !pipe_command(&check_index, diff->buf, diff->len,\n+\t\t\t\t      NULL, 0, NULL, 0);\n+\n+\tsetup_child_process(s, &check_worktree,\n+\t\t\t    \"apply\", \"--check\", reverse, NULL);\n+\tapplies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,\n+\t\t\t\t\t NULL, 0, NULL, 0);\n+\n+\tif (applies_worktree && applies_index) {\n+\t\tsetup_child_process(s, &apply_index,\n+\t\t\t\t    \"apply\", \"--cached\", reverse, NULL);\n+\t\tpipe_command(&apply_index, diff->buf, diff->len,\n+\t\t\t     NULL, 0, NULL, 0);\n+\n+\t\tsetup_child_process(s, &apply_worktree,\n+\t\t\t\t    \"apply\", reverse, NULL);\n+\t\tpipe_command(&apply_worktree, diff->buf, diff->len,\n+\t\t\t     NULL, 0, NULL, 0);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tif (!applies_index) {\n+\t\terr(s, _(\"The selected hunks do not apply to the index!\"));\n+\t\tif (prompt_yesno(s, _(\"Apply them to the worktree \"\n+\t\t\t\t\t  \"anyway? \")) > 0) {\n+\t\t\tsetup_child_process(s, &apply_worktree,\n+\t\t\t\t\t    \"apply\", reverse, NULL);\n+\t\t\treturn pipe_command(&apply_worktree, diff->buf,\n+\t\t\t\t\t    diff->len, NULL, 0, NULL, 0);\n+\t\t}\n+\t\terr(s, _(\"Nothing was applied.\\n\"));\n+\t} else\n+\t\t/* As a last resort, show the diff to the user */\n+\t\tfwrite(diff->buf, diff->len, 1, stderr);\n+\n+\treturn 0;\n+}\n+\n #define SUMMARY_HEADER_WIDTH 20\n #define SUMMARY_LINE_WIDTH 80\n static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,\n@@ -1389,11 +1506,16 @@ static int patch_update_file(struct add_p_state *s,\n \t\treassemble_patch(s, file_diff, 0, &s->buf);\n \n \t\tdiscard_index(s->s.r->index);\n-\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n-\t\targv_array_pushv(&cp.args, s->mode->apply);\n-\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n-\t\t\t\t NULL, 0, NULL, 0))\n-\t\t\terror(_(\"'git apply' failed\"));\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\targv_array_pushv(&cp.args, s->mode->apply);\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))\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@@ -1420,6 +1542,13 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\t\ts.mode = &patch_mode_reset_head;\n \t\telse\n \t\t\ts.mode = &patch_mode_reset_nothead;\n+\t} else if (mode == ADD_P_CHECKOUT) {\n+\t\tif (!revision)\n+\t\t\ts.mode = &patch_mode_checkout_index;\n+\t\telse if (!strcmp(revision, \"HEAD\"))\n+\t\t\ts.mode = &patch_mode_checkout_head;\n+\t\telse\n+\t\t\ts.mode = &patch_mode_checkout_nothead;\n \t} else\n \t\ts.mode = &patch_mode_stage;\n \ts.revision = revision;\ndiff --git a/builtin/add.c b/builtin/add.c\nindex f238e8b623..f4d6eb9e06 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -206,9 +206,10 @@ int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tmode = ADD_P_STASH;\n \t\telse if (!strcmp(patch_mode, \"--patch=reset\"))\n \t\t\tmode = ADD_P_RESET;\n+\t\telse if (!strcmp(patch_mode, \"--patch=checkout\"))\n+\t\t\tmode = ADD_P_CHECKOUT;\n \t\telse\n-\t\t\tdie(\"'%s' not yet supported in the built-in add -p\",\n-\t\t\t    patch_mode);\n+\t\t\tdie(\"'%s' not supported\", patch_mode);\n \n \t\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n \t}\n-- \ngitgitgadget\n\n"},{"id":"388347","messageId":"7a4c330d03716dac637cc566257647318beb93b4.1576579264.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.git.1576579264.gitgitgadget@gmail.com","subject":"[PATCH 7/7] commit --interactive: make it work with the built-in `add -i`","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-17T10:41:04Z","receivedAt":"2019-12-17T10:41:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe built-in `git add -i` machinery obviously has its `the_repository`\nstructure initialized at the point where `cmd_commit()` calls it, and\ntherefore does not look at the environment variable `GIT_INDEX_FILE`.\n\nBut when being called from `commit --interactive`, it has to, because\nthe index was already locked in that case, and we want to ask the\ninteractive add machinery to work on the `index.lock` file instead of\nthe `index` file.\n\nTechnically, we could teach `run_add_i()`, or for that matter\n`run_add_p()`, to look specifically at that environment variable, but\nthe entire idea of passing in a parameter of type `struct repository *`\nis to allow working on multiple repositories (and their index files)\nindependently.\n\nSo let's instead override the `index_file` field of that structure\ntemporarily.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/commit.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex e588bc6ad3..32ffc7beee 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -347,7 +347,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix\n \t\tdie(_(\"index file corrupt\"));\n \n \tif (interactive) {\n-\t\tchar *old_index_env = NULL;\n+\t\tchar *old_index_env = NULL, *old_repo_index_file;\n \t\thold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);\n \n \t\trefresh_cache_or_die(refresh_flags);\n@@ -355,12 +355,16 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix\n \t\tif (write_locked_index(&the_index, &index_lock, 0))\n \t\t\tdie(_(\"unable to create temporary index\"));\n \n+\t\told_repo_index_file = the_repository->index_file;\n+\t\tthe_repository->index_file =\n+\t\t\t(char *)get_lock_file_path(&index_lock);\n \t\told_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));\n-\t\tsetenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1);\n+\t\tsetenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);\n \n \t\tif (interactive_add(argc, argv, prefix, patch_interactive) != 0)\n \t\t\tdie(_(\"interactive add failed\"));\n \n+\t\tthe_repository->index_file = old_repo_index_file;\n \t\tif (old_index_env && *old_index_env)\n \t\t\tsetenv(INDEX_ENVIRONMENT, old_index_env, 1);\n \t\telse\n-- \ngitgitgadget\n"},{"id":"388348","messageId":"b63fca6dabd6f5dbbc8280647146153e82aca77c.1576579264.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.git.1576579264.gitgitgadget@gmail.com","subject":"[PATCH 6/7] built-in add -p: implement the \"worktree\" patch modes","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-17T10:41:03Z","receivedAt":"2019-12-17T10:41:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis is a straight-forward port of 2f0896ec3ad4 (restore: support\n--patch, 2019-04-25) which added support for `git restore -p`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n add-interactive.h |  1 +\n add-patch.c       | 51 +++++++++++++++++++++++++++++++++++++++++++++++\n builtin/add.c     |  2 ++\n 3 files changed, 54 insertions(+)\n\ndiff --git a/add-interactive.h b/add-interactive.h\nindex f865f1e8ca..4895ed1df5 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -28,6 +28,7 @@ enum add_p_mode {\n \tADD_P_STASH,\n \tADD_P_RESET,\n \tADD_P_CHECKOUT,\n+\tADD_P_WORKTREE,\n };\n \n int run_add_p(struct repository *r, enum add_p_mode mode,\ndiff --git a/add-patch.c b/add-patch.c\nindex dea99a79a4..2ad18dc3cb 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -174,6 +174,50 @@ static struct patch_mode patch_mode_checkout_nothead = {\n \t\t\t\"the file\\n\"),\n };\n \n+static struct patch_mode patch_mode_worktree_head = {\n+\t.diff = { \"diff-index\", NULL },\n+\t.apply = { \"-R\", NULL },\n+\t.apply_check = { \"-R\", NULL },\n+\t.is_reverse = 1,\n+\t.prompt_mode = {\n+\t\tN_(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard deletion from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard this hunk from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for discarding.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - discard this hunk from worktree\\n\"\n+\t\t   \"n - do not discard this hunk from worktree\\n\"\n+\t\t   \"q - quit; do not discard this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - discard this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not discard this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n+static struct patch_mode patch_mode_worktree_nothead = {\n+\t.diff = { \"diff-index\", \"-R\", NULL },\n+\t.apply = { NULL },\n+\t.apply_check = { NULL },\n+\t.is_reverse = 0,\n+\t.prompt_mode = {\n+\t\tN_(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply this hunk to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for applying.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - apply this hunk to worktree\\n\"\n+\t\t   \"n - do not apply this hunk to worktree\\n\"\n+\t\t   \"q - quit; do not apply this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - apply this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not apply this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n struct hunk_header {\n \tunsigned long old_offset, old_count, new_offset, new_count;\n \t/*\n@@ -1549,6 +1593,13 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\t\ts.mode = &patch_mode_checkout_head;\n \t\telse\n \t\t\ts.mode = &patch_mode_checkout_nothead;\n+\t} else if (mode == ADD_P_WORKTREE) {\n+\t\tif (!revision)\n+\t\t\ts.mode = &patch_mode_checkout_index;\n+\t\telse if (!strcmp(revision, \"HEAD\"))\n+\t\t\ts.mode = &patch_mode_worktree_head;\n+\t\telse\n+\t\t\ts.mode = &patch_mode_worktree_nothead;\n \t} else\n \t\ts.mode = &patch_mode_stage;\n \ts.revision = revision;\ndiff --git a/builtin/add.c b/builtin/add.c\nindex f4d6eb9e06..6fa2b2dd17 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -208,6 +208,8 @@ int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tmode = ADD_P_RESET;\n \t\telse if (!strcmp(patch_mode, \"--patch=checkout\"))\n \t\t\tmode = ADD_P_CHECKOUT;\n+\t\telse if (!strcmp(patch_mode, \"--patch=worktree\"))\n+\t\t\tmode = ADD_P_WORKTREE;\n \t\telse\n \t\t\tdie(\"'%s' not supported\", patch_mode);\n \n-- \ngitgitgadget\n\n"},{"id":"388397","messageId":"xmqq36dihhfj.fsf@gitster-ct.c.googlers.com","threadId":"52473","inReplyTo":"8f6139f94debb9b3bfb9c9e8b8e18e3c2fb46a06.1576579264.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/7] built-in add -p: prepare for patch modes other than \"stage\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-12-17T19:37:36Z","receivedAt":"2019-12-17T19:37:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> +\n> +enum add_p_mode {\n> +\tADD_P_STAGE,\n> +};\n\nNice to see a trailing comma here ;-)\n\n> +int run_add_p(struct repository *r, enum add_p_mode mode,\n> +\t      const char *revision, const struct pathspec *ps);\n\nThis makes readers wonder if \"const struct object_id *\" is more\nappropriate; \"const char *revision\" that holds human-readable name\nis better when the internal machinery uses it for reporting, so that\nmay be what is going on here (so is the new field in add_p_state\nstructure).\n\n>  #endif\n> diff --git a/add-patch.c b/add-patch.c\n> index 2c46fe5b33..8a691f07da 100644\n> --- a/add-patch.c\n> +++ b/add-patch.c\n> @@ -11,10 +11,33 @@ enum prompt_mode_type {\n>  \tPROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK\n>  };\n>  \n> -static const char *prompt_mode[] = {\n> -\tN_(\"Stage mode change [y,n,a,q,d%s,?]? \"),\n> -\tN_(\"Stage deletion [y,n,a,q,d%s,?]? \"),\n> -\tN_(\"Stage this hunk [y,n,a,q,d%s,?]? \")\n> +struct patch_mode {\n> +\tconst char *diff[4], *apply[4], *apply_check[4];\n\nHardcoded \"4\" and not-quite descriptive names puzzle readers at the\nfirst glance.  Let's read on to see if they need any further\nimprovement.\n\n> +\tunsigned is_reverse:1, apply_for_checkout:1;\n> +\tconst char *prompt_mode[PROMPT_HUNK + 1];\n\nThis relies on the enum value assignment (or listing) order to\nensure that PROMPT_HUNK always comes at the end.  Perhaps that\ndeserves a comment before \"enum prompt_mode_type\", e.g.\n\n\t+/* Keep PROMPT_HUNK at the end */\n\t enum prompt_mode_type {\n\t \tPROMPT_MODE_CHANGE = 0, ...\n\t };\n\n> +\tconst char *edit_hunk_hint, *help_patch_text;\n> +};\n> +\n> +static struct patch_mode patch_mode_stage = {\n> +\t.diff = { \"diff-files\", NULL },\n\nNice to see designated initializers used ;-)\n\nMental note: the \"diff\" field is (probably) for \"the command line\nto be used to generate the patch\"\n\n> +\t.apply = { \"--cached\", NULL },\n> +\t.apply_check = { \"--cached\", NULL },\n\nMental note: these \"apply\" and \"apply_check\" fields are (probably)\nnot for the command line; unlike the \"diff\" field, these only have\narguments.\n\nMental note: if the three field names become confusing, perhaps we\ncan clarify them by either (1) calling diff as diff_cmd[], or (2)\ncalling the other as apply_args[] and apply_check_args[], or (3)\nrename both.\n\n> +\t.is_reverse = 0,\n\nWouldn't it be sufficient to apply the default initialization, just\nlike it is done for apply_for_checkout bitfield?\n\n> @@ -1310,6 +1345,9 @@ int run_add_p(struct repository *r, const struct pathspec *ps)\n>  \n>  \tinit_add_i_state(&s.s, r);\n>  \n> +\ts.mode = &patch_mode_stage;\n> +\ts.revision = revision;\n\nThe phrase \"mode_stage\" may become problematic, as other modes that\nwill be introduced, like \"reset\", \"checkout\" all will stage\ndifferent contents to the index.  The only mode the machinery knows\nat this point in the series is how \"add\" stages contents to the\nindex, so \"patch_mode_add\" might turn out to be a better choice of\nthe phrase as we read the series along.  We'll see.\n\n> +\t\tif (!strcmp(patch_mode, \"--patch\"))\n> +\t\t\tmode = ADD_P_STAGE;\n\nThe same comment applies to this enum token.\n\nThanks.\n"},{"id":"388401","messageId":"xmqqy2vag19l.fsf@gitster-ct.c.googlers.com","threadId":"52473","inReplyTo":"846cf16e77f4af39310902129be40fda664ace72.1576579264.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/7] built-in add -p: implement the \"stash\" and \"reset\" patch modes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-12-17T20:12:06Z","receivedAt":"2019-12-17T20:12:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> From: Johannes Schindelin <johannes.schindelin@gmx.de>\n>\n> The `git stash` and `git reset` commands support a `--patch` option, and\n> both simply hand off to `git add -p` to perform that work. Let's teach\n> the built-in version of that command to be able to perform that work, too.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  add-interactive.h |  2 ++\n>  add-patch.c       | 85 ++++++++++++++++++++++++++++++++++++++++++++---\n>  builtin/add.c     |  4 +++\n>  3 files changed, 87 insertions(+), 4 deletions(-)\n>\n> diff --git a/add-interactive.h b/add-interactive.h\n> index 3defa2ff3d..c278f3e26f 100644\n> --- a/add-interactive.h\n> +++ b/add-interactive.h\n> @@ -25,6 +25,8 @@ int run_add_i(struct repository *r, const struct pathspec *ps);\n>  \n>  enum add_p_mode {\n>  \tADD_P_STAGE,\n> +\tADD_P_STASH,\n> +\tADD_P_RESET,\n\nAs I mentioned in my review on the previous step, ADD_P_ADD would be\nmore descriptive of what is going on when listed together with STASH\nand RESET here.\n\n> +static struct patch_mode patch_mode_reset_head = {\n> +\t.diff = { \"diff-index\", \"--cached\", NULL },\n> +\t.apply = { \"-R\", \"--cached\", NULL },\n> +\t.apply_check = { \"-R\", \"--cached\", NULL },\n> +\t.is_reverse = 1,\n> +\t.index_only = 1,\n> +\t.prompt_mode = {\n> +\t\tN_(\"Unstage mode change [y,n,q,a,d%s,?]? \"),\n> +\t\tN_(\"Unstage deletion [y,n,q,a,d%s,?]? \"),\n> +\t\tN_(\"Unstage this hunk [y,n,q,a,d%s,?]? \"),\n> +\t},\n> +\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n> +\t\t\t     \"will immediately be marked for unstaging.\"),\n> +\t.help_patch_text =\n> +\t\tN_(\"y - unstage this hunk\\n\"\n> +\t\t   \"n - do not unstage this hunk\\n\"\n> +\t\t   \"q - quit; do not unstage this hunk or any of the remaining \"\n> +\t\t\t\"ones\\n\"\n> +\t\t   \"a - unstage this hunk and all later hunks in the file\\n\"\n> +\t\t   \"d - do not unstage this hunk or any of the later hunks in \"\n> +\t\t\t\"the file\\n\"),\n> +};\n> +\n> +static struct patch_mode patch_mode_reset_nothead = {\n> +\t.diff = { \"diff-index\", \"-R\", \"--cached\", NULL },\n> +\t.apply = { \"--cached\", NULL },\n> +\t.apply_check = { \"--cached\", NULL },\n> +\t.is_reverse = 0,\n> +\t.index_only = 1,\n> +\t.prompt_mode = {\n> +\t\tN_(\"Apply mode change to index [y,n,q,a,d%s,?]? \"),\n> +\t\tN_(\"Apply deletion to index [y,n,q,a,d%s,?]? \"),\n> +\t\tN_(\"Apply this hunk to index [y,n,q,a,d%s,?]? \"),\n> +\t},\n> +\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n> +\t\t\t     \"will immediately be marked for applying.\"),\n> +\t.help_patch_text =\n> +\t\tN_(\"y - apply this hunk to index\\n\"\n> +\t\t   \"n - do not apply this hunk to index\\n\"\n> +\t\t   \"q - quit; do not apply this hunk or any of the remaining \"\n> +\t\t\t\"ones\\n\"\n> +\t\t   \"a - apply this hunk and all later hunks in the file\\n\"\n> +\t\t   \"d - do not apply this hunk or any of the later hunks in \"\n> +\t\t\t\"the file\\n\"),\n> +};\n\nInteresting that \"reset to HEAD\" and \"reset to non-HEAD\" would have\nto swap the direction to make it feel more natural to the users.\nThis is nothing new---just re-discovering that it is/was interesting.\n"},{"id":"388403","messageId":"xmqqr212g0xp.fsf@gitster-ct.c.googlers.com","threadId":"52473","inReplyTo":"40c00302f67ed927317fb5955e13690328e2f4e8.1576579264.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 4/7] built-in stash: use the built-in `git add -p` if so configured","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-12-17T20:19:14Z","receivedAt":"2019-12-17T20:19:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> diff --git a/builtin/stash.c b/builtin/stash.c\n> index 4e806176b0..2dafd97766 100644\n> --- a/builtin/stash.c\n> +++ b/builtin/stash.c\n> @@ -999,9 +999,9 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,\n>  {\n>  \tint ret = 0;\n>  \tstruct child_process cp_read_tree = CHILD_PROCESS_INIT;\n> -\tstruct child_process cp_add_i = CHILD_PROCESS_INIT;\n>  \tstruct child_process cp_diff_tree = CHILD_PROCESS_INIT;\n>  \tstruct index_state istate = { NULL };\n> +\tchar *old_index_env = NULL, *old_repo_index_file;\n>  \n>  \tremove_path(stash_index_path.buf);\n>  \n\nTogether with the previous step, it makes sense.  Earlier we always\nspawned the scripted version.  Now we first give control to the C\ncode and allow it to punt to the scripted version unless it is told\nto take control with USE_BUILTIN.\n\n> @@ -1015,16 +1015,19 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,\n>  \t}\n>  \n>  \t/* Find out what the user wants. */\n> -\tcp_add_i.git_cmd = 1;\n> -\targv_array_pushl(&cp_add_i.args, \"add--interactive\", \"--patch=stash\",\n> -\t\t\t \"--\", NULL);\n> -\tadd_pathspecs(&cp_add_i.args, ps);\n> -\targv_array_pushf(&cp_add_i.env_array, \"GIT_INDEX_FILE=%s\",\n> -\t\t\t stash_index_path.buf);\n> -\tif (run_command(&cp_add_i)) {\n> -\t\tret = -1;\n> -\t\tgoto done;\n> -\t}\n> +\told_repo_index_file = the_repository->index_file;\n> +\tthe_repository->index_file = stash_index_path.buf;\n> +\told_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));\n> +\tsetenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);\n> +\n> +\tret = run_add_interactive(NULL, \"--patch=stash\", ps);\n> +\n> +\tthe_repository->index_file = old_repo_index_file;\n> +\tif (old_index_env && *old_index_env)\n> +\t\tsetenv(INDEX_ENVIRONMENT, old_index_env, 1);\n> +\telse\n> +\t\tunsetenv(INDEX_ENVIRONMENT);\n> +\tFREE_AND_NULL(old_index_env);\n\nOK.  I suspect that, as we move more stuff that used to be an\nexternal call with one-shot environment assignments like this to an\ninternall call, we'd see patterns of \"save away the state of this\nand that environment variables, then replace them with these values\"\npaired with \"now restore the state of those environment variables\".\n\nWe might eventually want to add a helper for doing so easily, which\nmay make the caller look like\n\n\textern void save_environment(struct saved_env *, ...);\n\n\tstruct saved_env env;\n\tsave_environment(&env, \n\t\t\tINDEX_ENVIRONMENT, the_repository->index_file,\n\t\t\tNULL /* end of \"VAR, val\" tuples */);\n\n\tret = run_add_interactive(NULL, \"--patch=stash\", ps);\n\n        restore_environment(&env);\n\nIt might (or might not) be premature to introduce such a helper at\nthis stage in the series, though.\n\nThanks.\n"},{"id":"388404","messageId":"xmqqmubqg0q2.fsf@gitster-ct.c.googlers.com","threadId":"52473","inReplyTo":"pull.174.git.1576579264.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/7] stash/reset/checkout -p: optionally use the add --patch backend written in pure C","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-12-17T20:23:49Z","receivedAt":"2019-12-17T20:23:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> At this stage on the journey to a fully built-in git add, we already have\n> everything we need, including the --interactive and --patch options, as long\n> as the add.interactive.useBuiltin setting is set to true. This config\n> setting is kind of a feature flag that is currently turned off by default,\n> and will be for a while, until we get confident enough that the built-in\n> version does the job, and retire the Perl script.\n\nExcept for a few things that are all minor I already mentioned, and\nthat I'd need to think a bit more to form an opinion on 6/7 and 7/7,\nthey all looked quite straight-forward.\n\nWill queue.\n"},{"id":"388740","messageId":"nycvar.QRO.7.76.6.1912212118390.46@tvgsbejvaqbjf.bet","threadId":"52473","inReplyTo":"xmqq36dihhfj.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 1/7] built-in add -p: prepare for patch modes other than \"stage\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-12-21T21:19:35Z","receivedAt":"2019-12-21T21:20:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 17 Dec 2019, Junio C Hamano wrote:\n\n> \"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\n> writes:\n>\n> > +int run_add_p(struct repository *r, enum add_p_mode mode,\n> > +\t      const char *revision, const struct pathspec *ps);\n>\n> This makes readers wonder if \"const struct object_id *\" is more\n> appropriate; \"const char *revision\" that holds human-readable name\n> is better when the internal machinery uses it for reporting, so that\n> may be what is going on here (so is the new field in add_p_state\n> structure).\n\nThe main reason why this is a string instead of an object ID is to be able\nto discern between the `HEAD` vs `non-HEAD` versions.\n\nA secondary consideration was that we will want to keep the scripted\nversion of `add -p` as the default for now, and that needs the value as\nthe original string, not as the parsed object ID.\n\n> >  #endif\n> > diff --git a/add-patch.c b/add-patch.c\n> > index 2c46fe5b33..8a691f07da 100644\n> > --- a/add-patch.c\n> > +++ b/add-patch.c\n> > @@ -11,10 +11,33 @@ enum prompt_mode_type {\n> >  \tPROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK\n> >  };\n> >\n> > -static const char *prompt_mode[] = {\n> > -\tN_(\"Stage mode change [y,n,a,q,d%s,?]? \"),\n> > -\tN_(\"Stage deletion [y,n,a,q,d%s,?]? \"),\n> > -\tN_(\"Stage this hunk [y,n,a,q,d%s,?]? \")\n> > +struct patch_mode {\n> > +\tconst char *diff[4], *apply[4], *apply_check[4];\n>\n> Hardcoded \"4\" and not-quite descriptive names puzzle readers at the\n> first glance.  Let's read on to see if they need any further\n> improvement.\n\nGood point. I added a code comment.\n\n> > +\tunsigned is_reverse:1, apply_for_checkout:1;\n> > +\tconst char *prompt_mode[PROMPT_HUNK + 1];\n>\n> This relies on the enum value assignment (or listing) order to\n> ensure that PROMPT_HUNK always comes at the end.  Perhaps that\n> deserves a comment before \"enum prompt_mode_type\", e.g.\n>\n> \t+/* Keep PROMPT_HUNK at the end */\n> \t enum prompt_mode_type {\n> \t \tPROMPT_MODE_CHANGE = 0, ...\n> \t };\n\nI agree, and introduced `PROMPT_MODE_MAX` for that purpose.\n\n>\n> > +\tconst char *edit_hunk_hint, *help_patch_text;\n> > +};\n> > +\n> > +static struct patch_mode patch_mode_stage = {\n> > +\t.diff = { \"diff-files\", NULL },\n>\n> Nice to see designated initializers used ;-)\n>\n> Mental note: the \"diff\" field is (probably) for \"the command line\n> to be used to generate the patch\"\n>\n> > +\t.apply = { \"--cached\", NULL },\n> > +\t.apply_check = { \"--cached\", NULL },\n>\n> Mental note: these \"apply\" and \"apply_check\" fields are (probably)\n> not for the command line; unlike the \"diff\" field, these only have\n> arguments.\n>\n> Mental note: if the three field names become confusing, perhaps we\n> can clarify them by either (1) calling diff as diff_cmd[], or (2)\n> calling the other as apply_args[] and apply_check_args[], or (3)\n> rename both.\n\nI renamed all three.\n\n> > +\t.is_reverse = 0,\n>\n> Wouldn't it be sufficient to apply the default initialization, just\n> like it is done for apply_for_checkout bitfield?\n\nYep, I dropped those unnecessary initializations.\n\n> > @@ -1310,6 +1345,9 @@ int run_add_p(struct repository *r, const struct pathspec *ps)\n> >\n> >  \tinit_add_i_state(&s.s, r);\n> >\n> > +\ts.mode = &patch_mode_stage;\n> > +\ts.revision = revision;\n>\n> The phrase \"mode_stage\" may become problematic, as other modes that\n> will be introduced, like \"reset\", \"checkout\" all will stage\n> different contents to the index.  The only mode the machinery knows\n> at this point in the series is how \"add\" stages contents to the\n> index, so \"patch_mode_add\" might turn out to be a better choice of\n> the phrase as we read the series along.  We'll see.\n>\n> > +\t\tif (!strcmp(patch_mode, \"--patch\"))\n> > +\t\t\tmode = ADD_P_STAGE;\n>\n> The same comment applies to this enum token.\n\nI renamed both to the `*_add` and `*_ADD` version, respectively.\n\nThanks,\nDscho\n"},{"id":"388741","messageId":"nycvar.QRO.7.76.6.1912212221160.46@tvgsbejvaqbjf.bet","threadId":"52473","inReplyTo":"xmqqy2vag19l.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 2/7] built-in add -p: implement the \"stash\" and \"reset\" patch modes","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-12-21T21:23:48Z","receivedAt":"2019-12-21T21:24:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 17 Dec 2019, Junio C Hamano wrote:\n\n> \"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\n> writes:\n>\n> > diff --git a/add-interactive.h b/add-interactive.h\n> > index 3defa2ff3d..c278f3e26f 100644\n> > --- a/add-interactive.h\n> > +++ b/add-interactive.h\n> > @@ -25,6 +25,8 @@ int run_add_i(struct repository *r, const struct pathspec *ps);\n> >\n> >  enum add_p_mode {\n> >  \tADD_P_STAGE,\n> > +\tADD_P_STASH,\n> > +\tADD_P_RESET,\n>\n> As I mentioned in my review on the previous step, ADD_P_ADD would be\n> more descriptive of what is going on when listed together with STASH\n> and RESET here.\n\nI made the suggested change.\n\n> > +static struct patch_mode patch_mode_reset_head = {\n> > +\t.diff = { \"diff-index\", \"--cached\", NULL },\n> > +\t.apply = { \"-R\", \"--cached\", NULL },\n> > +\t.apply_check = { \"-R\", \"--cached\", NULL },\n> > +\t.is_reverse = 1,\n> > +\t.index_only = 1,\n> > +\t.prompt_mode = {\n> > +\t\tN_(\"Unstage mode change [y,n,q,a,d%s,?]? \"),\n> > +\t\tN_(\"Unstage deletion [y,n,q,a,d%s,?]? \"),\n> > +\t\tN_(\"Unstage this hunk [y,n,q,a,d%s,?]? \"),\n> > +\t},\n> > +\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n> > +\t\t\t     \"will immediately be marked for unstaging.\"),\n> > +\t.help_patch_text =\n> > +\t\tN_(\"y - unstage this hunk\\n\"\n> > +\t\t   \"n - do not unstage this hunk\\n\"\n> > +\t\t   \"q - quit; do not unstage this hunk or any of the remaining \"\n> > +\t\t\t\"ones\\n\"\n> > +\t\t   \"a - unstage this hunk and all later hunks in the file\\n\"\n> > +\t\t   \"d - do not unstage this hunk or any of the later hunks in \"\n> > +\t\t\t\"the file\\n\"),\n> > +};\n> > +\n> > +static struct patch_mode patch_mode_reset_nothead = {\n> > +\t.diff = { \"diff-index\", \"-R\", \"--cached\", NULL },\n> > +\t.apply = { \"--cached\", NULL },\n> > +\t.apply_check = { \"--cached\", NULL },\n> > +\t.is_reverse = 0,\n> > +\t.index_only = 1,\n> > +\t.prompt_mode = {\n> > +\t\tN_(\"Apply mode change to index [y,n,q,a,d%s,?]? \"),\n> > +\t\tN_(\"Apply deletion to index [y,n,q,a,d%s,?]? \"),\n> > +\t\tN_(\"Apply this hunk to index [y,n,q,a,d%s,?]? \"),\n> > +\t},\n> > +\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n> > +\t\t\t     \"will immediately be marked for applying.\"),\n> > +\t.help_patch_text =\n> > +\t\tN_(\"y - apply this hunk to index\\n\"\n> > +\t\t   \"n - do not apply this hunk to index\\n\"\n> > +\t\t   \"q - quit; do not apply this hunk or any of the remaining \"\n> > +\t\t\t\"ones\\n\"\n> > +\t\t   \"a - apply this hunk and all later hunks in the file\\n\"\n> > +\t\t   \"d - do not apply this hunk or any of the later hunks in \"\n> > +\t\t\t\"the file\\n\"),\n> > +};\n>\n> Interesting that \"reset to HEAD\" and \"reset to non-HEAD\" would have\n> to swap the direction to make it feel more natural to the users.\n> This is nothing new---just re-discovering that it is/was interesting.\n\nIndeed. It matches the intuition, but looks incongruent.\n\nCiao,\nDscho\n"},{"id":"388742","messageId":"nycvar.QRO.7.76.6.1912212224180.46@tvgsbejvaqbjf.bet","threadId":"52473","inReplyTo":"xmqqr212g0xp.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 4/7] built-in stash: use the built-in `git add -p` if so configured","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-12-21T21:26:51Z","receivedAt":"2019-12-21T21:27:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 17 Dec 2019, Junio C Hamano wrote:\n\n> \"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\n> writes:\n>\n> > diff --git a/builtin/stash.c b/builtin/stash.c\n> > index 4e806176b0..2dafd97766 100644\n> > --- a/builtin/stash.c\n> > +++ b/builtin/stash.c\n> > @@ -999,9 +999,9 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,\n> >  {\n> >  \tint ret = 0;\n> >  \tstruct child_process cp_read_tree = CHILD_PROCESS_INIT;\n> > -\tstruct child_process cp_add_i = CHILD_PROCESS_INIT;\n> >  \tstruct child_process cp_diff_tree = CHILD_PROCESS_INIT;\n> >  \tstruct index_state istate = { NULL };\n> > +\tchar *old_index_env = NULL, *old_repo_index_file;\n> >\n> >  \tremove_path(stash_index_path.buf);\n> >\n>\n> Together with the previous step, it makes sense.  Earlier we always\n> spawned the scripted version.  Now we first give control to the C\n> code and allow it to punt to the scripted version unless it is told\n> to take control with USE_BUILTIN.\n>\n> > @@ -1015,16 +1015,19 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,\n> >  \t}\n> >\n> >  \t/* Find out what the user wants. */\n> > -\tcp_add_i.git_cmd = 1;\n> > -\targv_array_pushl(&cp_add_i.args, \"add--interactive\", \"--patch=stash\",\n> > -\t\t\t \"--\", NULL);\n> > -\tadd_pathspecs(&cp_add_i.args, ps);\n> > -\targv_array_pushf(&cp_add_i.env_array, \"GIT_INDEX_FILE=%s\",\n> > -\t\t\t stash_index_path.buf);\n> > -\tif (run_command(&cp_add_i)) {\n> > -\t\tret = -1;\n> > -\t\tgoto done;\n> > -\t}\n> > +\told_repo_index_file = the_repository->index_file;\n> > +\tthe_repository->index_file = stash_index_path.buf;\n> > +\told_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));\n> > +\tsetenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);\n> > +\n> > +\tret = run_add_interactive(NULL, \"--patch=stash\", ps);\n> > +\n> > +\tthe_repository->index_file = old_repo_index_file;\n> > +\tif (old_index_env && *old_index_env)\n> > +\t\tsetenv(INDEX_ENVIRONMENT, old_index_env, 1);\n> > +\telse\n> > +\t\tunsetenv(INDEX_ENVIRONMENT);\n> > +\tFREE_AND_NULL(old_index_env);\n>\n> OK.  I suspect that, as we move more stuff that used to be an\n> external call with one-shot environment assignments like this to an\n> internall call, we'd see patterns of \"save away the state of this\n> and that environment variables, then replace them with these values\"\n> paired with \"now restore the state of those environment variables\".\n>\n> We might eventually want to add a helper for doing so easily, which\n> may make the caller look like\n>\n> \textern void save_environment(struct saved_env *, ...);\n>\n> \tstruct saved_env env;\n> \tsave_environment(&env,\n> \t\t\tINDEX_ENVIRONMENT, the_repository->index_file,\n> \t\t\tNULL /* end of \"VAR, val\" tuples */);\n>\n> \tret = run_add_interactive(NULL, \"--patch=stash\", ps);\n>\n>         restore_environment(&env);\n>\n> It might (or might not) be premature to introduce such a helper at\n> this stage in the series, though.\n\nTo be honest, I would rather have this at a different layer: I'd like\n`run_add_interactive()` to take an optional path to the index, to be able\nto override the default `<gitdir>/index`.\n\nBut that requires either quite a lot of changes to the Perl script, or it\nneeds to wait until the built-in version of `git add -p` stabilized enough\nto allow retiring the Perl script.\n\nI kinda aimed for the latter solution.\n\nCiao,\nDscho\n"},{"id":"388743","messageId":"pull.174.v2.git.1576965436.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.git.1576579264.gitgitgadget@gmail.com","subject":"[PATCH v2 0/7] stash/reset/checkout -p: optionally use the add --patch backend written in pure C","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-21T21:57:09Z","receivedAt":"2019-12-21T21:57:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"At this stage on the journey to a fully built-in git add, we already have\neverything we need, including the --interactive and --patch options, as long\nas the add.interactive.useBuiltin setting is set to true. This config\nsetting is kind of a feature flag that is currently turned off by default,\nand will be for a while, until we get confident enough that the built-in\nversion does the job, and retire the Perl script.\n\nHowever, the internal add--interactive helper is also used to back the \n--patch option of git stash, git reset, git checkout and git worktree.\n\nThis patch series (based on js/add-p-in-c) brings them \"online\".\n\nChanges since v1:\n\n * Renamed patch_mode_stage to patch_mode_add (and ADD_P_STAGE to ADD_P_ADD\n   ).\n * Renamed the fields diff, apply and apply_check (that were inherited from\n   the Perl script) to diff_cmd, apply_args and apply_check_args,\n   respectively.\n * Clarified the magic array size 4 of diff_cmd and friends.\n * Introduced pseudo value PROMPT_MODE_MAX to make the array size of \n   prompt_mode more obvious.\n * Got rid of the unneeded = 0 initializers for is_reverse.\n\nJohannes Schindelin (7):\n  built-in add -p: prepare for patch modes other than \"stage\"\n  built-in add -p: implement the \"stash\" and \"reset\" patch modes\n  legacy stash -p: respect the add.interactive.usebuiltin setting\n  built-in stash: use the built-in `git add -p` if so configured\n  built-in add -p: implement the \"checkout\" patch modes\n  built-in add -p: implement the \"worktree\" patch modes\n  commit --interactive: make it work with the built-in `add -i`\n\n add-interactive.c   |   2 +-\n add-interactive.h   |  12 +-\n add-patch.c         | 356 ++++++++++++++++++++++++++++++++++++++++----\n builtin/add.c       |  35 ++++-\n builtin/commit.c    |   8 +-\n builtin/stash.c     |  25 ++--\n git-legacy-stash.sh |   2 +-\n 7 files changed, 390 insertions(+), 50 deletions(-)\n\n\nbase-commit: 2e4083198d1508206488af4c82093ceb6cf20f4e\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-174%2Fdscho%2Fother-command-p-in-c-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-174/dscho/other-command-p-in-c-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/174\n\nRange-diff vs v1:\n\n 1:  8f6139f94d ! 1:  d894f8f427 built-in add -p: prepare for patch modes other than \"stage\"\n     @@ -27,7 +27,7 @@\n       \t\t\t       PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,\n       \t\t\t       PATHSPEC_LITERAL_PATH, \"\", args.argv);\n      -\t\tres = run_add_p(s->r, &ps_selected);\n     -+\t\tres = run_add_p(s->r, ADD_P_STAGE, NULL, &ps_selected);\n     ++\t\tres = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected);\n       \t\targv_array_clear(&args);\n       \t\tclear_pathspec(&ps_selected);\n       \t}\n     @@ -42,7 +42,7 @@\n      -int run_add_p(struct repository *r, const struct pathspec *ps);\n      +\n      +enum add_p_mode {\n     -+\tADD_P_STAGE,\n     ++\tADD_P_ADD,\n      +};\n      +\n      +int run_add_p(struct repository *r, enum add_p_mode mode,\n     @@ -54,7 +54,12 @@\n       --- a/add-patch.c\n       +++ b/add-patch.c\n      @@\n     - \tPROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK\n     + #include \"diff.h\"\n     + \n     + enum prompt_mode_type {\n     +-\tPROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK\n     ++\tPROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,\n     ++\tPROMPT_MODE_MAX, /* must be last */\n       };\n       \n      -static const char *prompt_mode[] = {\n     @@ -62,17 +67,21 @@\n      -\tN_(\"Stage deletion [y,n,a,q,d%s,?]? \"),\n      -\tN_(\"Stage this hunk [y,n,a,q,d%s,?]? \")\n      +struct patch_mode {\n     -+\tconst char *diff[4], *apply[4], *apply_check[4];\n     ++\t/*\n     ++\t * The magic constant 4 is chosen such that all patch modes\n     ++\t * provide enough space for three command-line arguments followed by a\n     ++\t * trailing `NULL`.\n     ++\t */\n     ++\tconst char *diff_cmd[4], *apply_args[4], *apply_check_args[4];\n      +\tunsigned is_reverse:1, apply_for_checkout:1;\n     -+\tconst char *prompt_mode[PROMPT_HUNK + 1];\n     ++\tconst char *prompt_mode[PROMPT_MODE_MAX];\n      +\tconst char *edit_hunk_hint, *help_patch_text;\n      +};\n      +\n     -+static struct patch_mode patch_mode_stage = {\n     -+\t.diff = { \"diff-files\", NULL },\n     -+\t.apply = { \"--cached\", NULL },\n     -+\t.apply_check = { \"--cached\", NULL },\n     -+\t.is_reverse = 0,\n     ++static struct patch_mode patch_mode_add = {\n     ++\t.diff_cmd = { \"diff-files\", NULL },\n     ++\t.apply_args = { \"--cached\", NULL },\n     ++\t.apply_check_args = { \"--cached\", NULL },\n      +\t.prompt_mode = {\n      +\t\tN_(\"Stage mode change [y,n,q,a,d%s,?]? \"),\n      +\t\tN_(\"Stage deletion [y,n,q,a,d%s,?]? \"),\n     @@ -106,7 +115,7 @@\n       \tstruct hunk *hunk = NULL;\n       \tint res;\n       \n     -+\targv_array_pushv(&args, s->mode->diff);\n     ++\targv_array_pushv(&args, s->mode->diff_cmd);\n      +\tif (s->revision) {\n      +\t\tstruct object_id oid;\n      +\t\targv_array_push(&args,\n     @@ -157,7 +166,7 @@\n       \tsetup_child_process(s, &cp,\n      -\t\t\t    \"apply\", \"--cached\", \"--check\", NULL);\n      +\t\t\t    \"apply\", \"--check\", NULL);\n     -+\targv_array_pushv(&cp.args, s->mode->apply_check);\n     ++\targv_array_pushv(&cp.args, s->mode->apply_check_args);\n       \tif (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))\n       \t\treturn error(_(\"'git apply --cached' failed\"));\n       \n     @@ -200,7 +209,7 @@\n       \t\tdiscard_index(s->s.r->index);\n      -\t\tsetup_child_process(s, &cp, \"apply\", \"--cached\", NULL);\n      +\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n     -+\t\targv_array_pushv(&cp.args, s->mode->apply);\n     ++\t\targv_array_pushv(&cp.args, s->mode->apply_args);\n       \t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n       \t\t\t\t NULL, 0, NULL, 0))\n      -\t\t\terror(_(\"'git apply --cached' failed\"));\n     @@ -222,7 +231,7 @@\n       \n       \tinit_add_i_state(&s.s, r);\n       \n     -+\ts.mode = &patch_mode_stage;\n     ++\ts.mode = &patch_mode_add;\n      +\ts.revision = revision;\n      +\n       \tif (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||\n     @@ -243,7 +252,7 @@\n      -\t\tif (strcmp(patch_mode, \"--patch\"))\n      +\n      +\t\tif (!strcmp(patch_mode, \"--patch\"))\n     -+\t\t\tmode = ADD_P_STAGE;\n     ++\t\t\tmode = ADD_P_ADD;\n      +\t\telse\n       \t\t\tdie(\"'%s' not yet supported in the built-in add -p\",\n       \t\t\t    patch_mode);\n 2:  846cf16e77 ! 2:  6977bede86 built-in add -p: implement the \"stash\" and \"reset\" patch modes\n     @@ -14,7 +14,7 @@\n      @@\n       \n       enum add_p_mode {\n     - \tADD_P_STAGE,\n     + \tADD_P_ADD,\n      +\tADD_P_STASH,\n      +\tADD_P_RESET,\n       };\n     @@ -25,12 +25,12 @@\n       --- a/add-patch.c\n       +++ b/add-patch.c\n      @@\n     - \n     - struct patch_mode {\n     - \tconst char *diff[4], *apply[4], *apply_check[4];\n     + \t * trailing `NULL`.\n     + \t */\n     + \tconst char *diff_cmd[4], *apply_args[4], *apply_check_args[4];\n      -\tunsigned is_reverse:1, apply_for_checkout:1;\n      +\tunsigned is_reverse:1, index_only:1, apply_for_checkout:1;\n     - \tconst char *prompt_mode[PROMPT_HUNK + 1];\n     + \tconst char *prompt_mode[PROMPT_MODE_MAX];\n       \tconst char *edit_hunk_hint, *help_patch_text;\n       };\n      @@\n     @@ -38,10 +38,9 @@\n       };\n       \n      +static struct patch_mode patch_mode_stash = {\n     -+\t.diff = { \"diff-index\", \"HEAD\", NULL },\n     -+\t.apply = { \"--cached\", NULL },\n     -+\t.apply_check = { \"--cached\", NULL },\n     -+\t.is_reverse = 0,\n     ++\t.diff_cmd = { \"diff-index\", \"HEAD\", NULL },\n     ++\t.apply_args = { \"--cached\", NULL },\n     ++\t.apply_check_args = { \"--cached\", NULL },\n      +\t.prompt_mode = {\n      +\t\tN_(\"Stash mode change [y,n,q,a,d%s,?]? \"),\n      +\t\tN_(\"Stash deletion [y,n,q,a,d%s,?]? \"),\n     @@ -60,9 +59,9 @@\n      +};\n      +\n      +static struct patch_mode patch_mode_reset_head = {\n     -+\t.diff = { \"diff-index\", \"--cached\", NULL },\n     -+\t.apply = { \"-R\", \"--cached\", NULL },\n     -+\t.apply_check = { \"-R\", \"--cached\", NULL },\n     ++\t.diff_cmd = { \"diff-index\", \"--cached\", NULL },\n     ++\t.apply_args = { \"-R\", \"--cached\", NULL },\n     ++\t.apply_check_args = { \"-R\", \"--cached\", NULL },\n      +\t.is_reverse = 1,\n      +\t.index_only = 1,\n      +\t.prompt_mode = {\n     @@ -83,10 +82,9 @@\n      +};\n      +\n      +static struct patch_mode patch_mode_reset_nothead = {\n     -+\t.diff = { \"diff-index\", \"-R\", \"--cached\", NULL },\n     -+\t.apply = { \"--cached\", NULL },\n     -+\t.apply_check = { \"--cached\", NULL },\n     -+\t.is_reverse = 0,\n     ++\t.diff_cmd = { \"diff-index\", \"-R\", \"--cached\", NULL },\n     ++\t.apply_args = { \"--cached\", NULL },\n     ++\t.apply_check_args = { \"--cached\", NULL },\n      +\t.index_only = 1,\n      +\t.prompt_mode = {\n      +\t\tN_(\"Apply mode change to index [y,n,q,a,d%s,?]? \"),\n     @@ -112,7 +110,7 @@\n       \n       \tinit_add_i_state(&s.s, r);\n       \n     --\ts.mode = &patch_mode_stage;\n     +-\ts.mode = &patch_mode_add;\n      +\tif (mode == ADD_P_STASH)\n      +\t\ts.mode = &patch_mode_stash;\n      +\telse if (mode == ADD_P_RESET) {\n     @@ -121,7 +119,7 @@\n      +\t\telse\n      +\t\t\ts.mode = &patch_mode_reset_nothead;\n      +\t} else\n     -+\t\ts.mode = &patch_mode_stage;\n     ++\t\ts.mode = &patch_mode_add;\n       \ts.revision = revision;\n       \n       \tif (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||\n     @@ -140,7 +138,7 @@\n      @@\n       \n       \t\tif (!strcmp(patch_mode, \"--patch\"))\n     - \t\t\tmode = ADD_P_STAGE;\n     + \t\t\tmode = ADD_P_ADD;\n      +\t\telse if (!strcmp(patch_mode, \"--patch=stash\"))\n      +\t\t\tmode = ADD_P_STASH;\n      +\t\telse if (!strcmp(patch_mode, \"--patch=reset\"))\n 3:  cddc2cb9de = 3:  bf9b7f897a legacy stash -p: respect the add.interactive.usebuiltin setting\n 4:  40c00302f6 = 4:  5051f37559 built-in stash: use the built-in `git add -p` if so configured\n 5:  3c55f106c7 ! 5:  b3672b35e6 built-in add -p: implement the \"checkout\" patch modes\n     @@ -18,7 +18,7 @@\n       --- a/add-interactive.h\n       +++ b/add-interactive.h\n      @@\n     - \tADD_P_STAGE,\n     + \tADD_P_ADD,\n       \tADD_P_STASH,\n       \tADD_P_RESET,\n      +\tADD_P_CHECKOUT,\n     @@ -34,9 +34,9 @@\n       };\n       \n      +static struct patch_mode patch_mode_checkout_index = {\n     -+\t.diff = { \"diff-files\", NULL },\n     -+\t.apply = { \"-R\", NULL },\n     -+\t.apply_check = { \"-R\", NULL },\n     ++\t.diff_cmd = { \"diff-files\", NULL },\n     ++\t.apply_args = { \"-R\", NULL },\n     ++\t.apply_check_args = { \"-R\", NULL },\n      +\t.is_reverse = 1,\n      +\t.prompt_mode = {\n      +\t\tN_(\"Discard mode change from worktree [y,n,q,a,d%s,?]? \"),\n     @@ -56,9 +56,9 @@\n      +};\n      +\n      +static struct patch_mode patch_mode_checkout_head = {\n     -+\t.diff = { \"diff-index\", NULL },\n     ++\t.diff_cmd = { \"diff-index\", NULL },\n      +\t.apply_for_checkout = 1,\n     -+\t.apply_check = { \"-R\", NULL },\n     ++\t.apply_check_args = { \"-R\", NULL },\n      +\t.is_reverse = 1,\n      +\t.prompt_mode = {\n      +\t\tN_(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n     @@ -78,10 +78,9 @@\n      +};\n      +\n      +static struct patch_mode patch_mode_checkout_nothead = {\n     -+\t.diff = { \"diff-index\", \"-R\", NULL },\n     ++\t.diff_cmd = { \"diff-index\", \"-R\", NULL },\n      +\t.apply_for_checkout = 1,\n     -+\t.apply_check = { NULL },\n     -+\t.is_reverse = 0,\n     ++\t.apply_check_args = { NULL },\n      +\t.prompt_mode = {\n      +\t\tN_(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n      +\t\tN_(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n     @@ -165,7 +164,7 @@\n       \n       \t\tdiscard_index(s->s.r->index);\n      -\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n     --\t\targv_array_pushv(&cp.args, s->mode->apply);\n     +-\t\targv_array_pushv(&cp.args, s->mode->apply_args);\n      -\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n      -\t\t\t\t NULL, 0, NULL, 0))\n      -\t\t\terror(_(\"'git apply' failed\"));\n     @@ -174,7 +173,7 @@\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\targv_array_pushv(&cp.args, s->mode->apply);\n     ++\t\t\targv_array_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     @@ -194,7 +193,7 @@\n      +\t\telse\n      +\t\t\ts.mode = &patch_mode_checkout_nothead;\n       \t} else\n     - \t\ts.mode = &patch_mode_stage;\n     + \t\ts.mode = &patch_mode_add;\n       \ts.revision = revision;\n      \n       diff --git a/builtin/add.c b/builtin/add.c\n 6:  b63fca6dab ! 6:  83e2ac6e67 built-in add -p: implement the \"worktree\" patch modes\n     @@ -27,9 +27,9 @@\n       };\n       \n      +static struct patch_mode patch_mode_worktree_head = {\n     -+\t.diff = { \"diff-index\", NULL },\n     -+\t.apply = { \"-R\", NULL },\n     -+\t.apply_check = { \"-R\", NULL },\n     ++\t.diff_cmd = { \"diff-index\", NULL },\n     ++\t.apply_args = { \"-R\", NULL },\n     ++\t.apply_check_args = { \"-R\", NULL },\n      +\t.is_reverse = 1,\n      +\t.prompt_mode = {\n      +\t\tN_(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n     @@ -49,10 +49,9 @@\n      +};\n      +\n      +static struct patch_mode patch_mode_worktree_nothead = {\n     -+\t.diff = { \"diff-index\", \"-R\", NULL },\n     -+\t.apply = { NULL },\n     -+\t.apply_check = { NULL },\n     -+\t.is_reverse = 0,\n     ++\t.diff_cmd = { \"diff-index\", \"-R\", NULL },\n     ++\t.apply_args = { NULL },\n     ++\t.apply_check_args = { NULL },\n      +\t.prompt_mode = {\n      +\t\tN_(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n      +\t\tN_(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n     @@ -85,7 +84,7 @@\n      +\t\telse\n      +\t\t\ts.mode = &patch_mode_worktree_nothead;\n       \t} else\n     - \t\ts.mode = &patch_mode_stage;\n     + \t\ts.mode = &patch_mode_add;\n       \ts.revision = revision;\n      \n       diff --git a/builtin/add.c b/builtin/add.c\n 7:  7a4c330d03 = 7:  ecb22159d8 commit --interactive: make it work with the built-in `add -i`\n\n-- \ngitgitgadget\n"},{"id":"388744","messageId":"d894f8f427a14fc9ceb5b64c1c5d8a508e261d59.1576965436.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.v2.git.1576965436.gitgitgadget@gmail.com","subject":"[PATCH v2 1/7] built-in add -p: prepare for patch modes other than \"stage\"","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-21T21:57:10Z","receivedAt":"2019-12-21T21:57:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe Perl script backing `git add -p` is used not only for that command,\nbut also for `git stash -p`, `git reset -p` and `git checkout -p`.\n\nIn preparation for teaching the C version of `git add -p` to support\nalso the latter commands, let's abstract away what is \"stage\" specific\ninto a dedicated data structure describing the differences between the\npatch modes.\n\nFinally, please note that the Perl version tries to make sure that the\ndiffs are only generated for the modified files. This is not actually\nnecessary, as the calls to Git's diff machinery already perform that\nwork, and perform it well. This makes it unnecessary to port the\n`FILTER` field of the `%patch_modes` struct, as well as the\n`get_diff_reference()` function.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n add-interactive.c |  2 +-\n add-interactive.h |  8 +++-\n add-patch.c       | 95 ++++++++++++++++++++++++++++++++++-------------\n builtin/add.c     | 10 ++++-\n 4 files changed, 85 insertions(+), 30 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 6a5048c83e..a5bb14f2f4 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -924,7 +924,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,\n \t\tparse_pathspec(&ps_selected,\n \t\t\t       PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,\n \t\t\t       PATHSPEC_LITERAL_PATH, \"\", args.argv);\n-\t\tres = run_add_p(s->r, &ps_selected);\n+\t\tres = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected);\n \t\targv_array_clear(&args);\n \t\tclear_pathspec(&ps_selected);\n \t}\ndiff --git a/add-interactive.h b/add-interactive.h\nindex 062dc3646c..e29a769aba 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -22,6 +22,12 @@ void init_add_i_state(struct add_i_state *s, struct repository *r);\n struct repository;\n struct pathspec;\n int run_add_i(struct repository *r, const struct pathspec *ps);\n-int run_add_p(struct repository *r, const struct pathspec *ps);\n+\n+enum add_p_mode {\n+\tADD_P_ADD,\n+};\n+\n+int run_add_p(struct repository *r, enum add_p_mode mode,\n+\t      const char *revision, const struct pathspec *ps);\n \n #endif\ndiff --git a/add-patch.c b/add-patch.c\nindex 2c46fe5b33..71356fbd9a 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -8,13 +8,41 @@\n #include \"diff.h\"\n \n enum prompt_mode_type {\n-\tPROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK\n+\tPROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,\n+\tPROMPT_MODE_MAX, /* must be last */\n };\n \n-static const char *prompt_mode[] = {\n-\tN_(\"Stage mode change [y,n,a,q,d%s,?]? \"),\n-\tN_(\"Stage deletion [y,n,a,q,d%s,?]? \"),\n-\tN_(\"Stage this hunk [y,n,a,q,d%s,?]? \")\n+struct patch_mode {\n+\t/*\n+\t * The magic constant 4 is chosen such that all patch modes\n+\t * provide enough space for three command-line arguments followed by a\n+\t * trailing `NULL`.\n+\t */\n+\tconst char *diff_cmd[4], *apply_args[4], *apply_check_args[4];\n+\tunsigned is_reverse:1, apply_for_checkout:1;\n+\tconst char *prompt_mode[PROMPT_MODE_MAX];\n+\tconst char *edit_hunk_hint, *help_patch_text;\n+};\n+\n+static struct patch_mode patch_mode_add = {\n+\t.diff_cmd = { \"diff-files\", NULL },\n+\t.apply_args = { \"--cached\", NULL },\n+\t.apply_check_args = { \"--cached\", NULL },\n+\t.prompt_mode = {\n+\t\tN_(\"Stage mode change [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Stage deletion [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Stage this hunk [y,n,q,a,d%s,?]? \")\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for staging.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - stage this hunk\\n\"\n+\t\t   \"n - do not stage this hunk\\n\"\n+\t\t   \"q - quit; do not stage this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - stage this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not stage this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\")\n };\n \n struct hunk_header {\n@@ -47,6 +75,10 @@ struct add_p_state {\n \t\tunsigned deleted:1, mode_change:1,binary:1;\n \t} *file_diff;\n \tsize_t file_diff_nr;\n+\n+\t/* patch mode */\n+\tstruct patch_mode *mode;\n+\tconst char *revision;\n };\n \n static void err(struct add_p_state *s, const char *fmt, ...)\n@@ -162,9 +194,18 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n \tstruct hunk *hunk = NULL;\n \tint res;\n \n+\targv_array_pushv(&args, s->mode->diff_cmd);\n+\tif (s->revision) {\n+\t\tstruct object_id oid;\n+\t\targv_array_push(&args,\n+\t\t\t\t/* could be on an unborn branch */\n+\t\t\t\t!strcmp(\"HEAD\", s->revision) &&\n+\t\t\t\tget_oid(\"HEAD\", &oid) ?\n+\t\t\t\tempty_tree_oid_hex() : s->revision);\n+\t}\n+\tcolor_arg_index = args.argc;\n \t/* Use `--no-color` explicitly, just in case `diff.color = always`. */\n-\targv_array_pushl(&args, \"diff-files\", \"-p\", \"--no-color\", \"--\", NULL);\n-\tcolor_arg_index = args.argc - 2;\n+\targv_array_pushl(&args, \"--no-color\", \"-p\", \"--\", NULL);\n \tfor (i = 0; i < ps->nr; i++)\n \t\targv_array_push(&args, ps->items[i].original);\n \n@@ -382,7 +423,10 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,\n \t\t\t\t- header->colored_extra_start;\n \t\t}\n \n-\t\tnew_offset += delta;\n+\t\tif (s->mode->is_reverse)\n+\t\t\told_offset -= delta;\n+\t\telse\n+\t\t\tnew_offset += delta;\n \n \t\tstrbuf_addf(out, \"@@ -%lu,%lu +%lu,%lu @@\",\n \t\t\t    old_offset, header->old_count,\n@@ -805,11 +849,10 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)\n \t\t\t\t\"(context).\\n\"\n \t\t\t\t\"To remove '%c' lines, delete them.\\n\"\n \t\t\t\t\"Lines starting with %c will be removed.\\n\"),\n-\t\t\t      '-', '+', comment_line_char);\n-\tstrbuf_commented_addf(&s->buf,\n-\t\t\t      _(\"If the patch applies cleanly, the edited hunk \"\n-\t\t\t\t\"will immediately be\\n\"\n-\t\t\t\t\"marked for staging.\\n\"));\n+\t\t\t      s->mode->is_reverse ? '+' : '-',\n+\t\t\t      s->mode->is_reverse ? '-' : '+',\n+\t\t\t      comment_line_char);\n+\tstrbuf_commented_addf(&s->buf, \"%s\", _(s->mode->edit_hunk_hint));\n \t/*\n \t * TRANSLATORS: 'it' refers to the patch mentioned in the previous\n \t * messages.\n@@ -890,7 +933,8 @@ static int run_apply_check(struct add_p_state *s,\n \treassemble_patch(s, file_diff, 1, &s->buf);\n \n \tsetup_child_process(s, &cp,\n-\t\t\t    \"apply\", \"--cached\", \"--check\", NULL);\n+\t\t\t    \"apply\", \"--check\", NULL);\n+\targv_array_pushv(&cp.args, s->mode->apply_check_args);\n \tif (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))\n \t\treturn error(_(\"'git apply --cached' failed\"));\n \n@@ -1005,13 +1049,6 @@ static size_t display_hunks(struct add_p_state *s,\n \treturn end_index;\n }\n \n-static const char help_patch_text[] =\n-N_(\"y - stage this hunk\\n\"\n-   \"n - do not stage this hunk\\n\"\n-   \"q - quit; do not stage this hunk or any of the remaining ones\\n\"\n-   \"a - stage this and all the remaining hunks\\n\"\n-   \"d - do not stage this hunk nor any of the remaining hunks\\n\");\n-\n static const char help_patch_remainder[] =\n N_(\"j - leave this hunk undecided, see next undecided hunk\\n\"\n    \"J - leave this hunk undecided, see next hunk\\n\"\n@@ -1097,7 +1134,8 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\t      (uintmax_t)hunk_index + 1,\n \t\t\t      (uintmax_t)file_diff->hunk_nr);\n \t\tcolor_fprintf(stdout, s->s.prompt_color,\n-\t\t\t      _(prompt_mode[prompt_mode_type]), s->buf.buf);\n+\t\t\t      _(s->mode->prompt_mode[prompt_mode_type]),\n+\t\t\t      s->buf.buf);\n \t\tfflush(stdout);\n \t\tif (strbuf_getline(&s->answer, stdin) == EOF)\n \t\t\tbreak;\n@@ -1254,7 +1292,7 @@ static int patch_update_file(struct add_p_state *s,\n \t\t\tconst char *p = _(help_patch_remainder), *eol = p;\n \n \t\t\tcolor_fprintf(stdout, s->s.help_color, \"%s\",\n-\t\t\t\t      _(help_patch_text));\n+\t\t\t\t      _(s->mode->help_patch_text));\n \n \t\t\t/*\n \t\t\t * Show only those lines of the remainder that are\n@@ -1288,10 +1326,11 @@ static int patch_update_file(struct add_p_state *s,\n \t\treassemble_patch(s, file_diff, 0, &s->buf);\n \n \t\tdiscard_index(s->s.r->index);\n-\t\tsetup_child_process(s, &cp, \"apply\", \"--cached\", NULL);\n+\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n+\t\targv_array_pushv(&cp.args, s->mode->apply_args);\n \t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n \t\t\t\t NULL, 0, NULL, 0))\n-\t\t\terror(_(\"'git apply --cached' failed\"));\n+\t\t\terror(_(\"'git apply' failed\"));\n \t\tif (!repo_read_index(s->s.r))\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@@ -1301,7 +1340,8 @@ static int patch_update_file(struct add_p_state *s,\n \treturn quit;\n }\n \n-int run_add_p(struct repository *r, const struct pathspec *ps)\n+int run_add_p(struct repository *r, enum add_p_mode mode,\n+\t      const char *revision, const struct pathspec *ps)\n {\n \tstruct add_p_state s = {\n \t\t{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT\n@@ -1310,6 +1350,9 @@ int run_add_p(struct repository *r, const struct pathspec *ps)\n \n \tinit_add_i_state(&s.s, r);\n \n+\ts.mode = &patch_mode_add;\n+\ts.revision = revision;\n+\n \tif (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||\n \t    repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,\n \t\t\t\t\t NULL, NULL, NULL) < 0 ||\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 1deb59a642..006016267e 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -194,12 +194,18 @@ int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\t\t    &use_builtin_add_i);\n \n \tif (use_builtin_add_i == 1) {\n+\t\tenum add_p_mode mode;\n+\n \t\tif (!patch_mode)\n \t\t\treturn !!run_add_i(the_repository, pathspec);\n-\t\tif (strcmp(patch_mode, \"--patch\"))\n+\n+\t\tif (!strcmp(patch_mode, \"--patch\"))\n+\t\t\tmode = ADD_P_ADD;\n+\t\telse\n \t\t\tdie(\"'%s' not yet supported in the built-in add -p\",\n \t\t\t    patch_mode);\n-\t\treturn !!run_add_p(the_repository, pathspec);\n+\n+\t\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n \t}\n \n \targv_array_push(&argv, \"add--interactive\");\n-- \ngitgitgadget\n\n"},{"id":"388745","messageId":"bf9b7f897adce9a2a8316e85b504a0721afeaa35.1576965436.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.v2.git.1576965436.gitgitgadget@gmail.com","subject":"[PATCH v2 3/7] legacy stash -p: respect the add.interactive.usebuiltin setting","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-21T21:57:12Z","receivedAt":"2019-12-21T21:57:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAs `git add` traditionally did not expose the `--patch=<mode>` modes via\ncommand-line options, the scripted version of `git stash` had to call\n`git add--interactive` directly.\n\nBut this prevents the built-in `add -p` from kicking in, as\n`add--interactive` is the scripted version (which does not have a\n\"fall-back\" to the built-in version).\n\nSo let's introduce support for internal switch for `git add` that the\nscripted `git stash` can use to call the appropriate backend (scripted\nor built-in, depending on `add.interactive.useBuiltin`).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/add.c       | 14 ++++++++++++++\n git-legacy-stash.sh |  2 +-\n 2 files changed, 15 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex b0d6891479..fa8bf6b10a 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -29,6 +29,7 @@ static const char * const builtin_add_usage[] = {\n static int patch_interactive, add_interactive, edit_interactive;\n static int take_worktree_changes;\n static int add_renormalize;\n+static int legacy_stash_p; /* support for the scripted `git stash` */\n \n struct update_callback_data {\n \tint flags;\n@@ -335,6 +336,8 @@ static struct option builtin_add_options[] = {\n \t\t   N_(\"override the executable bit of the listed files\")),\n \tOPT_HIDDEN_BOOL(0, \"warn-embedded-repo\", &warn_on_embedded_repo,\n \t\t\tN_(\"warn when adding an embedded repository\")),\n+\tOPT_HIDDEN_BOOL(0, \"legacy-stash-p\", &legacy_stash_p,\n+\t\t\tN_(\"backend for `git stash -p`\")),\n \tOPT_END(),\n };\n \n@@ -431,6 +434,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\tadd_interactive = 1;\n \tif (add_interactive)\n \t\texit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));\n+\tif (legacy_stash_p) {\n+\t\tstruct pathspec pathspec;\n+\n+\t\tparse_pathspec(&pathspec, 0,\n+\t\t\tPATHSPEC_PREFER_FULL |\n+\t\t\tPATHSPEC_SYMLINK_LEADING_PATH |\n+\t\t\tPATHSPEC_PREFIX_ORIGIN,\n+\t\t\tprefix, argv);\n+\n+\t\treturn run_add_interactive(NULL, \"--patch=stash\", &pathspec);\n+\t}\n \n \tif (edit_interactive)\n \t\treturn(edit_patch(argc, argv, prefix));\ndiff --git a/git-legacy-stash.sh b/git-legacy-stash.sh\nindex 07ad4a5459..ed039dfcbb 100755\n--- a/git-legacy-stash.sh\n+++ b/git-legacy-stash.sh\n@@ -206,7 +206,7 @@ create_stash () {\n \n \t\t# find out what the user wants\n \t\tGIT_INDEX_FILE=\"$TMP-index\" \\\n-\t\t\tgit add--interactive --patch=stash -- \"$@\" &&\n+\t\t\tgit add --legacy-stash-p -- \"$@\" &&\n \n \t\t# state of the working tree\n \t\tw_tree=$(GIT_INDEX_FILE=\"$TMP-index\" git write-tree) ||\n-- \ngitgitgadget\n\n"},{"id":"388746","messageId":"6977bede86608636265c069e21779de706d0b917.1576965436.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.v2.git.1576965436.gitgitgadget@gmail.com","subject":"[PATCH v2 2/7] built-in add -p: implement the \"stash\" and \"reset\" patch modes","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-21T21:57:11Z","receivedAt":"2019-12-21T21:57:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe `git stash` and `git reset` commands support a `--patch` option, and\nboth simply hand off to `git add -p` to perform that work. Let's teach\nthe built-in version of that command to be able to perform that work, too.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n add-interactive.h |  2 ++\n add-patch.c       | 83 ++++++++++++++++++++++++++++++++++++++++++++---\n builtin/add.c     |  4 +++\n 3 files changed, 85 insertions(+), 4 deletions(-)\n\ndiff --git a/add-interactive.h b/add-interactive.h\nindex e29a769aba..1f6a61326e 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -25,6 +25,8 @@ int run_add_i(struct repository *r, const struct pathspec *ps);\n \n enum add_p_mode {\n \tADD_P_ADD,\n+\tADD_P_STASH,\n+\tADD_P_RESET,\n };\n \n int run_add_p(struct repository *r, enum add_p_mode mode,\ndiff --git a/add-patch.c b/add-patch.c\nindex 71356fbd9a..af0a86f0f7 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -19,7 +19,7 @@ struct patch_mode {\n \t * trailing `NULL`.\n \t */\n \tconst char *diff_cmd[4], *apply_args[4], *apply_check_args[4];\n-\tunsigned is_reverse:1, apply_for_checkout:1;\n+\tunsigned is_reverse:1, index_only:1, apply_for_checkout:1;\n \tconst char *prompt_mode[PROMPT_MODE_MAX];\n \tconst char *edit_hunk_hint, *help_patch_text;\n };\n@@ -45,6 +45,72 @@ static struct patch_mode patch_mode_add = {\n \t\t\t\"the file\\n\")\n };\n \n+static struct patch_mode patch_mode_stash = {\n+\t.diff_cmd = { \"diff-index\", \"HEAD\", NULL },\n+\t.apply_args = { \"--cached\", NULL },\n+\t.apply_check_args = { \"--cached\", NULL },\n+\t.prompt_mode = {\n+\t\tN_(\"Stash mode change [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Stash deletion [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Stash this hunk [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for stashing.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - stash this hunk\\n\"\n+\t\t   \"n - do not stash this hunk\\n\"\n+\t\t   \"q - quit; do not stash this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - stash this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not stash this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n+static struct patch_mode patch_mode_reset_head = {\n+\t.diff_cmd = { \"diff-index\", \"--cached\", NULL },\n+\t.apply_args = { \"-R\", \"--cached\", NULL },\n+\t.apply_check_args = { \"-R\", \"--cached\", NULL },\n+\t.is_reverse = 1,\n+\t.index_only = 1,\n+\t.prompt_mode = {\n+\t\tN_(\"Unstage mode change [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Unstage deletion [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Unstage this hunk [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for unstaging.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - unstage this hunk\\n\"\n+\t\t   \"n - do not unstage this hunk\\n\"\n+\t\t   \"q - quit; do not unstage this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - unstage this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not unstage this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n+static struct patch_mode patch_mode_reset_nothead = {\n+\t.diff_cmd = { \"diff-index\", \"-R\", \"--cached\", NULL },\n+\t.apply_args = { \"--cached\", NULL },\n+\t.apply_check_args = { \"--cached\", NULL },\n+\t.index_only = 1,\n+\t.prompt_mode = {\n+\t\tN_(\"Apply mode change to index [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply deletion to index [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply this hunk to index [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for applying.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - apply this hunk to index\\n\"\n+\t\t   \"n - do not apply this hunk to index\\n\"\n+\t\t   \"q - quit; do not apply this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - apply this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not apply this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n struct hunk_header {\n \tunsigned long old_offset, old_count, new_offset, new_count;\n \t/*\n@@ -1350,12 +1416,21 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \n \tinit_add_i_state(&s.s, r);\n \n-\ts.mode = &patch_mode_add;\n+\tif (mode == ADD_P_STASH)\n+\t\ts.mode = &patch_mode_stash;\n+\telse if (mode == ADD_P_RESET) {\n+\t\tif (!revision || !strcmp(revision, \"HEAD\"))\n+\t\t\ts.mode = &patch_mode_reset_head;\n+\t\telse\n+\t\t\ts.mode = &patch_mode_reset_nothead;\n+\t} else\n+\t\ts.mode = &patch_mode_add;\n \ts.revision = revision;\n \n \tif (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||\n-\t    repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,\n-\t\t\t\t\t NULL, NULL, NULL) < 0 ||\n+\t    (!s.mode->index_only &&\n+\t     repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,\n+\t\t\t\t\t  NULL, NULL, NULL) < 0) ||\n \t    parse_diff(&s, ps) < 0) {\n \t\tstrbuf_release(&s.plain);\n \t\tstrbuf_release(&s.colored);\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 006016267e..b0d6891479 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -201,6 +201,10 @@ int run_add_interactive(const char *revision, const char *patch_mode,\n \n \t\tif (!strcmp(patch_mode, \"--patch\"))\n \t\t\tmode = ADD_P_ADD;\n+\t\telse if (!strcmp(patch_mode, \"--patch=stash\"))\n+\t\t\tmode = ADD_P_STASH;\n+\t\telse if (!strcmp(patch_mode, \"--patch=reset\"))\n+\t\t\tmode = ADD_P_RESET;\n \t\telse\n \t\t\tdie(\"'%s' not yet supported in the built-in add -p\",\n \t\t\t    patch_mode);\n-- \ngitgitgadget\n\n"},{"id":"388747","messageId":"ecb22159d8cf1aab83bafdf7c0cb10a2e2a6a826.1576965436.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.v2.git.1576965436.gitgitgadget@gmail.com","subject":"[PATCH v2 7/7] commit --interactive: make it work with the built-in `add -i`","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-21T21:57:16Z","receivedAt":"2019-12-21T21:57:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe built-in `git add -i` machinery obviously has its `the_repository`\nstructure initialized at the point where `cmd_commit()` calls it, and\ntherefore does not look at the environment variable `GIT_INDEX_FILE`.\n\nBut when being called from `commit --interactive`, it has to, because\nthe index was already locked in that case, and we want to ask the\ninteractive add machinery to work on the `index.lock` file instead of\nthe `index` file.\n\nTechnically, we could teach `run_add_i()`, or for that matter\n`run_add_p()`, to look specifically at that environment variable, but\nthe entire idea of passing in a parameter of type `struct repository *`\nis to allow working on multiple repositories (and their index files)\nindependently.\n\nSo let's instead override the `index_file` field of that structure\ntemporarily.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/commit.c | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex e588bc6ad3..32ffc7beee 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -347,7 +347,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix\n \t\tdie(_(\"index file corrupt\"));\n \n \tif (interactive) {\n-\t\tchar *old_index_env = NULL;\n+\t\tchar *old_index_env = NULL, *old_repo_index_file;\n \t\thold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);\n \n \t\trefresh_cache_or_die(refresh_flags);\n@@ -355,12 +355,16 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix\n \t\tif (write_locked_index(&the_index, &index_lock, 0))\n \t\t\tdie(_(\"unable to create temporary index\"));\n \n+\t\told_repo_index_file = the_repository->index_file;\n+\t\tthe_repository->index_file =\n+\t\t\t(char *)get_lock_file_path(&index_lock);\n \t\told_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));\n-\t\tsetenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1);\n+\t\tsetenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);\n \n \t\tif (interactive_add(argc, argv, prefix, patch_interactive) != 0)\n \t\t\tdie(_(\"interactive add failed\"));\n \n+\t\tthe_repository->index_file = old_repo_index_file;\n \t\tif (old_index_env && *old_index_env)\n \t\t\tsetenv(INDEX_ENVIRONMENT, old_index_env, 1);\n \t\telse\n-- \ngitgitgadget\n"},{"id":"388748","messageId":"b3672b35e694660a57ba1201ec79ecf561d27704.1576965436.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.v2.git.1576965436.gitgitgadget@gmail.com","subject":"[PATCH v2 5/7] built-in add -p: implement the \"checkout\" patch modes","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-21T21:57:14Z","receivedAt":"2019-12-21T21:57:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis patch teaches the built-in `git add -p` machinery all the tricks it\nneeds to know in order to act as the work horse for `git checkout -p`.\n\nApart from the minor changes (slightly reworded messages, different\n`diff` and `apply --check` invocations), it requires a new function to\nactually apply the changes, as `git checkout -p` is a bit special in\nthat respect: when the desired changes do not apply to the index, but\napply to the work tree, Git does not fail straight away, but asks the\nuser whether to apply the changes to the worktree at least.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n add-interactive.h |   1 +\n add-patch.c       | 138 ++++++++++++++++++++++++++++++++++++++++++++--\n builtin/add.c     |   5 +-\n 3 files changed, 137 insertions(+), 7 deletions(-)\n\ndiff --git a/add-interactive.h b/add-interactive.h\nindex 1f6a61326e..77907f6e21 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -27,6 +27,7 @@ enum add_p_mode {\n \tADD_P_ADD,\n \tADD_P_STASH,\n \tADD_P_RESET,\n+\tADD_P_CHECKOUT,\n };\n \n int run_add_p(struct repository *r, enum add_p_mode mode,\ndiff --git a/add-patch.c b/add-patch.c\nindex af0a86f0f7..ec5116c187 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -111,6 +111,71 @@ static struct patch_mode patch_mode_reset_nothead = {\n \t\t\t\"the file\\n\"),\n };\n \n+static struct patch_mode patch_mode_checkout_index = {\n+\t.diff_cmd = { \"diff-files\", NULL },\n+\t.apply_args = { \"-R\", NULL },\n+\t.apply_check_args = { \"-R\", NULL },\n+\t.is_reverse = 1,\n+\t.prompt_mode = {\n+\t\tN_(\"Discard mode change from worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard deletion from worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard this hunk from worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for discarding.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - discard this hunk from worktree\\n\"\n+\t\t   \"n - do not discard this hunk from worktree\\n\"\n+\t\t   \"q - quit; do not discard this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - discard this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not discard this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n+static struct patch_mode patch_mode_checkout_head = {\n+\t.diff_cmd = { \"diff-index\", NULL },\n+\t.apply_for_checkout = 1,\n+\t.apply_check_args = { \"-R\", NULL },\n+\t.is_reverse = 1,\n+\t.prompt_mode = {\n+\t\tN_(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard deletion from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard this hunk from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for discarding.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - discard this hunk from index and worktree\\n\"\n+\t\t   \"n - do not discard this hunk from index and worktree\\n\"\n+\t\t   \"q - quit; do not discard this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - discard this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not discard this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n+static struct patch_mode patch_mode_checkout_nothead = {\n+\t.diff_cmd = { \"diff-index\", \"-R\", NULL },\n+\t.apply_for_checkout = 1,\n+\t.apply_check_args = { NULL },\n+\t.prompt_mode = {\n+\t\tN_(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply this hunk to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for applying.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - apply this hunk to index and worktree\\n\"\n+\t\t   \"n - do not apply this hunk to index and worktree\\n\"\n+\t\t   \"q - quit; do not apply this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - apply this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not apply this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n struct hunk_header {\n \tunsigned long old_offset, old_count, new_offset, new_count;\n \t/*\n@@ -1067,6 +1132,57 @@ static int edit_hunk_loop(struct add_p_state *s,\n \t}\n }\n \n+static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,\n+\t\t\t      int is_reverse)\n+{\n+\tconst char *reverse = is_reverse ? \"-R\" : NULL;\n+\tstruct child_process check_index = CHILD_PROCESS_INIT;\n+\tstruct child_process check_worktree = CHILD_PROCESS_INIT;\n+\tstruct child_process apply_index = CHILD_PROCESS_INIT;\n+\tstruct child_process apply_worktree = CHILD_PROCESS_INIT;\n+\tint applies_index, applies_worktree;\n+\n+\tsetup_child_process(s, &check_index,\n+\t\t\t    \"apply\", \"--cached\", \"--check\", reverse, NULL);\n+\tapplies_index = !pipe_command(&check_index, diff->buf, diff->len,\n+\t\t\t\t      NULL, 0, NULL, 0);\n+\n+\tsetup_child_process(s, &check_worktree,\n+\t\t\t    \"apply\", \"--check\", reverse, NULL);\n+\tapplies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,\n+\t\t\t\t\t NULL, 0, NULL, 0);\n+\n+\tif (applies_worktree && applies_index) {\n+\t\tsetup_child_process(s, &apply_index,\n+\t\t\t\t    \"apply\", \"--cached\", reverse, NULL);\n+\t\tpipe_command(&apply_index, diff->buf, diff->len,\n+\t\t\t     NULL, 0, NULL, 0);\n+\n+\t\tsetup_child_process(s, &apply_worktree,\n+\t\t\t\t    \"apply\", reverse, NULL);\n+\t\tpipe_command(&apply_worktree, diff->buf, diff->len,\n+\t\t\t     NULL, 0, NULL, 0);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tif (!applies_index) {\n+\t\terr(s, _(\"The selected hunks do not apply to the index!\"));\n+\t\tif (prompt_yesno(s, _(\"Apply them to the worktree \"\n+\t\t\t\t\t  \"anyway? \")) > 0) {\n+\t\t\tsetup_child_process(s, &apply_worktree,\n+\t\t\t\t\t    \"apply\", reverse, NULL);\n+\t\t\treturn pipe_command(&apply_worktree, diff->buf,\n+\t\t\t\t\t    diff->len, NULL, 0, NULL, 0);\n+\t\t}\n+\t\terr(s, _(\"Nothing was applied.\\n\"));\n+\t} else\n+\t\t/* As a last resort, show the diff to the user */\n+\t\tfwrite(diff->buf, diff->len, 1, stderr);\n+\n+\treturn 0;\n+}\n+\n #define SUMMARY_HEADER_WIDTH 20\n #define SUMMARY_LINE_WIDTH 80\n static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,\n@@ -1392,11 +1508,16 @@ static int patch_update_file(struct add_p_state *s,\n \t\treassemble_patch(s, file_diff, 0, &s->buf);\n \n \t\tdiscard_index(s->s.r->index);\n-\t\tsetup_child_process(s, &cp, \"apply\", NULL);\n-\t\targv_array_pushv(&cp.args, s->mode->apply_args);\n-\t\tif (pipe_command(&cp, s->buf.buf, s->buf.len,\n-\t\t\t\t NULL, 0, NULL, 0))\n-\t\t\terror(_(\"'git apply' failed\"));\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\targv_array_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))\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@@ -1423,6 +1544,13 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\t\ts.mode = &patch_mode_reset_head;\n \t\telse\n \t\t\ts.mode = &patch_mode_reset_nothead;\n+\t} else if (mode == ADD_P_CHECKOUT) {\n+\t\tif (!revision)\n+\t\t\ts.mode = &patch_mode_checkout_index;\n+\t\telse if (!strcmp(revision, \"HEAD\"))\n+\t\t\ts.mode = &patch_mode_checkout_head;\n+\t\telse\n+\t\t\ts.mode = &patch_mode_checkout_nothead;\n \t} else\n \t\ts.mode = &patch_mode_add;\n \ts.revision = revision;\ndiff --git a/builtin/add.c b/builtin/add.c\nindex fa8bf6b10a..191856b036 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -206,9 +206,10 @@ int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tmode = ADD_P_STASH;\n \t\telse if (!strcmp(patch_mode, \"--patch=reset\"))\n \t\t\tmode = ADD_P_RESET;\n+\t\telse if (!strcmp(patch_mode, \"--patch=checkout\"))\n+\t\t\tmode = ADD_P_CHECKOUT;\n \t\telse\n-\t\t\tdie(\"'%s' not yet supported in the built-in add -p\",\n-\t\t\t    patch_mode);\n+\t\t\tdie(\"'%s' not supported\", patch_mode);\n \n \t\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n \t}\n-- \ngitgitgadget\n\n"},{"id":"388749","messageId":"5051f37559f88fc9b75391db37f19653498de65a.1576965436.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.v2.git.1576965436.gitgitgadget@gmail.com","subject":"[PATCH v2 4/7] built-in stash: use the built-in `git add -p` if so configured","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-21T21:57:13Z","receivedAt":"2019-12-21T21:57:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe scripted version of `git stash` called directly into the Perl script\n`git-add--interactive.perl`, and this was faithfully converted to C.\n\nHowever, we have a much better way to do this now: call the internal API\ndirectly, which will now incidentally also respect the\n`add.interactive.useBuiltin` setting. Let's just do this.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/stash.c | 25 ++++++++++++++-----------\n 1 file changed, 14 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 4e806176b0..2dafd97766 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -999,9 +999,9 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,\n {\n \tint ret = 0;\n \tstruct child_process cp_read_tree = CHILD_PROCESS_INIT;\n-\tstruct child_process cp_add_i = CHILD_PROCESS_INIT;\n \tstruct child_process cp_diff_tree = CHILD_PROCESS_INIT;\n \tstruct index_state istate = { NULL };\n+\tchar *old_index_env = NULL, *old_repo_index_file;\n \n \tremove_path(stash_index_path.buf);\n \n@@ -1015,16 +1015,19 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,\n \t}\n \n \t/* Find out what the user wants. */\n-\tcp_add_i.git_cmd = 1;\n-\targv_array_pushl(&cp_add_i.args, \"add--interactive\", \"--patch=stash\",\n-\t\t\t \"--\", NULL);\n-\tadd_pathspecs(&cp_add_i.args, ps);\n-\targv_array_pushf(&cp_add_i.env_array, \"GIT_INDEX_FILE=%s\",\n-\t\t\t stash_index_path.buf);\n-\tif (run_command(&cp_add_i)) {\n-\t\tret = -1;\n-\t\tgoto done;\n-\t}\n+\told_repo_index_file = the_repository->index_file;\n+\tthe_repository->index_file = stash_index_path.buf;\n+\told_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));\n+\tsetenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);\n+\n+\tret = run_add_interactive(NULL, \"--patch=stash\", ps);\n+\n+\tthe_repository->index_file = old_repo_index_file;\n+\tif (old_index_env && *old_index_env)\n+\t\tsetenv(INDEX_ENVIRONMENT, old_index_env, 1);\n+\telse\n+\t\tunsetenv(INDEX_ENVIRONMENT);\n+\tFREE_AND_NULL(old_index_env);\n \n \t/* State of the working tree. */\n \tif (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,\n-- \ngitgitgadget\n\n"},{"id":"388750","messageId":"83e2ac6e67bd92850c2e0cfe628a20176eac4670.1576965436.git.gitgitgadget@gmail.com","threadId":"52473","inReplyTo":"pull.174.v2.git.1576965436.gitgitgadget@gmail.com","subject":"[PATCH v2 6/7] built-in add -p: implement the \"worktree\" patch modes","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-12-21T21:57:15Z","receivedAt":"2019-12-21T21:57:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis is a straight-forward port of 2f0896ec3ad4 (restore: support\n--patch, 2019-04-25) which added support for `git restore -p`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n add-interactive.h |  1 +\n add-patch.c       | 50 +++++++++++++++++++++++++++++++++++++++++++++++\n builtin/add.c     |  2 ++\n 3 files changed, 53 insertions(+)\n\ndiff --git a/add-interactive.h b/add-interactive.h\nindex 77907f6e21..b2f23479c5 100644\n--- a/add-interactive.h\n+++ b/add-interactive.h\n@@ -28,6 +28,7 @@ enum add_p_mode {\n \tADD_P_STASH,\n \tADD_P_RESET,\n \tADD_P_CHECKOUT,\n+\tADD_P_WORKTREE,\n };\n \n int run_add_p(struct repository *r, enum add_p_mode mode,\ndiff --git a/add-patch.c b/add-patch.c\nindex ec5116c187..46c6c183d5 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -176,6 +176,49 @@ static struct patch_mode patch_mode_checkout_nothead = {\n \t\t\t\"the file\\n\"),\n };\n \n+static struct patch_mode patch_mode_worktree_head = {\n+\t.diff_cmd = { \"diff-index\", NULL },\n+\t.apply_args = { \"-R\", NULL },\n+\t.apply_check_args = { \"-R\", NULL },\n+\t.is_reverse = 1,\n+\t.prompt_mode = {\n+\t\tN_(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard deletion from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard this hunk from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for discarding.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - discard this hunk from worktree\\n\"\n+\t\t   \"n - do not discard this hunk from worktree\\n\"\n+\t\t   \"q - quit; do not discard this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - discard this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not discard this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n+static struct patch_mode patch_mode_worktree_nothead = {\n+\t.diff_cmd = { \"diff-index\", \"-R\", NULL },\n+\t.apply_args = { NULL },\n+\t.apply_check_args = { NULL },\n+\t.prompt_mode = {\n+\t\tN_(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply this hunk to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t},\n+\t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n+\t\t\t     \"will immediately be marked for applying.\"),\n+\t.help_patch_text =\n+\t\tN_(\"y - apply this hunk to worktree\\n\"\n+\t\t   \"n - do not apply this hunk to worktree\\n\"\n+\t\t   \"q - quit; do not apply this hunk or any of the remaining \"\n+\t\t\t\"ones\\n\"\n+\t\t   \"a - apply this hunk and all later hunks in the file\\n\"\n+\t\t   \"d - do not apply this hunk or any of the later hunks in \"\n+\t\t\t\"the file\\n\"),\n+};\n+\n struct hunk_header {\n \tunsigned long old_offset, old_count, new_offset, new_count;\n \t/*\n@@ -1551,6 +1594,13 @@ int run_add_p(struct repository *r, enum add_p_mode mode,\n \t\t\ts.mode = &patch_mode_checkout_head;\n \t\telse\n \t\t\ts.mode = &patch_mode_checkout_nothead;\n+\t} else if (mode == ADD_P_WORKTREE) {\n+\t\tif (!revision)\n+\t\t\ts.mode = &patch_mode_checkout_index;\n+\t\telse if (!strcmp(revision, \"HEAD\"))\n+\t\t\ts.mode = &patch_mode_worktree_head;\n+\t\telse\n+\t\t\ts.mode = &patch_mode_worktree_nothead;\n \t} else\n \t\ts.mode = &patch_mode_add;\n \ts.revision = revision;\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 191856b036..b5927105aa 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -208,6 +208,8 @@ int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tmode = ADD_P_RESET;\n \t\telse if (!strcmp(patch_mode, \"--patch=checkout\"))\n \t\t\tmode = ADD_P_CHECKOUT;\n+\t\telse if (!strcmp(patch_mode, \"--patch=worktree\"))\n+\t\t\tmode = ADD_P_WORKTREE;\n \t\telse\n \t\t\tdie(\"'%s' not supported\", patch_mode);\n \n-- \ngitgitgadget\n\n"}]}