{"thread":{"id":"59189","subject":"[PATCH 2/3] add API: remove run_add_interactive() wrapper function","startedAt":"2023-02-03T16:30:39Z","lastAt":"2023-02-07T10:21:18Z","messageCount":13,"participants":["Ævar Arnfjörð Bjarmason","Jeff King","Junio C Hamano","Philippe Blain","Phillip Wood"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"471437","messageId":"cover-0.3-00000000000-20230203T125859Z-avarab@gmail.com","threadId":"59189","inReplyTo":null,"subject":"[PATCH 0/3] add: remove Perl version of \"git add -[pi]\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-02-03T16:30:01Z","receivedAt":"2023-02-03T16:30:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The v2.37.0 we've made the built-in version of \"git add -i\" and \"git\nadd -p\" the default, after having had the built-in version optionally\navailable since v2.25.0.\n\nWith this the time we kept around the legacy implementation falls\nbetween the time we kept the similar \"rebase\" and \"stash\"\nimplementations.\n\nAfter it was made the default we had a next-release regression fix[1],\nbut haven't had any issues since then. Any outstanding bugs in it are\nsomething we'd fix in the C code, not something where users are likely\nto want an escape hatch to scramble back to the Perl implementation.\n\nBranch & CI for this available at:\nhttps://github.com/avar/git/tree/avar/nuke-perl-add--interactive\n\n1. 4788e8b2569 (add --interactive: allow `update` to stage deleted\n   files, 2022-06-28)\n   https://github.com/msys2/MSYS2-packages/issues/3066\n\nÆvar Arnfjörð Bjarmason (3):\n  add: remove \"add.interactive.useBuiltin\" & Perl \"git add--interactive\"\n  add API: remove run_add_interactive() wrapper function\n  docs & comments: replace mentions of \"git-add--interactive.perl\"\n\n .gitignore                   |    1 -\n Documentation/config/add.txt |    7 +-\n Documentation/git-add.txt    |    6 +-\n Makefile                     |    1 -\n builtin/add.c                |   61 +-\n builtin/checkout.c           |   12 +-\n builtin/clean.c              |    2 +-\n builtin/reset.c              |    4 +-\n builtin/stash.c              |    3 +-\n ci/run-build-and-tests.sh    |    1 -\n commit.h                     |    2 -\n git-add--interactive.perl    | 1920 ----------------------------------\n pathspec.c                   |    2 +-\n t/README                     |    4 -\n t/t2016-checkout-patch.sh    |    6 -\n t/t3701-add-interactive.sh   |   29 +-\n t/t6132-pathspec-exclude.sh  |    6 +-\n t/test-lib.sh                |    4 -\n 18 files changed, 49 insertions(+), 2022 deletions(-)\n delete mode 100755 git-add--interactive.perl\n\n-- \n2.39.1.1397.gbe42486b8a4\n\n"},{"id":"471436","messageId":"patch-2.3-fb0dafaf4a4-20230203T125859Z-avarab@gmail.com","threadId":"59189","inReplyTo":"cover-0.3-00000000000-20230203T125859Z-avarab@gmail.com","subject":"[PATCH 2/3] add API: remove run_add_interactive() wrapper function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-02-03T16:30:03Z","receivedAt":"2023-02-03T16:30:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that the Perl \"git-add--interactive\" has gone away in the\npreceding commit we don't need to pass along our desire for a mode as\na string, and can instead directly use the \"enum add_p_mode\", see\nd2a233cb8b9 (built-in add -p: prepare for patch modes other than\n\"stage\", 2019-12-21) for its introduction.\n\nAs a result of that the run_add_interactive() function would become a\ntrivial wrapper which would only run run_add_i() if a 0 (or now,\n\"NULL\") \"patch_mode\" was provided. Let's instead remove it, and have\nthe one callsite that wanted the \"NULL\" case (interactive_add())\nhandle it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/add.c      | 31 ++++---------------------------\n builtin/checkout.c | 12 +++++++-----\n builtin/reset.c    |  4 +++-\n builtin/stash.c    |  3 ++-\n commit.h           |  2 --\n 5 files changed, 16 insertions(+), 36 deletions(-)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 9f5b6e26a02..61dd386d109 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -238,30 +238,6 @@ static int refresh(int verbose, const struct pathspec *pathspec)\n \treturn ret;\n }\n \n-int run_add_interactive(const char *revision, const char *patch_mode,\n-\t\t\tconst struct pathspec *pathspec)\n-{\n-\tenum add_p_mode mode;\n-\n-\tif (!patch_mode)\n-\t\treturn !!run_add_i(the_repository, pathspec);\n-\n-\tif (!strcmp(patch_mode, \"--patch\"))\n-\t\tmode = ADD_P_ADD;\n-\telse if (!strcmp(patch_mode, \"--patch=stash\"))\n-\t\tmode = ADD_P_STASH;\n-\telse if (!strcmp(patch_mode, \"--patch=reset\"))\n-\t\tmode = ADD_P_RESET;\n-\telse if (!strcmp(patch_mode, \"--patch=checkout\"))\n-\t\tmode = ADD_P_CHECKOUT;\n-\telse if (!strcmp(patch_mode, \"--patch=worktree\"))\n-\t\tmode = ADD_P_WORKTREE;\n-\telse\n-\t\tdie(\"'%s' not supported\", patch_mode);\n-\n-\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n-}\n-\n int interactive_add(const char **argv, const char *prefix, int patch)\n {\n \tstruct pathspec pathspec;\n@@ -277,9 +253,10 @@ int interactive_add(const char **argv, const char *prefix, int patch)\n \t\t       PATHSPEC_PREFIX_ORIGIN,\n \t\t       prefix, argv);\n \n-\treturn run_add_interactive(NULL,\n-\t\t\t\t   patch ? \"--patch\" : NULL,\n-\t\t\t\t   &pathspec);\n+\tif (patch)\n+\t\treturn !!run_add_p(the_repository, ADD_P_ADD, NULL, &pathspec);\n+\telse\n+\t\treturn !!run_add_i(the_repository, &pathspec);\n }\n \n static int edit_patch(int argc, const char **argv, const char *prefix)\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5963e1b74b8..a5155cf55c1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -29,6 +29,7 @@\n #include \"xdiff-interface.h\"\n #include \"entry.h\"\n #include \"parallel-checkout.h\"\n+#include \"add-interactive.h\"\n \n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n@@ -499,7 +500,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\t    \"--merge\", \"--conflict\", \"--staged\");\n \n \tif (opts->patch_mode) {\n-\t\tconst char *patch_mode;\n+\t\tenum add_p_mode patch_mode;\n \t\tconst char *rev = new_branch_info->name;\n \t\tchar rev_oid[GIT_MAX_HEXSZ + 1];\n \n@@ -517,15 +518,16 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\t\trev = oid_to_hex_r(rev_oid, &new_branch_info->commit->object.oid);\n \n \t\tif (opts->checkout_index && opts->checkout_worktree)\n-\t\t\tpatch_mode = \"--patch=checkout\";\n+\t\t\tpatch_mode = ADD_P_CHECKOUT;\n \t\telse if (opts->checkout_index && !opts->checkout_worktree)\n-\t\t\tpatch_mode = \"--patch=reset\";\n+\t\t\tpatch_mode = ADD_P_RESET;\n \t\telse if (!opts->checkout_index && opts->checkout_worktree)\n-\t\t\tpatch_mode = \"--patch=worktree\";\n+\t\t\tpatch_mode = ADD_P_WORKTREE;\n \t\telse\n \t\t\tBUG(\"either flag must have been set, worktree=%d, index=%d\",\n \t\t\t    opts->checkout_worktree, opts->checkout_index);\n-\t\treturn run_add_interactive(rev, patch_mode, &opts->pathspec);\n+\t\treturn !!run_add_p(the_repository, patch_mode, rev,\n+\t\t\t\t   &opts->pathspec);\n \t}\n \n \trepo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);\ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex fea20a9ba0b..4b59aa9aeae 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -26,6 +26,7 @@\n #include \"submodule.h\"\n #include \"submodule-config.h\"\n #include \"dir.h\"\n+#include \"add-interactive.h\"\n \n #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n \n@@ -390,7 +391,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\tif (reset_type != NONE)\n \t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--patch\", \"--{hard,mixed,soft}\");\n \t\ttrace2_cmd_mode(\"patch-interactive\");\n-\t\treturn run_add_interactive(rev, \"--patch=reset\", &pathspec);\n+\t\treturn !!run_add_p(the_repository, ADD_P_RESET, rev,\n+\t\t\t\t   &pathspec);\n \t}\n \n \t/* git reset tree [--] paths... can be used to\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 839569a9803..9fcd2e5d993 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -18,6 +18,7 @@\n #include \"diffcore.h\"\n #include \"exec-cmd.h\"\n #include \"reflog.h\"\n+#include \"add-interactive.h\"\n \n #define INCLUDE_ALL_FILES 2\n \n@@ -1229,7 +1230,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,\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+\tret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps);\n \n \tthe_repository->index_file = old_repo_index_file;\n \tif (old_index_env && *old_index_env)\ndiff --git a/commit.h b/commit.h\nindex fa39202fa6b..cc2c5da7bdb 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -274,8 +274,6 @@ struct ref;\n int for_each_commit_graft(each_commit_graft_fn, void *);\n \n int interactive_add(const char **argv, const char *prefix, int patch);\n-int run_add_interactive(const char *revision, const char *patch_mode,\n-\t\t\tconst struct pathspec *pathspec);\n \n struct commit_extra_header {\n \tstruct commit_extra_header *next;\n-- \n2.39.1.1397.gbe42486b8a4\n\n"},{"id":"471438","messageId":"patch-3.3-6ee56de1371-20230203T125859Z-avarab@gmail.com","threadId":"59189","inReplyTo":"cover-0.3-00000000000-20230203T125859Z-avarab@gmail.com","subject":"[PATCH 3/3] docs & comments: replace mentions of \"git-add--interactive.perl\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-02-03T16:30:04Z","receivedAt":"2023-02-03T16:30:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that we've removed \"git-add--interactive.perl\" let's replace\nmentions of it with \"add-interactive.c\". In the case of the \"git add\"\ndocumentation we were using it as an example filename, so the mention\nwasn't wrong, but using a dead file is slightly confusing.\n\nThe \"borrowed\" comment here likewise isn't wrong, but let's mention\nthe successor file instead. In the case of pathspec.c the implied TODO\nitem should refer to the current code (and the comment may not even be\ncurrent, I didn't check).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-add.txt | 6 +++---\n builtin/clean.c           | 2 +-\n pathspec.c                | 2 +-\n 3 files changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-add.txt b/Documentation/git-add.txt\nindex a030d33c6e7..ed44c1cb31c 100644\n--- a/Documentation/git-add.txt\n+++ b/Documentation/git-add.txt\n@@ -274,7 +274,7 @@ status::\n ------------\n               staged     unstaged path\n      1:       binary      nothing foo.png\n-     2:     +403/-35        +1/-1 git-add--interactive.perl\n+     2:     +403/-35        +1/-1 add-interactive.c\n ------------\n +\n It shows that foo.png has differences from HEAD (but that is\n@@ -282,7 +282,7 @@ binary so line count cannot be shown) and there is no\n difference between indexed copy and the working tree\n version (if the working tree version were also different,\n 'binary' would have been shown in place of 'nothing').  The\n-other file, git-add{litdd}interactive.perl, has 403 lines added\n+other file, add-interactive.c, has 403 lines added\n and 35 lines deleted if you commit what is in the index, but\n working tree file has further modifications (one addition and\n one deletion).\n@@ -303,7 +303,7 @@ like this:\n ------------\n            staged     unstaged path\n   1:       binary      nothing foo.png\n-* 2:     +403/-35        +1/-1 git-add--interactive.perl\n+* 2:     +403/-35        +1/-1 add-interactive.c\n ------------\n +\n To remove selection, prefix the input with `-`\ndiff --git a/builtin/clean.c b/builtin/clean.c\nindex b2701a28158..1e6d491d3be 100644\n--- a/builtin/clean.c\n+++ b/builtin/clean.c\n@@ -560,7 +560,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,\n \n /*\n  * Implement a git-add-interactive compatible UI, which is borrowed\n- * from git-add--interactive.perl.\n+ * from add-interactive.c.\n  *\n  * Return value:\n  *\ndiff --git a/pathspec.c b/pathspec.c\nindex dbcfe7b3216..ab70fcbe613 100644\n--- a/pathspec.c\n+++ b/pathspec.c\n@@ -545,7 +545,7 @@ static void NORETURN unsupported_magic(const char *pattern,\n \t}\n \t/*\n \t * We may want to substitute \"this command\" with a command\n-\t * name. E.g. when add--interactive dies when running\n+\t * name. E.g. when \"git add -p\" or \"git add -i\" dies when running\n \t * \"checkout -p\"\n \t */\n \tdie(_(\"%s: pathspec magic not supported by this command: %s\"),\n-- \n2.39.1.1397.gbe42486b8a4\n\n"},{"id":"471439","messageId":"patch-1.3-f39dde93011-20230203T125859Z-avarab@gmail.com","threadId":"59189","inReplyTo":"cover-0.3-00000000000-20230203T125859Z-avarab@gmail.com","subject":"[PATCH 1/3] add: remove \"add.interactive.useBuiltin\" & Perl \"git add--interactive\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-02-03T16:30:02Z","receivedAt":"2023-02-03T16:30:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since [1] first released with Git v2.37.0 the built-in version of \"add\n-i\" has been the default. That built-in implementation was added in\n[2], first released with Git v2.25.0.\n\nAt this point enough time has passed to allow for finding any\nremaining bugs in this new implementation, so let's remove the\nfallback code.\n\nAs with similar migrations for \"stash\"[3] and \"rebase\"[4] we're\nkeeping a mention of \"add.interactive.useBuiltin\" in the\ndocumentation, but adding a warning() to notify any outstanding users\nthat the built-in is now the default. As with [5] and [6] we should\nfollow-up in the future and eventually remove that warning.\n\n1. 0527ccb1b55 (add -i: default to the built-in implementation,\n   2021-11-30)\n2. f83dff60a78 (Start to implement a built-in version of `git add\n   --interactive`, 2019-11-13)\n3. 8a2cd3f5123 (stash: remove the stash.useBuiltin setting,\n   2020-03-03)\n4. d03ebd411c6 (rebase: remove the rebase.useBuiltin setting,\n   2019-03-18)\n5. deeaf5ee077 (stash: remove documentation for `stash.useBuiltin`,\n   2022-01-27)\n6. 9bcde4d5314 (rebase: remove transitory rebase.useBuiltin setting &\n   env, 2021-03-23)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n .gitignore                   |    1 -\n Documentation/config/add.txt |    7 +-\n Makefile                     |    1 -\n builtin/add.c                |   66 +-\n ci/run-build-and-tests.sh    |    1 -\n git-add--interactive.perl    | 1920 ----------------------------------\n t/README                     |    4 -\n t/t2016-checkout-patch.sh    |    6 -\n t/t3701-add-interactive.sh   |   29 +-\n t/t6132-pathspec-exclude.sh  |    6 +-\n t/test-lib.sh                |    4 -\n 11 files changed, 46 insertions(+), 1999 deletions(-)\n delete mode 100755 git-add--interactive.perl\n\ndiff --git a/.gitignore b/.gitignore\nindex 6782f3cecac..e875c590545 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -14,7 +14,6 @@\n /bin-wrappers/\n /git\n /git-add\n-/git-add--interactive\n /git-am\n /git-annotate\n /git-apply\ndiff --git a/Documentation/config/add.txt b/Documentation/config/add.txt\nindex 3e859f34197..e0354ceaed9 100644\n--- a/Documentation/config/add.txt\n+++ b/Documentation/config/add.txt\n@@ -7,6 +7,7 @@ add.ignore-errors (deprecated)::\n \tvariables.\n \n add.interactive.useBuiltin::\n-\tSet to `false` to fall back to the original Perl implementation of\n-\tthe interactive version of linkgit:git-add[1] instead of the built-in\n-\tversion. Is `true` by default.\n+\tUnused configuration variable. Used in Git versions v2.25.0 to\n+\tv2.36.0 to enable the built-in version of linkgit:git-add[1]'s\n+\tinteractive mode, which then became the default in Git\n+\tversions v2.37.0 to v2.39.0.\ndiff --git a/Makefile b/Makefile\nindex 45bd6ac9c3e..50ee51fde32 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -708,7 +708,6 @@ SCRIPT_LIB += git-mergetool--lib\n SCRIPT_LIB += git-sh-i18n\n SCRIPT_LIB += git-sh-setup\n \n-SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-archimport.perl\n SCRIPT_PERL += git-cvsexportcommit.perl\n SCRIPT_PERL += git-cvsimport.perl\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 0c604022676..9f5b6e26a02 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -241,55 +241,35 @@ static int refresh(int verbose, const struct pathspec *pathspec)\n int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tconst struct pathspec *pathspec)\n {\n-\tint i;\n-\tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\tint use_builtin_add_i =\n-\t\tgit_env_bool(\"GIT_TEST_ADD_I_USE_BUILTIN\", -1);\n-\n-\tif (use_builtin_add_i < 0 &&\n-\t    git_config_get_bool(\"add.interactive.usebuiltin\",\n-\t\t\t\t&use_builtin_add_i))\n-\t\tuse_builtin_add_i = 1;\n-\n-\tif (use_builtin_add_i != 0) {\n-\t\tenum add_p_mode mode;\n-\n-\t\tif (!patch_mode)\n-\t\t\treturn !!run_add_i(the_repository, pathspec);\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 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-\t\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n-\t}\n+\tenum add_p_mode mode;\n+\n+\tif (!patch_mode)\n+\t\treturn !!run_add_i(the_repository, pathspec);\n+\n+\tif (!strcmp(patch_mode, \"--patch\"))\n+\t\tmode = ADD_P_ADD;\n+\telse if (!strcmp(patch_mode, \"--patch=stash\"))\n+\t\tmode = ADD_P_STASH;\n+\telse if (!strcmp(patch_mode, \"--patch=reset\"))\n+\t\tmode = ADD_P_RESET;\n+\telse if (!strcmp(patch_mode, \"--patch=checkout\"))\n+\t\tmode = ADD_P_CHECKOUT;\n+\telse if (!strcmp(patch_mode, \"--patch=worktree\"))\n+\t\tmode = ADD_P_WORKTREE;\n+\telse\n+\t\tdie(\"'%s' not supported\", patch_mode);\n \n-\tstrvec_push(&cmd.args, \"add--interactive\");\n-\tif (patch_mode)\n-\t\tstrvec_push(&cmd.args, patch_mode);\n-\tif (revision)\n-\t\tstrvec_push(&cmd.args, revision);\n-\tstrvec_push(&cmd.args, \"--\");\n-\tfor (i = 0; i < pathspec->nr; i++)\n-\t\t/* pass original pathspec, to be re-parsed */\n-\t\tstrvec_push(&cmd.args, pathspec->items[i].original);\n-\n-\tcmd.git_cmd = 1;\n-\treturn run_command(&cmd);\n+\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n }\n \n int interactive_add(const char **argv, const char *prefix, int patch)\n {\n \tstruct pathspec pathspec;\n+\tint unused;\n+\n+\tif (!git_config_get_bool(\"add.interactive.usebuiltin\", &unused))\n+\t\twarning(_(\"the add.interactive.useBuiltin setting has been removed!\\n\"\n+\t\t\t  \"See its entry in 'git help config' for details.\"));\n \n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_FULL |\ndiff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\nindex 8ebff425967..b098e10f52a 100755\n--- a/ci/run-build-and-tests.sh\n+++ b/ci/run-build-and-tests.sh\n@@ -26,7 +26,6 @@ linux-TEST-vars)\n \texport GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1\n \texport GIT_TEST_MULTI_PACK_INDEX=1\n \texport GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=1\n-\texport GIT_TEST_ADD_I_USE_BUILTIN=0\n \texport GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master\n \texport GIT_TEST_WRITE_REV_INDEX=1\n \texport GIT_TEST_CHECKOUT_WORKERS=2\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\ndeleted file mode 100755\nindex 95887fd8e52..00000000000\n--- a/git-add--interactive.perl\n+++ /dev/null\n@@ -1,1920 +0,0 @@\n-#!/usr/bin/perl\n-\n-use 5.008;\n-use strict;\n-use warnings;\n-use Git qw(unquote_path);\n-use Git::I18N;\n-\n-binmode(STDOUT, \":raw\");\n-\n-my $repo = Git->repository();\n-\n-my $menu_use_color = $repo->get_colorbool('color.interactive');\n-my ($prompt_color, $header_color, $help_color) =\n-\t$menu_use_color ? (\n-\t\t$repo->get_color('color.interactive.prompt', 'bold blue'),\n-\t\t$repo->get_color('color.interactive.header', 'bold'),\n-\t\t$repo->get_color('color.interactive.help', 'red bold'),\n-\t) : ();\n-my $error_color = ();\n-if ($menu_use_color) {\n-\tmy $help_color_spec = ($repo->config('color.interactive.help') or\n-\t\t\t\t'red bold');\n-\t$error_color = $repo->get_color('color.interactive.error',\n-\t\t\t\t\t$help_color_spec);\n-}\n-\n-my $diff_use_color = $repo->get_colorbool('color.diff');\n-my ($fraginfo_color) =\n-\t$diff_use_color ? (\n-\t\t$repo->get_color('color.diff.frag', 'cyan'),\n-\t) : ();\n-my ($diff_context_color) =\n-\t$diff_use_color ? (\n-\t\t$repo->get_color($repo->config('color.diff.context') ? 'color.diff.context' : 'color.diff.plain', ''),\n-\t) : ();\n-my ($diff_old_color) =\n-\t$diff_use_color ? (\n-\t\t$repo->get_color('color.diff.old', 'red'),\n-\t) : ();\n-my ($diff_new_color) =\n-\t$diff_use_color ? (\n-\t\t$repo->get_color('color.diff.new', 'green'),\n-\t) : ();\n-\n-my $normal_color = $repo->get_color(\"\", \"reset\");\n-\n-my $diff_algorithm = $repo->config('diff.algorithm');\n-my $diff_filter = $repo->config('interactive.difffilter');\n-\n-my $use_readkey = 0;\n-my $use_termcap = 0;\n-my %term_escapes;\n-\n-sub ReadMode;\n-sub ReadKey;\n-if ($repo->config_bool(\"interactive.singlekey\")) {\n-\teval {\n-\t\trequire Term::ReadKey;\n-\t\tTerm::ReadKey->import;\n-\t\t$use_readkey = 1;\n-\t};\n-\tif (!$use_readkey) {\n-\t\tprint STDERR \"missing Term::ReadKey, disabling interactive.singlekey\\n\";\n-\t}\n-\teval {\n-\t\trequire Term::Cap;\n-\t\tmy $termcap = Term::Cap->Tgetent;\n-\t\tforeach (values %$termcap) {\n-\t\t\t$term_escapes{$_} = 1 if /^\\e/;\n-\t\t}\n-\t\t$use_termcap = 1;\n-\t};\n-}\n-\n-sub colored {\n-\tmy $color = shift;\n-\tmy $string = join(\"\", @_);\n-\n-\tif (defined $color) {\n-\t\t# Put a color code at the beginning of each line, a reset at the end\n-\t\t# color after newlines that are not at the end of the string\n-\t\t$string =~ s/(\\n+)(.)/$1$color$2/g;\n-\t\t# reset before newlines\n-\t\t$string =~ s/(\\n+)/$normal_color$1/g;\n-\t\t# codes at beginning and end (if necessary):\n-\t\t$string =~ s/^/$color/;\n-\t\t$string =~ s/$/$normal_color/ unless $string =~ /\\n$/;\n-\t}\n-\treturn $string;\n-}\n-\n-# command line options\n-my $patch_mode_only;\n-my $patch_mode;\n-my $patch_mode_revision;\n-\n-sub apply_patch;\n-sub apply_patch_for_checkout_commit;\n-sub apply_patch_for_stash;\n-\n-my %patch_modes = (\n-\t'stage' => {\n-\t\tDIFF => 'diff-files -p',\n-\t\tAPPLY => sub { apply_patch 'apply --cached', @_; },\n-\t\tAPPLY_CHECK => 'apply --cached',\n-\t\tFILTER => 'file-only',\n-\t\tIS_REVERSE => 0,\n-\t},\n-\t'stash' => {\n-\t\tDIFF => 'diff-index -p HEAD',\n-\t\tAPPLY => sub { apply_patch 'apply --cached', @_; },\n-\t\tAPPLY_CHECK => 'apply --cached',\n-\t\tFILTER => undef,\n-\t\tIS_REVERSE => 0,\n-\t},\n-\t'reset_head' => {\n-\t\tDIFF => 'diff-index -p --cached',\n-\t\tAPPLY => sub { apply_patch 'apply -R --cached', @_; },\n-\t\tAPPLY_CHECK => 'apply -R --cached',\n-\t\tFILTER => 'index-only',\n-\t\tIS_REVERSE => 1,\n-\t},\n-\t'reset_nothead' => {\n-\t\tDIFF => 'diff-index -R -p --cached',\n-\t\tAPPLY => sub { apply_patch 'apply --cached', @_; },\n-\t\tAPPLY_CHECK => 'apply --cached',\n-\t\tFILTER => 'index-only',\n-\t\tIS_REVERSE => 0,\n-\t},\n-\t'checkout_index' => {\n-\t\tDIFF => 'diff-files -p',\n-\t\tAPPLY => sub { apply_patch 'apply -R', @_; },\n-\t\tAPPLY_CHECK => 'apply -R',\n-\t\tFILTER => 'file-only',\n-\t\tIS_REVERSE => 1,\n-\t},\n-\t'checkout_head' => {\n-\t\tDIFF => 'diff-index -p',\n-\t\tAPPLY => sub { apply_patch_for_checkout_commit '-R', @_ },\n-\t\tAPPLY_CHECK => 'apply -R',\n-\t\tFILTER => undef,\n-\t\tIS_REVERSE => 1,\n-\t},\n-\t'checkout_nothead' => {\n-\t\tDIFF => 'diff-index -R -p',\n-\t\tAPPLY => sub { apply_patch_for_checkout_commit '', @_ },\n-\t\tAPPLY_CHECK => 'apply',\n-\t\tFILTER => undef,\n-\t\tIS_REVERSE => 0,\n-\t},\n-\t'worktree_head' => {\n-\t\tDIFF => 'diff-index -p',\n-\t\tAPPLY => sub { apply_patch 'apply -R', @_ },\n-\t\tAPPLY_CHECK => 'apply -R',\n-\t\tFILTER => undef,\n-\t\tIS_REVERSE => 1,\n-\t},\n-\t'worktree_nothead' => {\n-\t\tDIFF => 'diff-index -R -p',\n-\t\tAPPLY => sub { apply_patch 'apply', @_ },\n-\t\tAPPLY_CHECK => 'apply',\n-\t\tFILTER => undef,\n-\t\tIS_REVERSE => 0,\n-\t},\n-);\n-\n-$patch_mode = 'stage';\n-my %patch_mode_flavour = %{$patch_modes{$patch_mode}};\n-\n-sub run_cmd_pipe {\n-\tif ($^O eq 'MSWin32') {\n-\t\tmy @invalid = grep {m/[\":*]/} @_;\n-\t\tdie \"$^O does not support: @invalid\\n\" if @invalid;\n-\t\tmy @args = map { m/ /o ? \"\\\"$_\\\"\": $_ } @_;\n-\t\treturn qx{@args};\n-\t} else {\n-\t\tmy $fh = undef;\n-\t\topen($fh, '-|', @_) or die;\n-\t\tmy @out = <$fh>;\n-\t\tclose $fh || die \"Cannot close @_ ($!)\";\n-\t\treturn @out;\n-\t}\n-}\n-\n-my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));\n-\n-if (!defined $GIT_DIR) {\n-\texit(1); # rev-parse would have already said \"not a git repo\"\n-}\n-chomp($GIT_DIR);\n-\n-sub refresh {\n-\tmy $fh;\n-\topen $fh, 'git update-index --refresh |'\n-\t    or die;\n-\twhile (<$fh>) {\n-\t\t;# ignore 'needs update'\n-\t}\n-\tclose $fh;\n-}\n-\n-sub list_untracked {\n-\tmap {\n-\t\tchomp $_;\n-\t\tunquote_path($_);\n-\t}\n-\trun_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);\n-}\n-\n-# TRANSLATORS: you can adjust this to align \"git add -i\" status menu\n-my $status_fmt = __('%12s %12s %s');\n-my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), __('path'));\n-\n-{\n-\tmy $initial;\n-\tsub is_initial_commit {\n-\t\t$initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0\n-\t\t\tunless defined $initial;\n-\t\treturn $initial;\n-\t}\n-}\n-\n-{\n-\tmy $empty_tree;\n-\tsub get_empty_tree {\n-\t\treturn $empty_tree if defined $empty_tree;\n-\n-\t\t($empty_tree) = run_cmd_pipe(qw(git hash-object -t tree /dev/null));\n-\t\tchomp $empty_tree;\n-\t\treturn $empty_tree;\n-\t}\n-}\n-\n-sub get_diff_reference {\n-\tmy $ref = shift;\n-\tif (defined $ref and $ref ne 'HEAD') {\n-\t\treturn $ref;\n-\t} elsif (is_initial_commit()) {\n-\t\treturn get_empty_tree();\n-\t} else {\n-\t\treturn 'HEAD';\n-\t}\n-}\n-\n-# Returns list of hashes, contents of each of which are:\n-# VALUE:\tpathname\n-# BINARY:\tis a binary path\n-# INDEX:\tis index different from HEAD?\n-# FILE:\t\tis file different from index?\n-# INDEX_ADDDEL:\tis it add/delete between HEAD and index?\n-# FILE_ADDDEL:\tis it add/delete between index and file?\n-# UNMERGED:\tis the path unmerged\n-\n-sub list_modified {\n-\tmy ($only) = @_;\n-\tmy (%data, @return);\n-\tmy ($add, $del, $adddel, $file);\n-\n-\tmy $reference = get_diff_reference($patch_mode_revision);\n-\tfor (run_cmd_pipe(qw(git diff-index --cached\n-\t\t\t     --numstat --summary), $reference,\n-\t\t\t     '--', @ARGV)) {\n-\t\tif (($add, $del, $file) =\n-\t\t    /^([-\\d]+)\t([-\\d]+)\t(.*)/) {\n-\t\t\tmy ($change, $bin);\n-\t\t\t$file = unquote_path($file);\n-\t\t\tif ($add eq '-' && $del eq '-') {\n-\t\t\t\t$change = __('binary');\n-\t\t\t\t$bin = 1;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$change = \"+$add/-$del\";\n-\t\t\t}\n-\t\t\t$data{$file} = {\n-\t\t\t\tINDEX => $change,\n-\t\t\t\tBINARY => $bin,\n-\t\t\t\tFILE => __('nothing'),\n-\t\t\t}\n-\t\t}\n-\t\telsif (($adddel, $file) =\n-\t\t       /^ (create|delete) mode [0-7]+ (.*)$/) {\n-\t\t\t$file = unquote_path($file);\n-\t\t\t$data{$file}{INDEX_ADDDEL} = $adddel;\n-\t\t}\n-\t}\n-\n-\tfor (run_cmd_pipe(qw(git diff-files --ignore-submodules=dirty --numstat --summary --raw --), @ARGV)) {\n-\t\tif (($add, $del, $file) =\n-\t\t    /^([-\\d]+)\t([-\\d]+)\t(.*)/) {\n-\t\t\t$file = unquote_path($file);\n-\t\t\tmy ($change, $bin);\n-\t\t\tif ($add eq '-' && $del eq '-') {\n-\t\t\t\t$change = __('binary');\n-\t\t\t\t$bin = 1;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$change = \"+$add/-$del\";\n-\t\t\t}\n-\t\t\t$data{$file}{FILE} = $change;\n-\t\t\tif ($bin) {\n-\t\t\t\t$data{$file}{BINARY} = 1;\n-\t\t\t}\n-\t\t}\n-\t\telsif (($adddel, $file) =\n-\t\t       /^ (create|delete) mode [0-7]+ (.*)$/) {\n-\t\t\t$file = unquote_path($file);\n-\t\t\t$data{$file}{FILE_ADDDEL} = $adddel;\n-\t\t}\n-\t\telsif (/^:[0-7]+ [0-7]+ [0-9a-f]+ [0-9a-f]+ (.)\t(.*)$/) {\n-\t\t\t$file = unquote_path($2);\n-\t\t\tif (!exists $data{$file}) {\n-\t\t\t\t$data{$file} = +{\n-\t\t\t\t\tINDEX => __('unchanged'),\n-\t\t\t\t\tBINARY => 0,\n-\t\t\t\t};\n-\t\t\t}\n-\t\t\tif ($1 eq 'U') {\n-\t\t\t\t$data{$file}{UNMERGED} = 1;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\tfor (sort keys %data) {\n-\t\tmy $it = $data{$_};\n-\n-\t\tif ($only) {\n-\t\t\tif ($only eq 'index-only') {\n-\t\t\t\tnext if ($it->{INDEX} eq __('unchanged'));\n-\t\t\t}\n-\t\t\tif ($only eq 'file-only') {\n-\t\t\t\tnext if ($it->{FILE} eq __('nothing'));\n-\t\t\t}\n-\t\t}\n-\t\tpush @return, +{\n-\t\t\tVALUE => $_,\n-\t\t\t%$it,\n-\t\t};\n-\t}\n-\treturn @return;\n-}\n-\n-sub find_unique {\n-\tmy ($string, @stuff) = @_;\n-\tmy $found = undef;\n-\tfor (my $i = 0; $i < @stuff; $i++) {\n-\t\tmy $it = $stuff[$i];\n-\t\tmy $hit = undef;\n-\t\tif (ref $it) {\n-\t\t\tif ((ref $it) eq 'ARRAY') {\n-\t\t\t\t$it = $it->[0];\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$it = $it->{VALUE};\n-\t\t\t}\n-\t\t}\n-\t\teval {\n-\t\t\tif ($it =~ /^$string/) {\n-\t\t\t\t$hit = 1;\n-\t\t\t};\n-\t\t};\n-\t\tif (defined $hit && defined $found) {\n-\t\t\treturn undef;\n-\t\t}\n-\t\tif ($hit) {\n-\t\t\t$found = $i + 1;\n-\t\t}\n-\t}\n-\treturn $found;\n-}\n-\n-# inserts string into trie and updates count for each character\n-sub update_trie {\n-\tmy ($trie, $string) = @_;\n-\tforeach (split //, $string) {\n-\t\t$trie = $trie->{$_} ||= {COUNT => 0};\n-\t\t$trie->{COUNT}++;\n-\t}\n-}\n-\n-# returns an array of tuples (prefix, remainder)\n-sub find_unique_prefixes {\n-\tmy @stuff = @_;\n-\tmy @return = ();\n-\n-\t# any single prefix exceeding the soft limit is omitted\n-\t# if any prefix exceeds the hard limit all are omitted\n-\t# 0 indicates no limit\n-\tmy $soft_limit = 0;\n-\tmy $hard_limit = 3;\n-\n-\t# build a trie modelling all possible options\n-\tmy %trie;\n-\tforeach my $print (@stuff) {\n-\t\tif ((ref $print) eq 'ARRAY') {\n-\t\t\t$print = $print->[0];\n-\t\t}\n-\t\telsif ((ref $print) eq 'HASH') {\n-\t\t\t$print = $print->{VALUE};\n-\t\t}\n-\t\tupdate_trie(\\%trie, $print);\n-\t\tpush @return, $print;\n-\t}\n-\n-\t# use the trie to find the unique prefixes\n-\tfor (my $i = 0; $i < @return; $i++) {\n-\t\tmy $ret = $return[$i];\n-\t\tmy @letters = split //, $ret;\n-\t\tmy %search = %trie;\n-\t\tmy ($prefix, $remainder);\n-\t\tmy $j;\n-\t\tfor ($j = 0; $j < @letters; $j++) {\n-\t\t\tmy $letter = $letters[$j];\n-\t\t\tif ($search{$letter}{COUNT} == 1) {\n-\t\t\t\t$prefix = substr $ret, 0, $j + 1;\n-\t\t\t\t$remainder = substr $ret, $j + 1;\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\tmy $prefix = substr $ret, 0, $j;\n-\t\t\t\treturn ()\n-\t\t\t\t    if ($hard_limit && $j + 1 > $hard_limit);\n-\t\t\t}\n-\t\t\t%search = %{$search{$letter}};\n-\t\t}\n-\t\tif (ord($letters[0]) > 127 ||\n-\t\t    ($soft_limit && $j + 1 > $soft_limit)) {\n-\t\t\t$prefix = undef;\n-\t\t\t$remainder = $ret;\n-\t\t}\n-\t\t$return[$i] = [$prefix, $remainder];\n-\t}\n-\treturn @return;\n-}\n-\n-# filters out prefixes which have special meaning to list_and_choose()\n-sub is_valid_prefix {\n-\tmy $prefix = shift;\n-\treturn (defined $prefix) &&\n-\t    !($prefix =~ /[\\s,]/) && # separators\n-\t    !($prefix =~ /^-/) &&    # deselection\n-\t    !($prefix =~ /^\\d+/) &&  # selection\n-\t    ($prefix ne '*') &&      # \"all\" wildcard\n-\t    ($prefix ne '?');        # prompt help\n-}\n-\n-# given a prefix/remainder tuple return a string with the prefix highlighted\n-# for now use square brackets; later might use ANSI colors (underline, bold)\n-sub highlight_prefix {\n-\tmy $prefix = shift;\n-\tmy $remainder = shift;\n-\n-\tif (!defined $prefix) {\n-\t\treturn $remainder;\n-\t}\n-\n-\tif (!is_valid_prefix($prefix)) {\n-\t\treturn \"$prefix$remainder\";\n-\t}\n-\n-\tif (!$menu_use_color) {\n-\t\treturn \"[$prefix]$remainder\";\n-\t}\n-\n-\treturn \"$prompt_color$prefix$normal_color$remainder\";\n-}\n-\n-sub error_msg {\n-\tprint STDERR colored $error_color, @_;\n-}\n-\n-sub list_and_choose {\n-\tmy ($opts, @stuff) = @_;\n-\tmy (@chosen, @return);\n-\tif (!@stuff) {\n-\t    return @return;\n-\t}\n-\tmy $i;\n-\tmy @prefixes = find_unique_prefixes(@stuff) unless $opts->{LIST_ONLY};\n-\n-      TOPLOOP:\n-\twhile (1) {\n-\t\tmy $last_lf = 0;\n-\n-\t\tif ($opts->{HEADER}) {\n-\t\t\tmy $indent = $opts->{LIST_FLAT} ? \"\" : \"     \";\n-\t\t\tprint colored $header_color, \"$indent$opts->{HEADER}\\n\";\n-\t\t}\n-\t\tfor ($i = 0; $i < @stuff; $i++) {\n-\t\t\tmy $chosen = $chosen[$i] ? '*' : ' ';\n-\t\t\tmy $print = $stuff[$i];\n-\t\t\tmy $ref = ref $print;\n-\t\t\tmy $highlighted = highlight_prefix(@{$prefixes[$i]})\n-\t\t\t    if @prefixes;\n-\t\t\tif ($ref eq 'ARRAY') {\n-\t\t\t\t$print = $highlighted || $print->[0];\n-\t\t\t}\n-\t\t\telsif ($ref eq 'HASH') {\n-\t\t\t\tmy $value = $highlighted || $print->{VALUE};\n-\t\t\t\t$print = sprintf($status_fmt,\n-\t\t\t\t    $print->{INDEX},\n-\t\t\t\t    $print->{FILE},\n-\t\t\t\t    $value);\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$print = $highlighted || $print;\n-\t\t\t}\n-\t\t\tprintf(\"%s%2d: %s\", $chosen, $i+1, $print);\n-\t\t\tif (($opts->{LIST_FLAT}) &&\n-\t\t\t    (($i + 1) % ($opts->{LIST_FLAT}))) {\n-\t\t\t\tprint \"\\t\";\n-\t\t\t\t$last_lf = 0;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\tprint \"\\n\";\n-\t\t\t\t$last_lf = 1;\n-\t\t\t}\n-\t\t}\n-\t\tif (!$last_lf) {\n-\t\t\tprint \"\\n\";\n-\t\t}\n-\n-\t\treturn if ($opts->{LIST_ONLY});\n-\n-\t\tprint colored $prompt_color, $opts->{PROMPT};\n-\t\tif ($opts->{SINGLETON}) {\n-\t\t\tprint \"> \";\n-\t\t}\n-\t\telse {\n-\t\t\tprint \">> \";\n-\t\t}\n-\t\tmy $line = <STDIN>;\n-\t\tif (!$line) {\n-\t\t\tprint \"\\n\";\n-\t\t\t$opts->{ON_EOF}->() if $opts->{ON_EOF};\n-\t\t\tlast;\n-\t\t}\n-\t\tchomp $line;\n-\t\tlast if $line eq '';\n-\t\tif ($line eq '?') {\n-\t\t\t$opts->{SINGLETON} ?\n-\t\t\t    singleton_prompt_help_cmd() :\n-\t\t\t    prompt_help_cmd();\n-\t\t\tnext TOPLOOP;\n-\t\t}\n-\t\tfor my $choice (split(/[\\s,]+/, $line)) {\n-\t\t\tmy $choose = 1;\n-\t\t\tmy ($bottom, $top);\n-\n-\t\t\t# Input that begins with '-'; unchoose\n-\t\t\tif ($choice =~ s/^-//) {\n-\t\t\t\t$choose = 0;\n-\t\t\t}\n-\t\t\t# A range can be specified like 5-7 or 5-.\n-\t\t\tif ($choice =~ /^(\\d+)-(\\d*)$/) {\n-\t\t\t\t($bottom, $top) = ($1, length($2) ? $2 : 1 + @stuff);\n-\t\t\t}\n-\t\t\telsif ($choice =~ /^\\d+$/) {\n-\t\t\t\t$bottom = $top = $choice;\n-\t\t\t}\n-\t\t\telsif ($choice eq '*') {\n-\t\t\t\t$bottom = 1;\n-\t\t\t\t$top = 1 + @stuff;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$bottom = $top = find_unique($choice, @stuff);\n-\t\t\t\tif (!defined $bottom) {\n-\t\t\t\t\terror_msg sprintf(__(\"Huh (%s)?\\n\"), $choice);\n-\t\t\t\t\tnext TOPLOOP;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tif ($opts->{SINGLETON} && $bottom != $top) {\n-\t\t\t\terror_msg sprintf(__(\"Huh (%s)?\\n\"), $choice);\n-\t\t\t\tnext TOPLOOP;\n-\t\t\t}\n-\t\t\tfor ($i = $bottom-1; $i <= $top-1; $i++) {\n-\t\t\t\tnext if (@stuff <= $i || $i < 0);\n-\t\t\t\t$chosen[$i] = $choose;\n-\t\t\t}\n-\t\t}\n-\t\tlast if ($opts->{IMMEDIATE} || $line eq '*');\n-\t}\n-\tfor ($i = 0; $i < @stuff; $i++) {\n-\t\tif ($chosen[$i]) {\n-\t\t\tpush @return, $stuff[$i];\n-\t\t}\n-\t}\n-\treturn @return;\n-}\n-\n-sub singleton_prompt_help_cmd {\n-\tprint colored $help_color, __ <<'EOF' ;\n-Prompt help:\n-1          - select a numbered item\n-foo        - select item based on unique prefix\n-           - (empty) select nothing\n-EOF\n-}\n-\n-sub prompt_help_cmd {\n-\tprint colored $help_color, __ <<'EOF' ;\n-Prompt help:\n-1          - select a single item\n-3-5        - select a range of items\n-2-3,6-9    - select multiple ranges\n-foo        - select item based on unique prefix\n--...       - unselect specified items\n-*          - choose all items\n-           - (empty) finish selecting\n-EOF\n-}\n-\n-sub status_cmd {\n-\tlist_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n-\t\t\tlist_modified());\n-\tprint \"\\n\";\n-}\n-\n-sub say_n_paths {\n-\tmy $did = shift @_;\n-\tmy $cnt = scalar @_;\n-\tif ($did eq 'added') {\n-\t\tprintf(__n(\"added %d path\\n\", \"added %d paths\\n\",\n-\t\t\t   $cnt), $cnt);\n-\t} elsif ($did eq 'updated') {\n-\t\tprintf(__n(\"updated %d path\\n\", \"updated %d paths\\n\",\n-\t\t\t   $cnt), $cnt);\n-\t} elsif ($did eq 'reverted') {\n-\t\tprintf(__n(\"reverted %d path\\n\", \"reverted %d paths\\n\",\n-\t\t\t   $cnt), $cnt);\n-\t} else {\n-\t\tprintf(__n(\"touched %d path\\n\", \"touched %d paths\\n\",\n-\t\t\t   $cnt), $cnt);\n-\t}\n-}\n-\n-sub update_cmd {\n-\tmy @mods = list_modified('file-only');\n-\treturn if (!@mods);\n-\n-\tmy @update = list_and_choose({ PROMPT => __('Update'),\n-\t\t\t\t       HEADER => $status_head, },\n-\t\t\t\t     @mods);\n-\tif (@update) {\n-\t\tsystem(qw(git update-index --add --remove --),\n-\t\t       map { $_->{VALUE} } @update);\n-\t\tsay_n_paths('updated', @update);\n-\t}\n-\tprint \"\\n\";\n-}\n-\n-sub revert_cmd {\n-\tmy @update = list_and_choose({ PROMPT => __('Revert'),\n-\t\t\t\t       HEADER => $status_head, },\n-\t\t\t\t     list_modified());\n-\tif (@update) {\n-\t\tif (is_initial_commit()) {\n-\t\t\tsystem(qw(git rm --cached),\n-\t\t\t\tmap { $_->{VALUE} } @update);\n-\t\t}\n-\t\telse {\n-\t\t\tmy @lines = run_cmd_pipe(qw(git ls-tree HEAD --),\n-\t\t\t\t\t\t map { $_->{VALUE} } @update);\n-\t\t\tmy $fh;\n-\t\t\topen $fh, '| git update-index --index-info'\n-\t\t\t    or die;\n-\t\t\tfor (@lines) {\n-\t\t\t\tprint $fh $_;\n-\t\t\t}\n-\t\t\tclose($fh);\n-\t\t\tfor (@update) {\n-\t\t\t\tif ($_->{INDEX_ADDDEL} &&\n-\t\t\t\t    $_->{INDEX_ADDDEL} eq 'create') {\n-\t\t\t\t\tsystem(qw(git update-index --force-remove --),\n-\t\t\t\t\t       $_->{VALUE});\n-\t\t\t\t\tprintf(__(\"note: %s is untracked now.\\n\"), $_->{VALUE});\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\t\trefresh();\n-\t\tsay_n_paths('reverted', @update);\n-\t}\n-\tprint \"\\n\";\n-}\n-\n-sub add_untracked_cmd {\n-\tmy @add = list_and_choose({ PROMPT => __('Add untracked') },\n-\t\t\t\t  list_untracked());\n-\tif (@add) {\n-\t\tsystem(qw(git update-index --add --), @add);\n-\t\tsay_n_paths('added', @add);\n-\t} else {\n-\t\tprint __(\"No untracked files.\\n\");\n-\t}\n-\tprint \"\\n\";\n-}\n-\n-sub run_git_apply {\n-\tmy $cmd = shift;\n-\tmy $fh;\n-\topen $fh, '| git ' . $cmd . \" --allow-overlap\";\n-\tprint $fh @_;\n-\treturn close $fh;\n-}\n-\n-sub parse_diff {\n-\tmy ($path) = @_;\n-\tmy @diff_cmd = split(\" \", $patch_mode_flavour{DIFF});\n-\tif (defined $diff_algorithm) {\n-\t\tsplice @diff_cmd, 1, 0, \"--diff-algorithm=${diff_algorithm}\";\n-\t}\n-\tif (defined $patch_mode_revision) {\n-\t\tpush @diff_cmd, get_diff_reference($patch_mode_revision);\n-\t}\n-\tmy @diff = run_cmd_pipe(\"git\", @diff_cmd, qw(--no-color --), $path);\n-\tmy @colored = ();\n-\tif ($diff_use_color) {\n-\t\tmy @display_cmd = (\"git\", @diff_cmd, qw(--color --), $path);\n-\t\tif (defined $diff_filter) {\n-\t\t\t# quotemeta is overkill, but sufficient for shell-quoting\n-\t\t\tmy $diff = join(' ', map { quotemeta } @display_cmd);\n-\t\t\t@display_cmd = (\"$diff | $diff_filter\");\n-\t\t}\n-\n-\t\t@colored = run_cmd_pipe(@display_cmd);\n-\t}\n-\tmy (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };\n-\n-\tif (@colored && @colored != @diff) {\n-\t\tprint STDERR\n-\t\t  \"fatal: mismatched output from interactive.diffFilter\\n\",\n-\t\t  \"hint: Your filter must maintain a one-to-one correspondence\\n\",\n-\t\t  \"hint: between its input and output lines.\\n\";\n-\t\texit 1;\n-\t}\n-\n-\tfor (my $i = 0; $i < @diff; $i++) {\n-\t\tif ($diff[$i] =~ /^@@ /) {\n-\t\t\tpush @hunk, { TEXT => [], DISPLAY => [],\n-\t\t\t\tTYPE => 'hunk' };\n-\t\t}\n-\t\tpush @{$hunk[-1]{TEXT}}, $diff[$i];\n-\t\tpush @{$hunk[-1]{DISPLAY}},\n-\t\t\t(@colored ? $colored[$i] : $diff[$i]);\n-\t}\n-\treturn @hunk;\n-}\n-\n-sub parse_diff_header {\n-\tmy $src = shift;\n-\n-\tmy $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };\n-\tmy $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };\n-\tmy $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };\n-\tmy $addition;\n-\n-\tfor (my $i = 0; $i < @{$src->{TEXT}}; $i++) {\n-\t\tif ($src->{TEXT}->[$i] =~ /^new file/) {\n-\t\t\t$addition = 1;\n-\t\t\t$head->{TYPE} = 'addition';\n-\t\t}\n-\t\tmy $dest =\n-\t\t   $src->{TEXT}->[$i] =~ /^(old|new) mode (\\d+)$/ ? $mode :\n-\t\t   $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :\n-\t\t   $head;\n-\t\tpush @{$dest->{TEXT}}, $src->{TEXT}->[$i];\n-\t\tpush @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];\n-\t}\n-\treturn ($head, $mode, $deletion, $addition);\n-}\n-\n-sub hunk_splittable {\n-\tmy ($text) = @_;\n-\n-\tmy @s = split_hunk($text);\n-\treturn (1 < @s);\n-}\n-\n-sub parse_hunk_header {\n-\tmy ($line) = @_;\n-\tmy ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =\n-\t    $line =~ /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@/;\n-\t$o_cnt = 1 unless defined $o_cnt;\n-\t$n_cnt = 1 unless defined $n_cnt;\n-\treturn ($o_ofs, $o_cnt, $n_ofs, $n_cnt);\n-}\n-\n-sub format_hunk_header {\n-\tmy ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = @_;\n-\treturn (\"@@ -$o_ofs\" .\n-\t\t(($o_cnt != 1) ? \",$o_cnt\" : '') .\n-\t\t\" +$n_ofs\" .\n-\t\t(($n_cnt != 1) ? \",$n_cnt\" : '') .\n-\t\t\" @@\\n\");\n-}\n-\n-sub split_hunk {\n-\tmy ($text, $display) = @_;\n-\tmy @split = ();\n-\tif (!defined $display) {\n-\t\t$display = $text;\n-\t}\n-\t# If there are context lines in the middle of a hunk,\n-\t# it can be split, but we would need to take care of\n-\t# overlaps later.\n-\n-\tmy ($o_ofs, undef, $n_ofs) = parse_hunk_header($text->[0]);\n-\tmy $hunk_start = 1;\n-\n-      OUTER:\n-\twhile (1) {\n-\t\tmy $next_hunk_start = undef;\n-\t\tmy $i = $hunk_start - 1;\n-\t\tmy $this = +{\n-\t\t\tTEXT => [],\n-\t\t\tDISPLAY => [],\n-\t\t\tTYPE => 'hunk',\n-\t\t\tOLD => $o_ofs,\n-\t\t\tNEW => $n_ofs,\n-\t\t\tOCNT => 0,\n-\t\t\tNCNT => 0,\n-\t\t\tADDDEL => 0,\n-\t\t\tPOSTCTX => 0,\n-\t\t\tUSE => undef,\n-\t\t};\n-\n-\t\twhile (++$i < @$text) {\n-\t\t\tmy $line = $text->[$i];\n-\t\t\tmy $display = $display->[$i];\n-\t\t\tif ($line =~ /^\\\\/) {\n-\t\t\t\tpush @{$this->{TEXT}}, $line;\n-\t\t\t\tpush @{$this->{DISPLAY}}, $display;\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\tif ($line =~ /^ /) {\n-\t\t\t\tif ($this->{ADDDEL} &&\n-\t\t\t\t    !defined $next_hunk_start) {\n-\t\t\t\t\t# We have seen leading context and\n-\t\t\t\t\t# adds/dels and then here is another\n-\t\t\t\t\t# context, which is trailing for this\n-\t\t\t\t\t# split hunk and leading for the next\n-\t\t\t\t\t# one.\n-\t\t\t\t\t$next_hunk_start = $i;\n-\t\t\t\t}\n-\t\t\t\tpush @{$this->{TEXT}}, $line;\n-\t\t\t\tpush @{$this->{DISPLAY}}, $display;\n-\t\t\t\t$this->{OCNT}++;\n-\t\t\t\t$this->{NCNT}++;\n-\t\t\t\tif (defined $next_hunk_start) {\n-\t\t\t\t\t$this->{POSTCTX}++;\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\n-\t\t\t# add/del\n-\t\t\tif (defined $next_hunk_start) {\n-\t\t\t\t# We are done with the current hunk and\n-\t\t\t\t# this is the first real change for the\n-\t\t\t\t# next split one.\n-\t\t\t\t$hunk_start = $next_hunk_start;\n-\t\t\t\t$o_ofs = $this->{OLD} + $this->{OCNT};\n-\t\t\t\t$n_ofs = $this->{NEW} + $this->{NCNT};\n-\t\t\t\t$o_ofs -= $this->{POSTCTX};\n-\t\t\t\t$n_ofs -= $this->{POSTCTX};\n-\t\t\t\tpush @split, $this;\n-\t\t\t\tredo OUTER;\n-\t\t\t}\n-\t\t\tpush @{$this->{TEXT}}, $line;\n-\t\t\tpush @{$this->{DISPLAY}}, $display;\n-\t\t\t$this->{ADDDEL}++;\n-\t\t\tif ($line =~ /^-/) {\n-\t\t\t\t$this->{OCNT}++;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$this->{NCNT}++;\n-\t\t\t}\n-\t\t}\n-\n-\t\tpush @split, $this;\n-\t\tlast;\n-\t}\n-\n-\tfor my $hunk (@split) {\n-\t\t$o_ofs = $hunk->{OLD};\n-\t\t$n_ofs = $hunk->{NEW};\n-\t\tmy $o_cnt = $hunk->{OCNT};\n-\t\tmy $n_cnt = $hunk->{NCNT};\n-\n-\t\tmy $head = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);\n-\t\tmy $display_head = $head;\n-\t\tunshift @{$hunk->{TEXT}}, $head;\n-\t\tif ($diff_use_color) {\n-\t\t\t$display_head = colored($fraginfo_color, $head);\n-\t\t}\n-\t\tunshift @{$hunk->{DISPLAY}}, $display_head;\n-\t}\n-\treturn @split;\n-}\n-\n-sub find_last_o_ctx {\n-\tmy ($it) = @_;\n-\tmy $text = $it->{TEXT};\n-\tmy ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]);\n-\tmy $i = @{$text};\n-\tmy $last_o_ctx = $o_ofs + $o_cnt;\n-\twhile (0 < --$i) {\n-\t\tmy $line = $text->[$i];\n-\t\tif ($line =~ /^ /) {\n-\t\t\t$last_o_ctx--;\n-\t\t\tnext;\n-\t\t}\n-\t\tlast;\n-\t}\n-\treturn $last_o_ctx;\n-}\n-\n-sub merge_hunk {\n-\tmy ($prev, $this) = @_;\n-\tmy ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) =\n-\t    parse_hunk_header($prev->{TEXT}[0]);\n-\tmy ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) =\n-\t    parse_hunk_header($this->{TEXT}[0]);\n-\n-\tmy (@line, $i, $ofs, $o_cnt, $n_cnt);\n-\t$ofs = $o0_ofs;\n-\t$o_cnt = $n_cnt = 0;\n-\tfor ($i = 1; $i < @{$prev->{TEXT}}; $i++) {\n-\t\tmy $line = $prev->{TEXT}[$i];\n-\t\tif ($line =~ /^\\+/) {\n-\t\t\t$n_cnt++;\n-\t\t\tpush @line, $line;\n-\t\t\tnext;\n-\t\t} elsif ($line =~ /^\\\\/) {\n-\t\t\tpush @line, $line;\n-\t\t\tnext;\n-\t\t}\n-\n-\t\tlast if ($o1_ofs <= $ofs);\n-\n-\t\t$o_cnt++;\n-\t\t$ofs++;\n-\t\tif ($line =~ /^ /) {\n-\t\t\t$n_cnt++;\n-\t\t}\n-\t\tpush @line, $line;\n-\t}\n-\n-\tfor ($i = 1; $i < @{$this->{TEXT}}; $i++) {\n-\t\tmy $line = $this->{TEXT}[$i];\n-\t\tif ($line =~ /^\\+/) {\n-\t\t\t$n_cnt++;\n-\t\t\tpush @line, $line;\n-\t\t\tnext;\n-\t\t} elsif ($line =~ /^\\\\/) {\n-\t\t\tpush @line, $line;\n-\t\t\tnext;\n-\t\t}\n-\t\t$ofs++;\n-\t\t$o_cnt++;\n-\t\tif ($line =~ /^ /) {\n-\t\t\t$n_cnt++;\n-\t\t}\n-\t\tpush @line, $line;\n-\t}\n-\tmy $head = format_hunk_header($o0_ofs, $o_cnt, $n0_ofs, $n_cnt);\n-\t@{$prev->{TEXT}} = ($head, @line);\n-}\n-\n-sub coalesce_overlapping_hunks {\n-\tmy (@in) = @_;\n-\tmy @out = ();\n-\n-\tmy ($last_o_ctx, $last_was_dirty);\n-\tmy $ofs_delta = 0;\n-\n-\tfor (@in) {\n-\t\tif ($_->{TYPE} ne 'hunk') {\n-\t\t\tpush @out, $_;\n-\t\t\tnext;\n-\t\t}\n-\t\tmy $text = $_->{TEXT};\n-\t\tmy ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =\n-\t\t\t\t\t\tparse_hunk_header($text->[0]);\n-\t\tunless ($_->{USE}) {\n-\t\t\t$ofs_delta += $o_cnt - $n_cnt;\n-\t\t\t# If this hunk has been edited then subtract\n-\t\t\t# the delta that is due to the edit.\n-\t\t\tif ($_->{OFS_DELTA}) {\n-\t\t\t\t$ofs_delta -= $_->{OFS_DELTA};\n-\t\t\t}\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($ofs_delta) {\n-\t\t\tif ($patch_mode_flavour{IS_REVERSE}) {\n-\t\t\t\t$o_ofs -= $ofs_delta;\n-\t\t\t} else {\n-\t\t\t\t$n_ofs += $ofs_delta;\n-\t\t\t}\n-\t\t\t$_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,\n-\t\t\t\t\t\t\t     $n_ofs, $n_cnt);\n-\t\t}\n-\t\t# If this hunk was edited then adjust the offset delta\n-\t\t# to reflect the edit.\n-\t\tif ($_->{OFS_DELTA}) {\n-\t\t\t$ofs_delta += $_->{OFS_DELTA};\n-\t\t}\n-\t\tif (defined $last_o_ctx &&\n-\t\t    $o_ofs <= $last_o_ctx &&\n-\t\t    !$_->{DIRTY} &&\n-\t\t    !$last_was_dirty) {\n-\t\t\tmerge_hunk($out[-1], $_);\n-\t\t}\n-\t\telse {\n-\t\t\tpush @out, $_;\n-\t\t}\n-\t\t$last_o_ctx = find_last_o_ctx($out[-1]);\n-\t\t$last_was_dirty = $_->{DIRTY};\n-\t}\n-\treturn @out;\n-}\n-\n-sub reassemble_patch {\n-\tmy $head = shift;\n-\tmy @patch;\n-\n-\t# Include everything in the header except the beginning of the diff.\n-\tpush @patch, (grep { !/^[-+]{3}/ } @$head);\n-\n-\t# Then include any headers from the hunk lines, which must\n-\t# come before any actual hunk.\n-\twhile (@_ && $_[0] !~ /^@/) {\n-\t\tpush @patch, shift;\n-\t}\n-\n-\t# Then begin the diff.\n-\tpush @patch, grep { /^[-+]{3}/ } @$head;\n-\n-\t# And then the actual hunks.\n-\tpush @patch, @_;\n-\n-\treturn @patch;\n-}\n-\n-sub color_diff {\n-\treturn map {\n-\t\tcolored((/^@/  ? $fraginfo_color :\n-\t\t\t /^\\+/ ? $diff_new_color :\n-\t\t\t /^-/  ? $diff_old_color :\n-\t\t\t $diff_context_color),\n-\t\t\t$_);\n-\t} @_;\n-}\n-\n-my %edit_hunk_manually_modes = (\n-\tstage => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for staging.\"),\n-\tstash => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for stashing.\"),\n-\treset_head => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for unstaging.\"),\n-\treset_nothead => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for applying.\"),\n-\tcheckout_index => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for discarding.\"),\n-\tcheckout_head => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for discarding.\"),\n-\tcheckout_nothead => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for applying.\"),\n-\tworktree_head => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for discarding.\"),\n-\tworktree_nothead => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for applying.\"),\n-);\n-\n-sub recount_edited_hunk {\n-\tlocal $_;\n-\tmy ($oldtext, $newtext) = @_;\n-\tmy ($o_cnt, $n_cnt) = (0, 0);\n-\tfor (@{$newtext}[1..$#{$newtext}]) {\n-\t\tmy $mode = substr($_, 0, 1);\n-\t\tif ($mode eq '-') {\n-\t\t\t$o_cnt++;\n-\t\t} elsif ($mode eq '+') {\n-\t\t\t$n_cnt++;\n-\t\t} elsif ($mode eq ' ' or $mode eq \"\\n\") {\n-\t\t\t$o_cnt++;\n-\t\t\t$n_cnt++;\n-\t\t}\n-\t}\n-\tmy ($o_ofs, undef, $n_ofs, undef) =\n-\t\t\t\t\tparse_hunk_header($newtext->[0]);\n-\t$newtext->[0] = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);\n-\tmy (undef, $orig_o_cnt, undef, $orig_n_cnt) =\n-\t\t\t\t\tparse_hunk_header($oldtext->[0]);\n-\t# Return the change in the number of lines inserted by this hunk\n-\treturn $orig_o_cnt - $orig_n_cnt - $o_cnt + $n_cnt;\n-}\n-\n-sub edit_hunk_manually {\n-\tmy ($oldtext) = @_;\n-\n-\tmy $hunkfile = $repo->repo_path . \"/addp-hunk-edit.diff\";\n-\tmy $fh;\n-\topen $fh, '>', $hunkfile\n-\t\tor die sprintf(__(\"failed to open hunk edit file for writing: %s\"), $!);\n-\tprint $fh Git::comment_lines __(\"Manual hunk edit mode -- see bottom for a quick guide.\\n\");\n-\tprint $fh @$oldtext;\n-\tmy $is_reverse = $patch_mode_flavour{IS_REVERSE};\n-\tmy ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', '-');\n-\tmy $comment_line_char = Git::get_comment_line_char;\n-\tprint $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, $remove_plus, $comment_line_char),\n----\n-To remove '%s' lines, make them ' ' lines (context).\n-To remove '%s' lines, delete them.\n-Lines starting with %s will be removed.\n-EOF\n-__($edit_hunk_manually_modes{$patch_mode}),\n-# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.\n-__ <<EOF2 ;\n-If it does not apply cleanly, you will be given an opportunity to\n-edit again.  If all lines of the hunk are removed, then the edit is\n-aborted and the hunk is left unchanged.\n-EOF2\n-\tclose $fh;\n-\n-\tchomp(my ($editor) = run_cmd_pipe(qw(git var GIT_EDITOR)));\n-\tsystem('sh', '-c', $editor.' \"$@\"', $editor, $hunkfile);\n-\n-\tif ($? != 0) {\n-\t\treturn undef;\n-\t}\n-\n-\topen $fh, '<', $hunkfile\n-\t\tor die sprintf(__(\"failed to open hunk edit file for reading: %s\"), $!);\n-\tmy @newtext = grep { !/^\\Q$comment_line_char\\E/ } <$fh>;\n-\tclose $fh;\n-\tunlink $hunkfile;\n-\n-\t# Abort if nothing remains\n-\tif (!grep { /\\S/ } @newtext) {\n-\t\treturn undef;\n-\t}\n-\n-\t# Reinsert the first hunk header if the user accidentally deleted it\n-\tif ($newtext[0] !~ /^@/) {\n-\t\tunshift @newtext, $oldtext->[0];\n-\t}\n-\treturn \\@newtext;\n-}\n-\n-sub diff_applies {\n-\treturn run_git_apply($patch_mode_flavour{APPLY_CHECK} . ' --check',\n-\t\t\t     map { @{$_->{TEXT}} } @_);\n-}\n-\n-sub _restore_terminal_and_die {\n-\tReadMode 'restore';\n-\tprint \"\\n\";\n-\texit 1;\n-}\n-\n-sub prompt_single_character {\n-\tif ($use_readkey) {\n-\t\tlocal $SIG{TERM} = \\&_restore_terminal_and_die;\n-\t\tlocal $SIG{INT} = \\&_restore_terminal_and_die;\n-\t\tReadMode 'cbreak';\n-\t\tmy $key = ReadKey 0;\n-\t\tReadMode 'restore';\n-\t\tif (defined $key) {\n-\t\t\tif ($use_termcap and $key eq \"\\e\") {\n-\t\t\t\twhile (!defined $term_escapes{$key}) {\n-\t\t\t\t\tmy $next = ReadKey 0.5;\n-\t\t\t\t\tlast if (!defined $next);\n-\t\t\t\t\t$key .= $next;\n-\t\t\t\t}\n-\t\t\t\t$key =~ s/\\e/^[/;\n-\t\t\t}\n-\t\t\tprint \"$key\";\n-\t\t}\n-\t\tprint \"\\n\";\n-\t\treturn $key;\n-\t} else {\n-\t\treturn <STDIN>;\n-\t}\n-}\n-\n-sub prompt_yesno {\n-\tmy ($prompt) = @_;\n-\twhile (1) {\n-\t\tprint colored $prompt_color, $prompt;\n-\t\tmy $line = prompt_single_character;\n-\t\treturn undef unless defined $line;\n-\t\treturn 0 if $line =~ /^n/i;\n-\t\treturn 1 if $line =~ /^y/i;\n-\t}\n-}\n-\n-sub edit_hunk_loop {\n-\tmy ($head, $hunks, $ix) = @_;\n-\tmy $hunk = $hunks->[$ix];\n-\tmy $text = $hunk->{TEXT};\n-\n-\twhile (1) {\n-\t\tmy $newtext = edit_hunk_manually($text);\n-\t\tif (!defined $newtext) {\n-\t\t\treturn undef;\n-\t\t}\n-\t\tmy $newhunk = {\n-\t\t\tTEXT => $newtext,\n-\t\t\tTYPE => $hunk->{TYPE},\n-\t\t\tUSE => 1,\n-\t\t\tDIRTY => 1,\n-\t\t};\n-\t\t$newhunk->{OFS_DELTA} = recount_edited_hunk($text, $newtext);\n-\t\t# If this hunk has already been edited then add the\n-\t\t# offset delta of the previous edit to get the real\n-\t\t# delta from the original unedited hunk.\n-\t\t$hunk->{OFS_DELTA} and\n-\t\t\t\t$newhunk->{OFS_DELTA} += $hunk->{OFS_DELTA};\n-\t\tif (diff_applies($head,\n-\t\t\t\t @{$hunks}[0..$ix-1],\n-\t\t\t\t $newhunk,\n-\t\t\t\t @{$hunks}[$ix+1..$#{$hunks}])) {\n-\t\t\t$newhunk->{DISPLAY} = [color_diff(@{$newtext})];\n-\t\t\treturn $newhunk;\n-\t\t}\n-\t\telse {\n-\t\t\tprompt_yesno(\n-\t\t\t\t# TRANSLATORS: do not translate [y/n]\n-\t\t\t\t# The program will only accept that input\n-\t\t\t\t# at this point.\n-\t\t\t\t# Consider translating (saying \"no\" discards!) as\n-\t\t\t\t# (saying \"n\" for \"no\" discards!) if the translation\n-\t\t\t\t# of the word \"no\" does not start with n.\n-\t\t\t\t__('Your edited hunk does not apply. Edit again '\n-\t\t\t\t   . '(saying \"no\" discards!) [y/n]? ')\n-\t\t\t\t) or return undef;\n-\t\t}\n-\t}\n-}\n-\n-my %help_patch_modes = (\n-\tstage => N__(\n-\"y - stage this hunk\n-n - do not stage this hunk\n-q - quit; do not stage this hunk or any of the remaining ones\n-a - stage this hunk and all later hunks in the file\n-d - do not stage this hunk or any of the later hunks in the file\"),\n-\tstash => N__(\n-\"y - stash this hunk\n-n - do not stash this hunk\n-q - quit; do not stash this hunk or any of the remaining ones\n-a - stash this hunk and all later hunks in the file\n-d - do not stash this hunk or any of the later hunks in the file\"),\n-\treset_head => N__(\n-\"y - unstage this hunk\n-n - do not unstage this hunk\n-q - quit; do not unstage this hunk or any of the remaining ones\n-a - unstage this hunk and all later hunks in the file\n-d - do not unstage this hunk or any of the later hunks in the file\"),\n-\treset_nothead => N__(\n-\"y - apply this hunk to index\n-n - do not apply this hunk to index\n-q - quit; do not apply this hunk or any of the remaining ones\n-a - apply this hunk and all later hunks in the file\n-d - do not apply this hunk or any of the later hunks in the file\"),\n-\tcheckout_index => N__(\n-\"y - discard this hunk from worktree\n-n - do not discard this hunk from worktree\n-q - quit; do not discard this hunk or any of the remaining ones\n-a - discard this hunk and all later hunks in the file\n-d - do not discard this hunk or any of the later hunks in the file\"),\n-\tcheckout_head => N__(\n-\"y - discard this hunk from index and worktree\n-n - do not discard this hunk from index and worktree\n-q - quit; do not discard this hunk or any of the remaining ones\n-a - discard this hunk and all later hunks in the file\n-d - do not discard this hunk or any of the later hunks in the file\"),\n-\tcheckout_nothead => N__(\n-\"y - apply this hunk to index and worktree\n-n - do not apply this hunk to index and worktree\n-q - quit; do not apply this hunk or any of the remaining ones\n-a - apply this hunk and all later hunks in the file\n-d - do not apply this hunk or any of the later hunks in the file\"),\n-\tworktree_head => N__(\n-\"y - discard this hunk from worktree\n-n - do not discard this hunk from worktree\n-q - quit; do not discard this hunk or any of the remaining ones\n-a - discard this hunk and all later hunks in the file\n-d - do not discard this hunk or any of the later hunks in the file\"),\n-\tworktree_nothead => N__(\n-\"y - apply this hunk to worktree\n-n - do not apply this hunk to worktree\n-q - quit; do not apply this hunk or any of the remaining ones\n-a - apply this hunk and all later hunks in the file\n-d - do not apply this hunk or any of the later hunks in the file\"),\n-);\n-\n-sub help_patch_cmd {\n-\tlocal $_;\n-\tmy $other = $_[0] . \",?\";\n-\tprint colored $help_color, __($help_patch_modes{$patch_mode}), \"\\n\",\n-\t\tmap { \"$_\\n\" } grep {\n-\t\t\tmy $c = quotemeta(substr($_, 0, 1));\n-\t\t\t$other =~ /,$c/\n-\t\t} split \"\\n\", __ <<EOF ;\n-g - select a hunk to go to\n-/ - search for a hunk matching the given regex\n-j - leave this hunk undecided, see next undecided hunk\n-J - leave this hunk undecided, see next hunk\n-k - leave this hunk undecided, see previous undecided hunk\n-K - leave this hunk undecided, see previous hunk\n-s - split the current hunk into smaller hunks\n-e - manually edit the current hunk\n-? - print help\n-EOF\n-}\n-\n-sub apply_patch {\n-\tmy $cmd = shift;\n-\tmy $ret = run_git_apply $cmd, @_;\n-\tif (!$ret) {\n-\t\tprint STDERR @_;\n-\t}\n-\treturn $ret;\n-}\n-\n-sub apply_patch_for_checkout_commit {\n-\tmy $reverse = shift;\n-\tmy $applies_index = run_git_apply 'apply '.$reverse.' --cached --check', @_;\n-\tmy $applies_worktree = run_git_apply 'apply '.$reverse.' --check', @_;\n-\n-\tif ($applies_worktree && $applies_index) {\n-\t\trun_git_apply 'apply '.$reverse.' --cached', @_;\n-\t\trun_git_apply 'apply '.$reverse, @_;\n-\t\treturn 1;\n-\t} elsif (!$applies_index) {\n-\t\tprint colored $error_color, __(\"The selected hunks do not apply to the index!\\n\");\n-\t\tif (prompt_yesno __(\"Apply them to the worktree anyway? \")) {\n-\t\t\treturn run_git_apply 'apply '.$reverse, @_;\n-\t\t} else {\n-\t\t\tprint colored $error_color, __(\"Nothing was applied.\\n\");\n-\t\t\treturn 0;\n-\t\t}\n-\t} else {\n-\t\tprint STDERR @_;\n-\t\treturn 0;\n-\t}\n-}\n-\n-sub patch_update_cmd {\n-\tmy @all_mods = list_modified($patch_mode_flavour{FILTER});\n-\terror_msg sprintf(__(\"ignoring unmerged: %s\\n\"), $_->{VALUE})\n-\t\tfor grep { $_->{UNMERGED} } @all_mods;\n-\t@all_mods = grep { !$_->{UNMERGED} } @all_mods;\n-\n-\tmy @mods = grep { !($_->{BINARY}) } @all_mods;\n-\tmy @them;\n-\n-\tif (!@mods) {\n-\t\tif (@all_mods) {\n-\t\t\tprint STDERR __(\"Only binary files changed.\\n\");\n-\t\t} else {\n-\t\t\tprint STDERR __(\"No changes.\\n\");\n-\t\t}\n-\t\treturn 0;\n-\t}\n-\tif ($patch_mode_only) {\n-\t\t@them = @mods;\n-\t}\n-\telse {\n-\t\t@them = list_and_choose({ PROMPT => __('Patch update'),\n-\t\t\t\t\t  HEADER => $status_head, },\n-\t\t\t\t\t@mods);\n-\t}\n-\tfor (@them) {\n-\t\treturn 0 if patch_update_file($_->{VALUE});\n-\t}\n-}\n-\n-# Generate a one line summary of a hunk.\n-sub summarize_hunk {\n-\tmy $rhunk = shift;\n-\tmy $summary = $rhunk->{TEXT}[0];\n-\n-\t# Keep the line numbers, discard extra context.\n-\t$summary =~ s/@@(.*?)@@.*/$1 /s;\n-\t$summary .= \" \" x (20 - length $summary);\n-\n-\t# Add some user context.\n-\tfor my $line (@{$rhunk->{TEXT}}) {\n-\t\tif ($line =~ m/^[+-].*\\w/) {\n-\t\t\t$summary .= $line;\n-\t\t\tlast;\n-\t\t}\n-\t}\n-\n-\tchomp $summary;\n-\treturn substr($summary, 0, 80) . \"\\n\";\n-}\n-\n-\n-# Print a one-line summary of each hunk in the array ref in\n-# the first argument, starting with the index in the 2nd.\n-sub display_hunks {\n-\tmy ($hunks, $i) = @_;\n-\tmy $ctr = 0;\n-\t$i ||= 0;\n-\tfor (; $i < @$hunks && $ctr < 20; $i++, $ctr++) {\n-\t\tmy $status = \" \";\n-\t\tif (defined $hunks->[$i]{USE}) {\n-\t\t\t$status = $hunks->[$i]{USE} ? \"+\" : \"-\";\n-\t\t}\n-\t\tprintf \"%s%2d: %s\",\n-\t\t\t$status,\n-\t\t\t$i + 1,\n-\t\t\tsummarize_hunk($hunks->[$i]);\n-\t}\n-\treturn $i;\n-}\n-\n-my %patch_update_prompt_modes = (\n-\tstage => {\n-\t\tmode => N__(\"Stage mode change [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Stage deletion [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Stage addition [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Stage this hunk [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tstash => {\n-\t\tmode => N__(\"Stash mode change [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Stash deletion [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Stash addition [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Stash this hunk [y,n,q,a,d%s,?]? \"),\n-\t},\n-\treset_head => {\n-\t\tmode => N__(\"Unstage mode change [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Unstage deletion [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Unstage addition [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Unstage this hunk [y,n,q,a,d%s,?]? \"),\n-\t},\n-\treset_nothead => {\n-\t\tmode => N__(\"Apply mode change to index [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Apply deletion to index [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Apply addition to index [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Apply this hunk to index [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tcheckout_index => {\n-\t\tmode => N__(\"Discard mode change from worktree [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Discard deletion from worktree [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Discard addition from worktree [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Discard this hunk from worktree [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tcheckout_head => {\n-\t\tmode => N__(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Discard deletion from index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Discard addition from index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Discard this hunk from index and worktree [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tcheckout_nothead => {\n-\t\tmode => N__(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Apply addition to index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Apply this hunk to index and worktree [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tworktree_head => {\n-\t\tmode => N__(\"Discard mode change from worktree [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Discard deletion from worktree [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Discard addition from worktree [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Discard this hunk from worktree [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tworktree_nothead => {\n-\t\tmode => N__(\"Apply mode change to worktree [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Apply deletion to worktree [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Apply addition to worktree [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Apply this hunk to worktree [y,n,q,a,d%s,?]? \"),\n-\t},\n-);\n-\n-sub patch_update_file {\n-\tmy $quit = 0;\n-\tmy ($ix, $num);\n-\tmy $path = shift;\n-\tmy ($head, @hunk) = parse_diff($path);\n-\t($head, my $mode, my $deletion, my $addition) = parse_diff_header($head);\n-\tfor (@{$head->{DISPLAY}}) {\n-\t\tprint;\n-\t}\n-\n-\tif (@{$mode->{TEXT}}) {\n-\t\tunshift @hunk, $mode;\n-\t}\n-\tif (@{$deletion->{TEXT}}) {\n-\t\tforeach my $hunk (@hunk) {\n-\t\t\tpush @{$deletion->{TEXT}}, @{$hunk->{TEXT}};\n-\t\t\tpush @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};\n-\t\t}\n-\t\t@hunk = ($deletion);\n-\t}\n-\n-\t$num = scalar @hunk;\n-\t$ix = 0;\n-\n-\twhile (1) {\n-\t\tmy ($prev, $next, $other, $undecided, $i);\n-\t\t$other = '';\n-\n-\t\tlast if ($ix and !$num);\n-\t\tif ($num <= $ix) {\n-\t\t\t$ix = 0;\n-\t\t}\n-\t\tfor ($i = 0; $i < $ix; $i++) {\n-\t\t\tif (!defined $hunk[$i]{USE}) {\n-\t\t\t\t$prev = 1;\n-\t\t\t\t$other .= ',k';\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t}\n-\t\tif ($ix) {\n-\t\t\t$other .= ',K';\n-\t\t}\n-\t\tfor ($i = $ix + 1; $i < $num; $i++) {\n-\t\t\tif (!defined $hunk[$i]{USE}) {\n-\t\t\t\t$next = 1;\n-\t\t\t\t$other .= ',j';\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t}\n-\t\tif ($ix < $num - 1) {\n-\t\t\t$other .= ',J';\n-\t\t}\n-\t\tif ($num > 1) {\n-\t\t\t$other .= ',g,/';\n-\t\t}\n-\t\tfor ($i = 0; $i < $num; $i++) {\n-\t\t\tif (!defined $hunk[$i]{USE}) {\n-\t\t\t\t$undecided = 1;\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t}\n-\t\tlast if (!$undecided && ($num || !$addition));\n-\n-\t\tif ($num) {\n-\t\t\tif ($hunk[$ix]{TYPE} eq 'hunk' &&\n-\t\t\t    hunk_splittable($hunk[$ix]{TEXT})) {\n-\t\t\t\t$other .= ',s';\n-\t\t\t}\n-\t\t\tif ($hunk[$ix]{TYPE} eq 'hunk') {\n-\t\t\t\t$other .= ',e';\n-\t\t\t}\n-\t\t\tfor (@{$hunk[$ix]{DISPLAY}}) {\n-\t\t\t\tprint;\n-\t\t\t}\n-\t\t}\n-\t\tmy $type = $num ? $hunk[$ix]{TYPE} : $head->{TYPE};\n-\t\tprint colored $prompt_color, \"(\", ($ix+1), \"/\", ($num ? $num : 1), \") \",\n-\t\t\tsprintf(__($patch_update_prompt_modes{$patch_mode}{$type}), $other);\n-\n-\t\tmy $line = prompt_single_character;\n-\t\tlast unless defined $line;\n-\t\tif ($line) {\n-\t\t\tif ($line =~ /^y/i) {\n-\t\t\t\tif ($num) {\n-\t\t\t\t\t$hunk[$ix]{USE} = 1;\n-\t\t\t\t} else {\n-\t\t\t\t\t$head->{USE} = 1;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\telsif ($line =~ /^n/i) {\n-\t\t\t\tif ($num) {\n-\t\t\t\t\t$hunk[$ix]{USE} = 0;\n-\t\t\t\t} else {\n-\t\t\t\t\t$head->{USE} = 0;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\telsif ($line =~ /^a/i) {\n-\t\t\t\tif ($num) {\n-\t\t\t\t\twhile ($ix < $num) {\n-\t\t\t\t\t\tif (!defined $hunk[$ix]{USE}) {\n-\t\t\t\t\t\t\t$hunk[$ix]{USE} = 1;\n-\t\t\t\t\t\t}\n-\t\t\t\t\t\t$ix++;\n-\t\t\t\t\t}\n-\t\t\t\t} else {\n-\t\t\t\t\t$head->{USE} = 1;\n-\t\t\t\t\t$ix++;\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^g(.*)/) {\n-\t\t\t\tmy $response = $1;\n-\t\t\t\tunless ($other =~ /g/) {\n-\t\t\t\t\terror_msg __(\"No other hunks to goto\\n\");\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t\tmy $no = $ix > 10 ? $ix - 10 : 0;\n-\t\t\t\twhile ($response eq '') {\n-\t\t\t\t\t$no = display_hunks(\\@hunk, $no);\n-\t\t\t\t\tif ($no < $num) {\n-\t\t\t\t\t\tprint __(\"go to which hunk (<ret> to see more)? \");\n-\t\t\t\t\t} else {\n-\t\t\t\t\t\tprint __(\"go to which hunk? \");\n-\t\t\t\t\t}\n-\t\t\t\t\t$response = <STDIN>;\n-\t\t\t\t\tif (!defined $response) {\n-\t\t\t\t\t\t$response = '';\n-\t\t\t\t\t}\n-\t\t\t\t\tchomp $response;\n-\t\t\t\t}\n-\t\t\t\tif ($response !~ /^\\s*\\d+\\s*$/) {\n-\t\t\t\t\terror_msg sprintf(__(\"Invalid number: '%s'\\n\"),\n-\t\t\t\t\t\t\t     $response);\n-\t\t\t\t} elsif (0 < $response && $response <= $num) {\n-\t\t\t\t\t$ix = $response - 1;\n-\t\t\t\t} else {\n-\t\t\t\t\terror_msg sprintf(__n(\"Sorry, only %d hunk available.\\n\",\n-\t\t\t\t\t\t\t      \"Sorry, only %d hunks available.\\n\", $num), $num);\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^d/i) {\n-\t\t\t\tif ($num) {\n-\t\t\t\t\twhile ($ix < $num) {\n-\t\t\t\t\t\tif (!defined $hunk[$ix]{USE}) {\n-\t\t\t\t\t\t\t$hunk[$ix]{USE} = 0;\n-\t\t\t\t\t\t}\n-\t\t\t\t\t\t$ix++;\n-\t\t\t\t\t}\n-\t\t\t\t} else {\n-\t\t\t\t\t$head->{USE} = 0;\n-\t\t\t\t\t$ix++;\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^q/i) {\n-\t\t\t\tif ($num) {\n-\t\t\t\t\tfor ($i = 0; $i < $num; $i++) {\n-\t\t\t\t\t\tif (!defined $hunk[$i]{USE}) {\n-\t\t\t\t\t\t\t$hunk[$i]{USE} = 0;\n-\t\t\t\t\t\t}\n-\t\t\t\t\t}\n-\t\t\t\t} elsif (!defined $head->{USE}) {\n-\t\t\t\t\t$head->{USE} = 0;\n-\t\t\t\t}\n-\t\t\t\t$quit = 1;\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t\telsif ($line =~ m|^/(.*)|) {\n-\t\t\t\tmy $regex = $1;\n-\t\t\t\tunless ($other =~ m|/|) {\n-\t\t\t\t\terror_msg __(\"No other hunks to search\\n\");\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t\tif ($regex eq \"\") {\n-\t\t\t\t\tprint colored $prompt_color, __(\"search for regex? \");\n-\t\t\t\t\t$regex = <STDIN>;\n-\t\t\t\t\tif (defined $regex) {\n-\t\t\t\t\t\tchomp $regex;\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t\tmy $search_string;\n-\t\t\t\teval {\n-\t\t\t\t\t$search_string = qr{$regex}m;\n-\t\t\t\t};\n-\t\t\t\tif ($@) {\n-\t\t\t\t\tmy ($err,$exp) = ($@, $1);\n-\t\t\t\t\t$err =~ s/ at .*git-add--interactive line \\d+, <STDIN> line \\d+.*$//;\n-\t\t\t\t\terror_msg sprintf(__(\"Malformed search regexp %s: %s\\n\"), $exp, $err);\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t\tmy $iy = $ix;\n-\t\t\t\twhile (1) {\n-\t\t\t\t\tmy $text = join (\"\", @{$hunk[$iy]{TEXT}});\n-\t\t\t\t\tlast if ($text =~ $search_string);\n-\t\t\t\t\t$iy++;\n-\t\t\t\t\t$iy = 0 if ($iy >= $num);\n-\t\t\t\t\tif ($ix == $iy) {\n-\t\t\t\t\t\terror_msg __(\"No hunk matches the given pattern\\n\");\n-\t\t\t\t\t\tlast;\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t\t$ix = $iy;\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^K/) {\n-\t\t\t\tif ($other =~ /K/) {\n-\t\t\t\t\t$ix--;\n-\t\t\t\t}\n-\t\t\t\telse {\n-\t\t\t\t\terror_msg __(\"No previous hunk\\n\");\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^J/) {\n-\t\t\t\tif ($other =~ /J/) {\n-\t\t\t\t\t$ix++;\n-\t\t\t\t}\n-\t\t\t\telse {\n-\t\t\t\t\terror_msg __(\"No next hunk\\n\");\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^k/) {\n-\t\t\t\tif ($other =~ /k/) {\n-\t\t\t\t\twhile (1) {\n-\t\t\t\t\t\t$ix--;\n-\t\t\t\t\t\tlast if (!$ix ||\n-\t\t\t\t\t\t\t !defined $hunk[$ix]{USE});\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t\telse {\n-\t\t\t\t\terror_msg __(\"No previous hunk\\n\");\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^j/) {\n-\t\t\t\tif ($other !~ /j/) {\n-\t\t\t\t\terror_msg __(\"No next hunk\\n\");\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\telsif ($line =~ /^s/) {\n-\t\t\t\tunless ($other =~ /s/) {\n-\t\t\t\t\terror_msg __(\"Sorry, cannot split this hunk\\n\");\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t\tmy @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});\n-\t\t\t\tif (1 < @split) {\n-\t\t\t\t\tprint colored $header_color, sprintf(\n-\t\t\t\t\t\t__n(\"Split into %d hunk.\\n\",\n-\t\t\t\t\t\t    \"Split into %d hunks.\\n\",\n-\t\t\t\t\t\t    scalar(@split)), scalar(@split));\n-\t\t\t\t}\n-\t\t\t\tsplice (@hunk, $ix, 1, @split);\n-\t\t\t\t$num = scalar @hunk;\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^e/) {\n-\t\t\t\tunless ($other =~ /e/) {\n-\t\t\t\t\terror_msg __(\"Sorry, cannot edit this hunk\\n\");\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t\tmy $newhunk = edit_hunk_loop($head, \\@hunk, $ix);\n-\t\t\t\tif (defined $newhunk) {\n-\t\t\t\t\tsplice @hunk, $ix, 1, $newhunk;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\thelp_patch_cmd($other);\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\t# soft increment\n-\t\t\twhile (1) {\n-\t\t\t\t$ix++;\n-\t\t\t\tlast if ($ix >= $num ||\n-\t\t\t\t\t !defined $hunk[$ix]{USE});\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\t@hunk = coalesce_overlapping_hunks(@hunk) if ($num);\n-\n-\tmy $n_lofs = 0;\n-\tmy @result = ();\n-\tfor (@hunk) {\n-\t\tif ($_->{USE}) {\n-\t\t\tpush @result, @{$_->{TEXT}};\n-\t\t}\n-\t}\n-\n-\tif (@result or $head->{USE}) {\n-\t\tmy @patch = reassemble_patch($head->{TEXT}, @result);\n-\t\tmy $apply_routine = $patch_mode_flavour{APPLY};\n-\t\t&$apply_routine(@patch);\n-\t\trefresh();\n-\t}\n-\n-\tprint \"\\n\";\n-\treturn $quit;\n-}\n-\n-sub diff_cmd {\n-\tmy @mods = list_modified('index-only');\n-\t@mods = grep { !($_->{BINARY}) } @mods;\n-\treturn if (!@mods);\n-\tmy (@them) = list_and_choose({ PROMPT => __('Review diff'),\n-\t\t\t\t     IMMEDIATE => 1,\n-\t\t\t\t     HEADER => $status_head, },\n-\t\t\t\t   @mods);\n-\treturn if (!@them);\n-\tmy $reference = (is_initial_commit()) ? get_empty_tree() : 'HEAD';\n-\tsystem(qw(git diff -p --cached), $reference, '--',\n-\t\tmap { $_->{VALUE} } @them);\n-}\n-\n-sub quit_cmd {\n-\tprint __(\"Bye.\\n\");\n-\texit(0);\n-}\n-\n-sub help_cmd {\n-# TRANSLATORS: please do not translate the command names\n-# 'status', 'update', 'revert', etc.\n-\tprint colored $help_color, __ <<'EOF' ;\n-status        - show paths with changes\n-update        - add working tree state to the staged set of changes\n-revert        - revert staged set of changes back to the HEAD version\n-patch         - pick hunks and update selectively\n-diff          - view diff between HEAD and index\n-add untracked - add contents of untracked files to the staged set of changes\n-EOF\n-}\n-\n-sub process_args {\n-\treturn unless @ARGV;\n-\tmy $arg = shift @ARGV;\n-\tif ($arg =~ /--patch(?:=(.*))?/) {\n-\t\tif (defined $1) {\n-\t\t\tif ($1 eq 'reset') {\n-\t\t\t\t$patch_mode = 'reset_head';\n-\t\t\t\t$patch_mode_revision = 'HEAD';\n-\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\tif ($arg ne '--') {\n-\t\t\t\t\t$patch_mode_revision = $arg;\n-\n-\t\t\t\t\t# NEEDSWORK: Instead of comparing to the literal \"HEAD\",\n-\t\t\t\t\t# compare the commit objects instead so that other ways of\n-\t\t\t\t\t# saying the same thing (such as \"@\") are also handled\n-\t\t\t\t\t# appropriately.\n-\t\t\t\t\t#\n-\t\t\t\t\t# This applies to the cases below too.\n-\t\t\t\t\t$patch_mode = ($arg eq 'HEAD' ?\n-\t\t\t\t\t\t       'reset_head' : 'reset_nothead');\n-\t\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\t}\n-\t\t\t} elsif ($1 eq 'checkout') {\n-\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\tif ($arg eq '--') {\n-\t\t\t\t\t$patch_mode = 'checkout_index';\n-\t\t\t\t} else {\n-\t\t\t\t\t$patch_mode_revision = $arg;\n-\t\t\t\t\t$patch_mode = ($arg eq 'HEAD' ?\n-\t\t\t\t\t\t       'checkout_head' : 'checkout_nothead');\n-\t\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\t}\n-\t\t\t} elsif ($1 eq 'worktree') {\n-\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\tif ($arg eq '--') {\n-\t\t\t\t\t$patch_mode = 'checkout_index';\n-\t\t\t\t} else {\n-\t\t\t\t\t$patch_mode_revision = $arg;\n-\t\t\t\t\t$patch_mode = ($arg eq 'HEAD' ?\n-\t\t\t\t\t\t       'worktree_head' : 'worktree_nothead');\n-\t\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\t}\n-\t\t\t} elsif ($1 eq 'stage' or $1 eq 'stash') {\n-\t\t\t\t$patch_mode = $1;\n-\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t} else {\n-\t\t\t\tdie sprintf(__(\"unknown --patch mode: %s\"), $1);\n-\t\t\t}\n-\t\t} else {\n-\t\t\t$patch_mode = 'stage';\n-\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t}\n-\t\tdie sprintf(__(\"invalid argument %s, expecting --\"),\n-\t\t\t       $arg) unless $arg eq \"--\";\n-\t\t%patch_mode_flavour = %{$patch_modes{$patch_mode}};\n-\t\t$patch_mode_only = 1;\n-\t}\n-\telsif ($arg ne \"--\") {\n-\t\tdie sprintf(__(\"invalid argument %s, expecting --\"), $arg);\n-\t}\n-}\n-\n-sub main_loop {\n-\tmy @cmd = ([ 'status', \\&status_cmd, ],\n-\t\t   [ 'update', \\&update_cmd, ],\n-\t\t   [ 'revert', \\&revert_cmd, ],\n-\t\t   [ 'add untracked', \\&add_untracked_cmd, ],\n-\t\t   [ 'patch', \\&patch_update_cmd, ],\n-\t\t   [ 'diff', \\&diff_cmd, ],\n-\t\t   [ 'quit', \\&quit_cmd, ],\n-\t\t   [ 'help', \\&help_cmd, ],\n-\t);\n-\twhile (1) {\n-\t\tmy ($it) = list_and_choose({ PROMPT => __('What now'),\n-\t\t\t\t\t     SINGLETON => 1,\n-\t\t\t\t\t     LIST_FLAT => 4,\n-\t\t\t\t\t     HEADER => __('*** Commands ***'),\n-\t\t\t\t\t     ON_EOF => \\&quit_cmd,\n-\t\t\t\t\t     IMMEDIATE => 1 }, @cmd);\n-\t\tif ($it) {\n-\t\t\teval {\n-\t\t\t\t$it->[1]->();\n-\t\t\t};\n-\t\t\tif ($@) {\n-\t\t\t\tprint \"$@\";\n-\t\t\t}\n-\t\t}\n-\t}\n-}\n-\n-process_args();\n-refresh();\n-if ($patch_mode_only) {\n-\tpatch_update_cmd();\n-}\n-else {\n-\tstatus_cmd();\n-\tmain_loop();\n-}\ndiff --git a/t/README b/t/README\nindex 979b2d4833d..29576c37488 100644\n--- a/t/README\n+++ b/t/README\n@@ -449,10 +449,6 @@ the --sparse command-line argument.\n GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path\n by overriding the minimum number of cache entries required per thread.\n \n-GIT_TEST_ADD_I_USE_BUILTIN=<boolean>, when false, disables the\n-built-in version of git add -i. See 'add.interactive.useBuiltin' in\n-git-config(1).\n-\n GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading\n of the index for the whole test suite by bypassing the default number of\n cache entries and thread minimums. Setting this to 1 will make the\ndiff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh\nindex a5822e41af2..747eb5563ec 100755\n--- a/t/t2016-checkout-patch.sh\n+++ b/t/t2016-checkout-patch.sh\n@@ -4,12 +4,6 @@ test_description='git checkout --patch'\n \n . ./lib-patch-mode.sh\n \n-if ! test_have_prereq ADD_I_USE_BUILTIN && ! test_have_prereq PERL\n-then\n-\tskip_all='skipping interactive add tests, PERL not set'\n-\ttest_done\n-fi\n-\n test_expect_success 'setup' '\n \tmkdir dir &&\n \techo parent > dir/foo &&\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 5841f280fb2..3a99837d9b1 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -7,7 +7,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n-if test_have_prereq !ADD_I_USE_BUILTIN,!PERL\n+if test_have_prereq !PERL\n then\n \tskip_all='skipping add -i (scripted) tests, perl not available'\n \ttest_done\n@@ -46,6 +46,21 @@ force_color () {\n \t)\n }\n \n+test_expect_success 'warn about add.interactive.useBuiltin' '\n+\tcat >expect <<-\\EOF &&\n+\twarning: the add.interactive.useBuiltin setting has been removed!\n+\tSee its entry in '\\''git help config'\\'' for details.\n+\tNo changes.\n+\tEOF\n+\n+\tfor v in = =true =false\n+\tdo\n+\t\tgit -c \"add.interactive.useBuiltin$v\" add -p >out 2>actual &&\n+\t\ttest_must_be_empty out &&\n+\t\ttest_cmp expect actual || return 1\n+\tdone\n+'\n+\n test_expect_success 'setup (initial)' '\n \techo content >file &&\n \tgit add file &&\n@@ -547,15 +562,7 @@ test_expect_success 'split hunk \"add -p (edit)\"' '\n \t! grep \"^+15\" actual\n '\n \n-test_expect_success 'setup ADD_I_USE_BUILTIN check' '\n-\tresult=success &&\n-\tif ! test_have_prereq ADD_I_USE_BUILTIN\n-\tthen\n-\t\tresult=failure\n-\tfi\n-'\n-\n-test_expect_$result 'split hunk \"add -p (no, yes, edit)\"' '\n+test_expect_success 'split hunk \"add -p (no, yes, edit)\"' '\n \ttest_write_lines 5 10 20 21 30 31 40 50 60 >test &&\n \tgit reset &&\n \t# test sequence is s(plit), n(o), y(es), e(dit)\n@@ -579,7 +586,7 @@ test_expect_success 'split hunk with incomplete line at end' '\n \ttest_must_fail git grep --cached before\n '\n \n-test_expect_$result 'edit, adding lines to the first hunk' '\n+test_expect_success 'edit, adding lines to the first hunk' '\n \ttest_write_lines 10 11 20 30 40 50 51 60 >test &&\n \tgit reset &&\n \ttr _ \" \" >patch <<-EOF &&\ndiff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh\nindex cada952f9ae..9fdafeb1e90 100755\n--- a/t/t6132-pathspec-exclude.sh\n+++ b/t/t6132-pathspec-exclude.sh\n@@ -293,11 +293,7 @@ test_expect_success 'add with all negative' '\n \ttest_cmp expect actual\n '\n \n-test_lazy_prereq ADD_I_USE_BUILTIN_OR_PERL '\n-\ttest_have_prereq ADD_I_USE_BUILTIN || test_have_prereq PERL\n-'\n-\n-test_expect_success ADD_I_USE_BUILTIN_OR_PERL 'add -p with all negative' '\n+test_expect_success 'add -p with all negative' '\n \tH=$(git rev-parse HEAD) &&\n \tgit reset --hard $H &&\n \tgit clean -f &&\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 01e88781dd2..d272cca008b 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -1937,10 +1937,6 @@ test_lazy_prereq SHA1 '\n \tesac\n '\n \n-test_lazy_prereq ADD_I_USE_BUILTIN '\n-\ttest_bool_env GIT_TEST_ADD_I_USE_BUILTIN true\n-'\n-\n # Ensure that no test accidentally triggers a Git command\n # that runs the actual maintenance scheduler, affecting a user's\n # system permanently.\n-- \n2.39.1.1397.gbe42486b8a4\n\n"},{"id":"471441","messageId":"Y906yEGFEIglRyVb@coredump.intra.peff.net","threadId":"59189","inReplyTo":"cover-0.3-00000000000-20230203T125859Z-avarab@gmail.com","subject":"Re: [PATCH 0/3] add: remove Perl version of \"git add -[pi]\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2023-02-03T16:48:08Z","receivedAt":"2023-02-03T16:48:17Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Feb 03, 2023 at 05:30:01PM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> After it was made the default we had a next-release regression fix[1],\n> but haven't had any issues since then. Any outstanding bugs in it are\n> something we'd fix in the C code, not something where users are likely\n> to want an escape hatch to scramble back to the Perl implementation.\n\nI'm in favor of dropping the perl version, but note that this paragraph\nisn't quite accurate. There was at least one more regression after that,\nsolved by fb094cb583 (Merge branch 'js/add-p-diff-parsing-fix',\n2022-09-09).\n\nI'd probably leave the final decision on its status to Johannes. This is\n(I think) the last time we discussed it:\n\n  https://lore.kernel.org/git/rsrn5988-37n4-7q45-s1o9-6n40rropp120@tzk.qr/\n\nThat was 6 months ago, so maybe it's time now.\n\n-Peff\n"},{"id":"471457","messageId":"xmqqilgio85n.fsf@gitster.g","threadId":"59189","inReplyTo":"Y906yEGFEIglRyVb@coredump.intra.peff.net","subject":"Re: [PATCH 0/3] add: remove Perl version of \"git add -[pi]\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-02-03T19:34:12Z","receivedAt":"2023-02-03T19:34:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Fri, Feb 03, 2023 at 05:30:01PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>\n>> After it was made the default we had a next-release regression fix[1],\n>> but haven't had any issues since then. Any outstanding bugs in it are\n>> something we'd fix in the C code, not something where users are likely\n>> to want an escape hatch to scramble back to the Perl implementation.\n>\n> I'm in favor of dropping the perl version, but note that this paragraph\n> isn't quite accurate. There was at least one more regression after that,\n> solved by fb094cb583 (Merge branch 'js/add-p-diff-parsing-fix',\n> 2022-09-09).\n>\n> I'd probably leave the final decision on its status to Johannes. This is\n> (I think) the last time we discussed it:\n>\n>   https://lore.kernel.org/git/rsrn5988-37n4-7q45-s1o9-6n40rropp120@tzk.qr/\n>\n> That was 6 months ago, so maybe it's time now.\n\nSounds good to me, too.\n"},{"id":"471574","messageId":"7ed6f7a8-61de-3b60-c7fa-4b5acdbb4e98@gmail.com","threadId":"59189","inReplyTo":"patch-1.3-f39dde93011-20230203T125859Z-avarab@gmail.com","subject":"Re: [PATCH 1/3] add: remove \"add.interactive.useBuiltin\" & Perl \"git add--interactive\"","fromName":"Philippe Blain","fromEmail":"levraiphilippeblain@gmail.com","sentAt":"2023-02-06T18:38:03Z","receivedAt":"2023-02-06T18:38:19Z","isPatch":true,"sender":{"key":"levraiphilippeblain@gmail.com","avatar":"https://avatars.githubusercontent.com/u/44212482?v=4"},"body":"Hi Ævar,\n\nLe 2023-02-03 à 11:30, Ævar Arnfjörð Bjarmason a écrit :\n> Since [1] first released with Git v2.37.0 the built-in version of \"add\n> -i\" has been the default. That built-in implementation was added in\n> [2], first released with Git v2.25.0.\n> \n> At this point enough time has passed to allow for finding any\n> remaining bugs in this new implementation, so let's remove the\n> fallback code.\n> \n> As with similar migrations for \"stash\"[3] and \"rebase\"[4] we're\n> keeping a mention of \"add.interactive.useBuiltin\" in the\n> documentation, but adding a warning() to notify any outstanding users\n> that the built-in is now the default. As with [5] and [6] we should\n> follow-up in the future and eventually remove that warning.\n> \n> 1. 0527ccb1b55 (add -i: default to the built-in implementation,\n>    2021-11-30)\n> 2. f83dff60a78 (Start to implement a built-in version of `git add\n>    --interactive`, 2019-11-13)\n> 3. 8a2cd3f5123 (stash: remove the stash.useBuiltin setting,\n>    2020-03-03)\n> 4. d03ebd411c6 (rebase: remove the rebase.useBuiltin setting,\n>    2019-03-18)\n> 5. deeaf5ee077 (stash: remove documentation for `stash.useBuiltin`,\n>    2022-01-27)\n> 6. 9bcde4d5314 (rebase: remove transitory rebase.useBuiltin setting &\n>    env, 2021-03-23)\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  .gitignore                   |    1 -\n>  Documentation/config/add.txt |    7 +-\n>  Makefile                     |    1 -\n>  builtin/add.c                |   66 +-\n>  ci/run-build-and-tests.sh    |    1 -\n>  git-add--interactive.perl    | 1920 ----------------------------------\n>  t/README                     |    4 -\n>  t/t2016-checkout-patch.sh    |    6 -\n>  t/t3701-add-interactive.sh   |   29 +-\n>  t/t6132-pathspec-exclude.sh  |    6 +-\n>  t/test-lib.sh                |    4 -\n>  11 files changed, 46 insertions(+), 1999 deletions(-)\n>  delete mode 100755 git-add--interactive.perl\n\nINSTALL mentions that Perl is needed to use 'git add -p', so we would want\nto change that also, I think. I came up with this change to avoid reflowing \nthe whole paragraph (feel free to squash it in)\n\ndiff --git a/INSTALL b/INSTALL\nindex 3344788397..c34d967f15 100644\n--- a/INSTALL\n+++ b/INSTALL\n@@ -120,7 +120,7 @@ Issues of note:\n          for everyday use (e.g. \"bisect\", \"request-pull\").\n \n        - \"Perl\" version 5.8 or later is needed to use some of the\n-         features (e.g. preparing a partial commit using \"git add -i/-p\",\n+         features (e.g. sending patches using \"git send-email\",\n          interacting with svn repositories with \"git svn\").  If you can\n          live without these, use NO_PERL.  Note that recent releases of\n          Redhat/Fedora are reported to ship Perl binary package with some\n\nThe other commands still in Perl not mentioned above are:\n\ngit-archimport.perl\ngit-cvsexportcommit.perl\ngit-cvsimport.perl\ngit-cvsserver.perl\n\nwhich I think are too obscure these days to warrant a mention in there...\n"},{"id":"471630","messageId":"cover-v2-0.3-00000000000-20230206T225639Z-avarab@gmail.com","threadId":"59189","inReplyTo":"cover-0.3-00000000000-20230203T125859Z-avarab@gmail.com","subject":"[PATCH v2 0/3] add: remove Perl version of \"git add -[pi]\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-02-06T22:58:55Z","receivedAt":"2023-02-06T22:59:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"We've given the \"git add -i\" migration away from Perl to a built-in\nenough time to remove the old Perl code.\n\nFor v1, see:\nhttps://lore.kernel.org/git/cover-0.3-00000000000-20230203T125859Z-avarab@gmail.com/\n\nBranch & CI for this at:\nhttps://github.com/avar/git/tree/avar/nuke-perl-add--interactive-2\n\nThe only change from v1 is to squash in a suggested change from\nPhilippe Blain, thanks!\n\nÆvar Arnfjörð Bjarmason (3):\n  add: remove \"add.interactive.useBuiltin\" & Perl \"git add--interactive\"\n  add API: remove run_add_interactive() wrapper function\n  docs & comments: replace mentions of \"git-add--interactive.perl\"\n\n .gitignore                   |    1 -\n Documentation/config/add.txt |    7 +-\n Documentation/git-add.txt    |    6 +-\n INSTALL                      |    2 +-\n Makefile                     |    1 -\n builtin/add.c                |   61 +-\n builtin/checkout.c           |   12 +-\n builtin/clean.c              |    2 +-\n builtin/reset.c              |    4 +-\n builtin/stash.c              |    3 +-\n ci/run-build-and-tests.sh    |    1 -\n commit.h                     |    2 -\n git-add--interactive.perl    | 1920 ----------------------------------\n pathspec.c                   |    2 +-\n t/README                     |    4 -\n t/t2016-checkout-patch.sh    |    6 -\n t/t3701-add-interactive.sh   |   29 +-\n t/t6132-pathspec-exclude.sh  |    6 +-\n t/test-lib.sh                |    4 -\n 19 files changed, 50 insertions(+), 2023 deletions(-)\n delete mode 100755 git-add--interactive.perl\n\nRange-diff against v1:\n1:  f39dde93011 ! 1:  71c7922b25f add: remove \"add.interactive.useBuiltin\" & Perl \"git add--interactive\"\n    @@ Documentation/config/add.txt: add.ignore-errors (deprecated)::\n     +\tinteractive mode, which then became the default in Git\n     +\tversions v2.37.0 to v2.39.0.\n     \n    + ## INSTALL ##\n    +@@ INSTALL: Issues of note:\n    + \t  for everyday use (e.g. \"bisect\", \"request-pull\").\n    + \n    + \t- \"Perl\" version 5.8 or later is needed to use some of the\n    +-\t  features (e.g. preparing a partial commit using \"git add -i/-p\",\n    ++\t  features (e.g. sending patches using \"git send-email\",\n    + \t  interacting with svn repositories with \"git svn\").  If you can\n    + \t  live without these, use NO_PERL.  Note that recent releases of\n    + \t  Redhat/Fedora are reported to ship Perl binary package with some\n    +\n      ## Makefile ##\n     @@ Makefile: SCRIPT_LIB += git-mergetool--lib\n      SCRIPT_LIB += git-sh-i18n\n2:  fb0dafaf4a4 = 2:  9299f22cc2c add API: remove run_add_interactive() wrapper function\n3:  6ee56de1371 = 3:  09bece75c22 docs & comments: replace mentions of \"git-add--interactive.perl\"\n-- \n2.39.1.1425.gac85d95d48c\n\n"},{"id":"471632","messageId":"patch-v2-1.3-71c7922b25f-20230206T225639Z-avarab@gmail.com","threadId":"59189","inReplyTo":"cover-v2-0.3-00000000000-20230206T225639Z-avarab@gmail.com","subject":"[PATCH v2 1/3] add: remove \"add.interactive.useBuiltin\" & Perl \"git add--interactive\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-02-06T22:58:56Z","receivedAt":"2023-02-06T22:59:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since [1] first released with Git v2.37.0 the built-in version of \"add\n-i\" has been the default. That built-in implementation was added in\n[2], first released with Git v2.25.0.\n\nAt this point enough time has passed to allow for finding any\nremaining bugs in this new implementation, so let's remove the\nfallback code.\n\nAs with similar migrations for \"stash\"[3] and \"rebase\"[4] we're\nkeeping a mention of \"add.interactive.useBuiltin\" in the\ndocumentation, but adding a warning() to notify any outstanding users\nthat the built-in is now the default. As with [5] and [6] we should\nfollow-up in the future and eventually remove that warning.\n\n1. 0527ccb1b55 (add -i: default to the built-in implementation,\n   2021-11-30)\n2. f83dff60a78 (Start to implement a built-in version of `git add\n   --interactive`, 2019-11-13)\n3. 8a2cd3f5123 (stash: remove the stash.useBuiltin setting,\n   2020-03-03)\n4. d03ebd411c6 (rebase: remove the rebase.useBuiltin setting,\n   2019-03-18)\n5. deeaf5ee077 (stash: remove documentation for `stash.useBuiltin`,\n   2022-01-27)\n6. 9bcde4d5314 (rebase: remove transitory rebase.useBuiltin setting &\n   env, 2021-03-23)\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n .gitignore                   |    1 -\n Documentation/config/add.txt |    7 +-\n INSTALL                      |    2 +-\n Makefile                     |    1 -\n builtin/add.c                |   66 +-\n ci/run-build-and-tests.sh    |    1 -\n git-add--interactive.perl    | 1920 ----------------------------------\n t/README                     |    4 -\n t/t2016-checkout-patch.sh    |    6 -\n t/t3701-add-interactive.sh   |   29 +-\n t/t6132-pathspec-exclude.sh  |    6 +-\n t/test-lib.sh                |    4 -\n 12 files changed, 47 insertions(+), 2000 deletions(-)\n delete mode 100755 git-add--interactive.perl\n\ndiff --git a/.gitignore b/.gitignore\nindex 6782f3cecac..e875c590545 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -14,7 +14,6 @@\n /bin-wrappers/\n /git\n /git-add\n-/git-add--interactive\n /git-am\n /git-annotate\n /git-apply\ndiff --git a/Documentation/config/add.txt b/Documentation/config/add.txt\nindex 3e859f34197..e0354ceaed9 100644\n--- a/Documentation/config/add.txt\n+++ b/Documentation/config/add.txt\n@@ -7,6 +7,7 @@ add.ignore-errors (deprecated)::\n \tvariables.\n \n add.interactive.useBuiltin::\n-\tSet to `false` to fall back to the original Perl implementation of\n-\tthe interactive version of linkgit:git-add[1] instead of the built-in\n-\tversion. Is `true` by default.\n+\tUnused configuration variable. Used in Git versions v2.25.0 to\n+\tv2.36.0 to enable the built-in version of linkgit:git-add[1]'s\n+\tinteractive mode, which then became the default in Git\n+\tversions v2.37.0 to v2.39.0.\ndiff --git a/INSTALL b/INSTALL\nindex d5694f8c470..4b422888828 100644\n--- a/INSTALL\n+++ b/INSTALL\n@@ -120,7 +120,7 @@ Issues of note:\n \t  for everyday use (e.g. \"bisect\", \"request-pull\").\n \n \t- \"Perl\" version 5.8 or later is needed to use some of the\n-\t  features (e.g. preparing a partial commit using \"git add -i/-p\",\n+\t  features (e.g. sending patches using \"git send-email\",\n \t  interacting with svn repositories with \"git svn\").  If you can\n \t  live without these, use NO_PERL.  Note that recent releases of\n \t  Redhat/Fedora are reported to ship Perl binary package with some\ndiff --git a/Makefile b/Makefile\nindex 45bd6ac9c3e..50ee51fde32 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -708,7 +708,6 @@ SCRIPT_LIB += git-mergetool--lib\n SCRIPT_LIB += git-sh-i18n\n SCRIPT_LIB += git-sh-setup\n \n-SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-archimport.perl\n SCRIPT_PERL += git-cvsexportcommit.perl\n SCRIPT_PERL += git-cvsimport.perl\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 0c604022676..9f5b6e26a02 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -241,55 +241,35 @@ static int refresh(int verbose, const struct pathspec *pathspec)\n int run_add_interactive(const char *revision, const char *patch_mode,\n \t\t\tconst struct pathspec *pathspec)\n {\n-\tint i;\n-\tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\tint use_builtin_add_i =\n-\t\tgit_env_bool(\"GIT_TEST_ADD_I_USE_BUILTIN\", -1);\n-\n-\tif (use_builtin_add_i < 0 &&\n-\t    git_config_get_bool(\"add.interactive.usebuiltin\",\n-\t\t\t\t&use_builtin_add_i))\n-\t\tuse_builtin_add_i = 1;\n-\n-\tif (use_builtin_add_i != 0) {\n-\t\tenum add_p_mode mode;\n-\n-\t\tif (!patch_mode)\n-\t\t\treturn !!run_add_i(the_repository, pathspec);\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 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-\t\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n-\t}\n+\tenum add_p_mode mode;\n+\n+\tif (!patch_mode)\n+\t\treturn !!run_add_i(the_repository, pathspec);\n+\n+\tif (!strcmp(patch_mode, \"--patch\"))\n+\t\tmode = ADD_P_ADD;\n+\telse if (!strcmp(patch_mode, \"--patch=stash\"))\n+\t\tmode = ADD_P_STASH;\n+\telse if (!strcmp(patch_mode, \"--patch=reset\"))\n+\t\tmode = ADD_P_RESET;\n+\telse if (!strcmp(patch_mode, \"--patch=checkout\"))\n+\t\tmode = ADD_P_CHECKOUT;\n+\telse if (!strcmp(patch_mode, \"--patch=worktree\"))\n+\t\tmode = ADD_P_WORKTREE;\n+\telse\n+\t\tdie(\"'%s' not supported\", patch_mode);\n \n-\tstrvec_push(&cmd.args, \"add--interactive\");\n-\tif (patch_mode)\n-\t\tstrvec_push(&cmd.args, patch_mode);\n-\tif (revision)\n-\t\tstrvec_push(&cmd.args, revision);\n-\tstrvec_push(&cmd.args, \"--\");\n-\tfor (i = 0; i < pathspec->nr; i++)\n-\t\t/* pass original pathspec, to be re-parsed */\n-\t\tstrvec_push(&cmd.args, pathspec->items[i].original);\n-\n-\tcmd.git_cmd = 1;\n-\treturn run_command(&cmd);\n+\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n }\n \n int interactive_add(const char **argv, const char *prefix, int patch)\n {\n \tstruct pathspec pathspec;\n+\tint unused;\n+\n+\tif (!git_config_get_bool(\"add.interactive.usebuiltin\", &unused))\n+\t\twarning(_(\"the add.interactive.useBuiltin setting has been removed!\\n\"\n+\t\t\t  \"See its entry in 'git help config' for details.\"));\n \n \tparse_pathspec(&pathspec, 0,\n \t\t       PATHSPEC_PREFER_FULL |\ndiff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh\nindex 8ebff425967..b098e10f52a 100755\n--- a/ci/run-build-and-tests.sh\n+++ b/ci/run-build-and-tests.sh\n@@ -26,7 +26,6 @@ linux-TEST-vars)\n \texport GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1\n \texport GIT_TEST_MULTI_PACK_INDEX=1\n \texport GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=1\n-\texport GIT_TEST_ADD_I_USE_BUILTIN=0\n \texport GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master\n \texport GIT_TEST_WRITE_REV_INDEX=1\n \texport GIT_TEST_CHECKOUT_WORKERS=2\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\ndeleted file mode 100755\nindex 95887fd8e52..00000000000\n--- a/git-add--interactive.perl\n+++ /dev/null\n@@ -1,1920 +0,0 @@\n-#!/usr/bin/perl\n-\n-use 5.008;\n-use strict;\n-use warnings;\n-use Git qw(unquote_path);\n-use Git::I18N;\n-\n-binmode(STDOUT, \":raw\");\n-\n-my $repo = Git->repository();\n-\n-my $menu_use_color = $repo->get_colorbool('color.interactive');\n-my ($prompt_color, $header_color, $help_color) =\n-\t$menu_use_color ? (\n-\t\t$repo->get_color('color.interactive.prompt', 'bold blue'),\n-\t\t$repo->get_color('color.interactive.header', 'bold'),\n-\t\t$repo->get_color('color.interactive.help', 'red bold'),\n-\t) : ();\n-my $error_color = ();\n-if ($menu_use_color) {\n-\tmy $help_color_spec = ($repo->config('color.interactive.help') or\n-\t\t\t\t'red bold');\n-\t$error_color = $repo->get_color('color.interactive.error',\n-\t\t\t\t\t$help_color_spec);\n-}\n-\n-my $diff_use_color = $repo->get_colorbool('color.diff');\n-my ($fraginfo_color) =\n-\t$diff_use_color ? (\n-\t\t$repo->get_color('color.diff.frag', 'cyan'),\n-\t) : ();\n-my ($diff_context_color) =\n-\t$diff_use_color ? (\n-\t\t$repo->get_color($repo->config('color.diff.context') ? 'color.diff.context' : 'color.diff.plain', ''),\n-\t) : ();\n-my ($diff_old_color) =\n-\t$diff_use_color ? (\n-\t\t$repo->get_color('color.diff.old', 'red'),\n-\t) : ();\n-my ($diff_new_color) =\n-\t$diff_use_color ? (\n-\t\t$repo->get_color('color.diff.new', 'green'),\n-\t) : ();\n-\n-my $normal_color = $repo->get_color(\"\", \"reset\");\n-\n-my $diff_algorithm = $repo->config('diff.algorithm');\n-my $diff_filter = $repo->config('interactive.difffilter');\n-\n-my $use_readkey = 0;\n-my $use_termcap = 0;\n-my %term_escapes;\n-\n-sub ReadMode;\n-sub ReadKey;\n-if ($repo->config_bool(\"interactive.singlekey\")) {\n-\teval {\n-\t\trequire Term::ReadKey;\n-\t\tTerm::ReadKey->import;\n-\t\t$use_readkey = 1;\n-\t};\n-\tif (!$use_readkey) {\n-\t\tprint STDERR \"missing Term::ReadKey, disabling interactive.singlekey\\n\";\n-\t}\n-\teval {\n-\t\trequire Term::Cap;\n-\t\tmy $termcap = Term::Cap->Tgetent;\n-\t\tforeach (values %$termcap) {\n-\t\t\t$term_escapes{$_} = 1 if /^\\e/;\n-\t\t}\n-\t\t$use_termcap = 1;\n-\t};\n-}\n-\n-sub colored {\n-\tmy $color = shift;\n-\tmy $string = join(\"\", @_);\n-\n-\tif (defined $color) {\n-\t\t# Put a color code at the beginning of each line, a reset at the end\n-\t\t# color after newlines that are not at the end of the string\n-\t\t$string =~ s/(\\n+)(.)/$1$color$2/g;\n-\t\t# reset before newlines\n-\t\t$string =~ s/(\\n+)/$normal_color$1/g;\n-\t\t# codes at beginning and end (if necessary):\n-\t\t$string =~ s/^/$color/;\n-\t\t$string =~ s/$/$normal_color/ unless $string =~ /\\n$/;\n-\t}\n-\treturn $string;\n-}\n-\n-# command line options\n-my $patch_mode_only;\n-my $patch_mode;\n-my $patch_mode_revision;\n-\n-sub apply_patch;\n-sub apply_patch_for_checkout_commit;\n-sub apply_patch_for_stash;\n-\n-my %patch_modes = (\n-\t'stage' => {\n-\t\tDIFF => 'diff-files -p',\n-\t\tAPPLY => sub { apply_patch 'apply --cached', @_; },\n-\t\tAPPLY_CHECK => 'apply --cached',\n-\t\tFILTER => 'file-only',\n-\t\tIS_REVERSE => 0,\n-\t},\n-\t'stash' => {\n-\t\tDIFF => 'diff-index -p HEAD',\n-\t\tAPPLY => sub { apply_patch 'apply --cached', @_; },\n-\t\tAPPLY_CHECK => 'apply --cached',\n-\t\tFILTER => undef,\n-\t\tIS_REVERSE => 0,\n-\t},\n-\t'reset_head' => {\n-\t\tDIFF => 'diff-index -p --cached',\n-\t\tAPPLY => sub { apply_patch 'apply -R --cached', @_; },\n-\t\tAPPLY_CHECK => 'apply -R --cached',\n-\t\tFILTER => 'index-only',\n-\t\tIS_REVERSE => 1,\n-\t},\n-\t'reset_nothead' => {\n-\t\tDIFF => 'diff-index -R -p --cached',\n-\t\tAPPLY => sub { apply_patch 'apply --cached', @_; },\n-\t\tAPPLY_CHECK => 'apply --cached',\n-\t\tFILTER => 'index-only',\n-\t\tIS_REVERSE => 0,\n-\t},\n-\t'checkout_index' => {\n-\t\tDIFF => 'diff-files -p',\n-\t\tAPPLY => sub { apply_patch 'apply -R', @_; },\n-\t\tAPPLY_CHECK => 'apply -R',\n-\t\tFILTER => 'file-only',\n-\t\tIS_REVERSE => 1,\n-\t},\n-\t'checkout_head' => {\n-\t\tDIFF => 'diff-index -p',\n-\t\tAPPLY => sub { apply_patch_for_checkout_commit '-R', @_ },\n-\t\tAPPLY_CHECK => 'apply -R',\n-\t\tFILTER => undef,\n-\t\tIS_REVERSE => 1,\n-\t},\n-\t'checkout_nothead' => {\n-\t\tDIFF => 'diff-index -R -p',\n-\t\tAPPLY => sub { apply_patch_for_checkout_commit '', @_ },\n-\t\tAPPLY_CHECK => 'apply',\n-\t\tFILTER => undef,\n-\t\tIS_REVERSE => 0,\n-\t},\n-\t'worktree_head' => {\n-\t\tDIFF => 'diff-index -p',\n-\t\tAPPLY => sub { apply_patch 'apply -R', @_ },\n-\t\tAPPLY_CHECK => 'apply -R',\n-\t\tFILTER => undef,\n-\t\tIS_REVERSE => 1,\n-\t},\n-\t'worktree_nothead' => {\n-\t\tDIFF => 'diff-index -R -p',\n-\t\tAPPLY => sub { apply_patch 'apply', @_ },\n-\t\tAPPLY_CHECK => 'apply',\n-\t\tFILTER => undef,\n-\t\tIS_REVERSE => 0,\n-\t},\n-);\n-\n-$patch_mode = 'stage';\n-my %patch_mode_flavour = %{$patch_modes{$patch_mode}};\n-\n-sub run_cmd_pipe {\n-\tif ($^O eq 'MSWin32') {\n-\t\tmy @invalid = grep {m/[\":*]/} @_;\n-\t\tdie \"$^O does not support: @invalid\\n\" if @invalid;\n-\t\tmy @args = map { m/ /o ? \"\\\"$_\\\"\": $_ } @_;\n-\t\treturn qx{@args};\n-\t} else {\n-\t\tmy $fh = undef;\n-\t\topen($fh, '-|', @_) or die;\n-\t\tmy @out = <$fh>;\n-\t\tclose $fh || die \"Cannot close @_ ($!)\";\n-\t\treturn @out;\n-\t}\n-}\n-\n-my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));\n-\n-if (!defined $GIT_DIR) {\n-\texit(1); # rev-parse would have already said \"not a git repo\"\n-}\n-chomp($GIT_DIR);\n-\n-sub refresh {\n-\tmy $fh;\n-\topen $fh, 'git update-index --refresh |'\n-\t    or die;\n-\twhile (<$fh>) {\n-\t\t;# ignore 'needs update'\n-\t}\n-\tclose $fh;\n-}\n-\n-sub list_untracked {\n-\tmap {\n-\t\tchomp $_;\n-\t\tunquote_path($_);\n-\t}\n-\trun_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);\n-}\n-\n-# TRANSLATORS: you can adjust this to align \"git add -i\" status menu\n-my $status_fmt = __('%12s %12s %s');\n-my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), __('path'));\n-\n-{\n-\tmy $initial;\n-\tsub is_initial_commit {\n-\t\t$initial = system('git rev-parse HEAD -- >/dev/null 2>&1') != 0\n-\t\t\tunless defined $initial;\n-\t\treturn $initial;\n-\t}\n-}\n-\n-{\n-\tmy $empty_tree;\n-\tsub get_empty_tree {\n-\t\treturn $empty_tree if defined $empty_tree;\n-\n-\t\t($empty_tree) = run_cmd_pipe(qw(git hash-object -t tree /dev/null));\n-\t\tchomp $empty_tree;\n-\t\treturn $empty_tree;\n-\t}\n-}\n-\n-sub get_diff_reference {\n-\tmy $ref = shift;\n-\tif (defined $ref and $ref ne 'HEAD') {\n-\t\treturn $ref;\n-\t} elsif (is_initial_commit()) {\n-\t\treturn get_empty_tree();\n-\t} else {\n-\t\treturn 'HEAD';\n-\t}\n-}\n-\n-# Returns list of hashes, contents of each of which are:\n-# VALUE:\tpathname\n-# BINARY:\tis a binary path\n-# INDEX:\tis index different from HEAD?\n-# FILE:\t\tis file different from index?\n-# INDEX_ADDDEL:\tis it add/delete between HEAD and index?\n-# FILE_ADDDEL:\tis it add/delete between index and file?\n-# UNMERGED:\tis the path unmerged\n-\n-sub list_modified {\n-\tmy ($only) = @_;\n-\tmy (%data, @return);\n-\tmy ($add, $del, $adddel, $file);\n-\n-\tmy $reference = get_diff_reference($patch_mode_revision);\n-\tfor (run_cmd_pipe(qw(git diff-index --cached\n-\t\t\t     --numstat --summary), $reference,\n-\t\t\t     '--', @ARGV)) {\n-\t\tif (($add, $del, $file) =\n-\t\t    /^([-\\d]+)\t([-\\d]+)\t(.*)/) {\n-\t\t\tmy ($change, $bin);\n-\t\t\t$file = unquote_path($file);\n-\t\t\tif ($add eq '-' && $del eq '-') {\n-\t\t\t\t$change = __('binary');\n-\t\t\t\t$bin = 1;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$change = \"+$add/-$del\";\n-\t\t\t}\n-\t\t\t$data{$file} = {\n-\t\t\t\tINDEX => $change,\n-\t\t\t\tBINARY => $bin,\n-\t\t\t\tFILE => __('nothing'),\n-\t\t\t}\n-\t\t}\n-\t\telsif (($adddel, $file) =\n-\t\t       /^ (create|delete) mode [0-7]+ (.*)$/) {\n-\t\t\t$file = unquote_path($file);\n-\t\t\t$data{$file}{INDEX_ADDDEL} = $adddel;\n-\t\t}\n-\t}\n-\n-\tfor (run_cmd_pipe(qw(git diff-files --ignore-submodules=dirty --numstat --summary --raw --), @ARGV)) {\n-\t\tif (($add, $del, $file) =\n-\t\t    /^([-\\d]+)\t([-\\d]+)\t(.*)/) {\n-\t\t\t$file = unquote_path($file);\n-\t\t\tmy ($change, $bin);\n-\t\t\tif ($add eq '-' && $del eq '-') {\n-\t\t\t\t$change = __('binary');\n-\t\t\t\t$bin = 1;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$change = \"+$add/-$del\";\n-\t\t\t}\n-\t\t\t$data{$file}{FILE} = $change;\n-\t\t\tif ($bin) {\n-\t\t\t\t$data{$file}{BINARY} = 1;\n-\t\t\t}\n-\t\t}\n-\t\telsif (($adddel, $file) =\n-\t\t       /^ (create|delete) mode [0-7]+ (.*)$/) {\n-\t\t\t$file = unquote_path($file);\n-\t\t\t$data{$file}{FILE_ADDDEL} = $adddel;\n-\t\t}\n-\t\telsif (/^:[0-7]+ [0-7]+ [0-9a-f]+ [0-9a-f]+ (.)\t(.*)$/) {\n-\t\t\t$file = unquote_path($2);\n-\t\t\tif (!exists $data{$file}) {\n-\t\t\t\t$data{$file} = +{\n-\t\t\t\t\tINDEX => __('unchanged'),\n-\t\t\t\t\tBINARY => 0,\n-\t\t\t\t};\n-\t\t\t}\n-\t\t\tif ($1 eq 'U') {\n-\t\t\t\t$data{$file}{UNMERGED} = 1;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\tfor (sort keys %data) {\n-\t\tmy $it = $data{$_};\n-\n-\t\tif ($only) {\n-\t\t\tif ($only eq 'index-only') {\n-\t\t\t\tnext if ($it->{INDEX} eq __('unchanged'));\n-\t\t\t}\n-\t\t\tif ($only eq 'file-only') {\n-\t\t\t\tnext if ($it->{FILE} eq __('nothing'));\n-\t\t\t}\n-\t\t}\n-\t\tpush @return, +{\n-\t\t\tVALUE => $_,\n-\t\t\t%$it,\n-\t\t};\n-\t}\n-\treturn @return;\n-}\n-\n-sub find_unique {\n-\tmy ($string, @stuff) = @_;\n-\tmy $found = undef;\n-\tfor (my $i = 0; $i < @stuff; $i++) {\n-\t\tmy $it = $stuff[$i];\n-\t\tmy $hit = undef;\n-\t\tif (ref $it) {\n-\t\t\tif ((ref $it) eq 'ARRAY') {\n-\t\t\t\t$it = $it->[0];\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$it = $it->{VALUE};\n-\t\t\t}\n-\t\t}\n-\t\teval {\n-\t\t\tif ($it =~ /^$string/) {\n-\t\t\t\t$hit = 1;\n-\t\t\t};\n-\t\t};\n-\t\tif (defined $hit && defined $found) {\n-\t\t\treturn undef;\n-\t\t}\n-\t\tif ($hit) {\n-\t\t\t$found = $i + 1;\n-\t\t}\n-\t}\n-\treturn $found;\n-}\n-\n-# inserts string into trie and updates count for each character\n-sub update_trie {\n-\tmy ($trie, $string) = @_;\n-\tforeach (split //, $string) {\n-\t\t$trie = $trie->{$_} ||= {COUNT => 0};\n-\t\t$trie->{COUNT}++;\n-\t}\n-}\n-\n-# returns an array of tuples (prefix, remainder)\n-sub find_unique_prefixes {\n-\tmy @stuff = @_;\n-\tmy @return = ();\n-\n-\t# any single prefix exceeding the soft limit is omitted\n-\t# if any prefix exceeds the hard limit all are omitted\n-\t# 0 indicates no limit\n-\tmy $soft_limit = 0;\n-\tmy $hard_limit = 3;\n-\n-\t# build a trie modelling all possible options\n-\tmy %trie;\n-\tforeach my $print (@stuff) {\n-\t\tif ((ref $print) eq 'ARRAY') {\n-\t\t\t$print = $print->[0];\n-\t\t}\n-\t\telsif ((ref $print) eq 'HASH') {\n-\t\t\t$print = $print->{VALUE};\n-\t\t}\n-\t\tupdate_trie(\\%trie, $print);\n-\t\tpush @return, $print;\n-\t}\n-\n-\t# use the trie to find the unique prefixes\n-\tfor (my $i = 0; $i < @return; $i++) {\n-\t\tmy $ret = $return[$i];\n-\t\tmy @letters = split //, $ret;\n-\t\tmy %search = %trie;\n-\t\tmy ($prefix, $remainder);\n-\t\tmy $j;\n-\t\tfor ($j = 0; $j < @letters; $j++) {\n-\t\t\tmy $letter = $letters[$j];\n-\t\t\tif ($search{$letter}{COUNT} == 1) {\n-\t\t\t\t$prefix = substr $ret, 0, $j + 1;\n-\t\t\t\t$remainder = substr $ret, $j + 1;\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\tmy $prefix = substr $ret, 0, $j;\n-\t\t\t\treturn ()\n-\t\t\t\t    if ($hard_limit && $j + 1 > $hard_limit);\n-\t\t\t}\n-\t\t\t%search = %{$search{$letter}};\n-\t\t}\n-\t\tif (ord($letters[0]) > 127 ||\n-\t\t    ($soft_limit && $j + 1 > $soft_limit)) {\n-\t\t\t$prefix = undef;\n-\t\t\t$remainder = $ret;\n-\t\t}\n-\t\t$return[$i] = [$prefix, $remainder];\n-\t}\n-\treturn @return;\n-}\n-\n-# filters out prefixes which have special meaning to list_and_choose()\n-sub is_valid_prefix {\n-\tmy $prefix = shift;\n-\treturn (defined $prefix) &&\n-\t    !($prefix =~ /[\\s,]/) && # separators\n-\t    !($prefix =~ /^-/) &&    # deselection\n-\t    !($prefix =~ /^\\d+/) &&  # selection\n-\t    ($prefix ne '*') &&      # \"all\" wildcard\n-\t    ($prefix ne '?');        # prompt help\n-}\n-\n-# given a prefix/remainder tuple return a string with the prefix highlighted\n-# for now use square brackets; later might use ANSI colors (underline, bold)\n-sub highlight_prefix {\n-\tmy $prefix = shift;\n-\tmy $remainder = shift;\n-\n-\tif (!defined $prefix) {\n-\t\treturn $remainder;\n-\t}\n-\n-\tif (!is_valid_prefix($prefix)) {\n-\t\treturn \"$prefix$remainder\";\n-\t}\n-\n-\tif (!$menu_use_color) {\n-\t\treturn \"[$prefix]$remainder\";\n-\t}\n-\n-\treturn \"$prompt_color$prefix$normal_color$remainder\";\n-}\n-\n-sub error_msg {\n-\tprint STDERR colored $error_color, @_;\n-}\n-\n-sub list_and_choose {\n-\tmy ($opts, @stuff) = @_;\n-\tmy (@chosen, @return);\n-\tif (!@stuff) {\n-\t    return @return;\n-\t}\n-\tmy $i;\n-\tmy @prefixes = find_unique_prefixes(@stuff) unless $opts->{LIST_ONLY};\n-\n-      TOPLOOP:\n-\twhile (1) {\n-\t\tmy $last_lf = 0;\n-\n-\t\tif ($opts->{HEADER}) {\n-\t\t\tmy $indent = $opts->{LIST_FLAT} ? \"\" : \"     \";\n-\t\t\tprint colored $header_color, \"$indent$opts->{HEADER}\\n\";\n-\t\t}\n-\t\tfor ($i = 0; $i < @stuff; $i++) {\n-\t\t\tmy $chosen = $chosen[$i] ? '*' : ' ';\n-\t\t\tmy $print = $stuff[$i];\n-\t\t\tmy $ref = ref $print;\n-\t\t\tmy $highlighted = highlight_prefix(@{$prefixes[$i]})\n-\t\t\t    if @prefixes;\n-\t\t\tif ($ref eq 'ARRAY') {\n-\t\t\t\t$print = $highlighted || $print->[0];\n-\t\t\t}\n-\t\t\telsif ($ref eq 'HASH') {\n-\t\t\t\tmy $value = $highlighted || $print->{VALUE};\n-\t\t\t\t$print = sprintf($status_fmt,\n-\t\t\t\t    $print->{INDEX},\n-\t\t\t\t    $print->{FILE},\n-\t\t\t\t    $value);\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$print = $highlighted || $print;\n-\t\t\t}\n-\t\t\tprintf(\"%s%2d: %s\", $chosen, $i+1, $print);\n-\t\t\tif (($opts->{LIST_FLAT}) &&\n-\t\t\t    (($i + 1) % ($opts->{LIST_FLAT}))) {\n-\t\t\t\tprint \"\\t\";\n-\t\t\t\t$last_lf = 0;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\tprint \"\\n\";\n-\t\t\t\t$last_lf = 1;\n-\t\t\t}\n-\t\t}\n-\t\tif (!$last_lf) {\n-\t\t\tprint \"\\n\";\n-\t\t}\n-\n-\t\treturn if ($opts->{LIST_ONLY});\n-\n-\t\tprint colored $prompt_color, $opts->{PROMPT};\n-\t\tif ($opts->{SINGLETON}) {\n-\t\t\tprint \"> \";\n-\t\t}\n-\t\telse {\n-\t\t\tprint \">> \";\n-\t\t}\n-\t\tmy $line = <STDIN>;\n-\t\tif (!$line) {\n-\t\t\tprint \"\\n\";\n-\t\t\t$opts->{ON_EOF}->() if $opts->{ON_EOF};\n-\t\t\tlast;\n-\t\t}\n-\t\tchomp $line;\n-\t\tlast if $line eq '';\n-\t\tif ($line eq '?') {\n-\t\t\t$opts->{SINGLETON} ?\n-\t\t\t    singleton_prompt_help_cmd() :\n-\t\t\t    prompt_help_cmd();\n-\t\t\tnext TOPLOOP;\n-\t\t}\n-\t\tfor my $choice (split(/[\\s,]+/, $line)) {\n-\t\t\tmy $choose = 1;\n-\t\t\tmy ($bottom, $top);\n-\n-\t\t\t# Input that begins with '-'; unchoose\n-\t\t\tif ($choice =~ s/^-//) {\n-\t\t\t\t$choose = 0;\n-\t\t\t}\n-\t\t\t# A range can be specified like 5-7 or 5-.\n-\t\t\tif ($choice =~ /^(\\d+)-(\\d*)$/) {\n-\t\t\t\t($bottom, $top) = ($1, length($2) ? $2 : 1 + @stuff);\n-\t\t\t}\n-\t\t\telsif ($choice =~ /^\\d+$/) {\n-\t\t\t\t$bottom = $top = $choice;\n-\t\t\t}\n-\t\t\telsif ($choice eq '*') {\n-\t\t\t\t$bottom = 1;\n-\t\t\t\t$top = 1 + @stuff;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$bottom = $top = find_unique($choice, @stuff);\n-\t\t\t\tif (!defined $bottom) {\n-\t\t\t\t\terror_msg sprintf(__(\"Huh (%s)?\\n\"), $choice);\n-\t\t\t\t\tnext TOPLOOP;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tif ($opts->{SINGLETON} && $bottom != $top) {\n-\t\t\t\terror_msg sprintf(__(\"Huh (%s)?\\n\"), $choice);\n-\t\t\t\tnext TOPLOOP;\n-\t\t\t}\n-\t\t\tfor ($i = $bottom-1; $i <= $top-1; $i++) {\n-\t\t\t\tnext if (@stuff <= $i || $i < 0);\n-\t\t\t\t$chosen[$i] = $choose;\n-\t\t\t}\n-\t\t}\n-\t\tlast if ($opts->{IMMEDIATE} || $line eq '*');\n-\t}\n-\tfor ($i = 0; $i < @stuff; $i++) {\n-\t\tif ($chosen[$i]) {\n-\t\t\tpush @return, $stuff[$i];\n-\t\t}\n-\t}\n-\treturn @return;\n-}\n-\n-sub singleton_prompt_help_cmd {\n-\tprint colored $help_color, __ <<'EOF' ;\n-Prompt help:\n-1          - select a numbered item\n-foo        - select item based on unique prefix\n-           - (empty) select nothing\n-EOF\n-}\n-\n-sub prompt_help_cmd {\n-\tprint colored $help_color, __ <<'EOF' ;\n-Prompt help:\n-1          - select a single item\n-3-5        - select a range of items\n-2-3,6-9    - select multiple ranges\n-foo        - select item based on unique prefix\n--...       - unselect specified items\n-*          - choose all items\n-           - (empty) finish selecting\n-EOF\n-}\n-\n-sub status_cmd {\n-\tlist_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n-\t\t\tlist_modified());\n-\tprint \"\\n\";\n-}\n-\n-sub say_n_paths {\n-\tmy $did = shift @_;\n-\tmy $cnt = scalar @_;\n-\tif ($did eq 'added') {\n-\t\tprintf(__n(\"added %d path\\n\", \"added %d paths\\n\",\n-\t\t\t   $cnt), $cnt);\n-\t} elsif ($did eq 'updated') {\n-\t\tprintf(__n(\"updated %d path\\n\", \"updated %d paths\\n\",\n-\t\t\t   $cnt), $cnt);\n-\t} elsif ($did eq 'reverted') {\n-\t\tprintf(__n(\"reverted %d path\\n\", \"reverted %d paths\\n\",\n-\t\t\t   $cnt), $cnt);\n-\t} else {\n-\t\tprintf(__n(\"touched %d path\\n\", \"touched %d paths\\n\",\n-\t\t\t   $cnt), $cnt);\n-\t}\n-}\n-\n-sub update_cmd {\n-\tmy @mods = list_modified('file-only');\n-\treturn if (!@mods);\n-\n-\tmy @update = list_and_choose({ PROMPT => __('Update'),\n-\t\t\t\t       HEADER => $status_head, },\n-\t\t\t\t     @mods);\n-\tif (@update) {\n-\t\tsystem(qw(git update-index --add --remove --),\n-\t\t       map { $_->{VALUE} } @update);\n-\t\tsay_n_paths('updated', @update);\n-\t}\n-\tprint \"\\n\";\n-}\n-\n-sub revert_cmd {\n-\tmy @update = list_and_choose({ PROMPT => __('Revert'),\n-\t\t\t\t       HEADER => $status_head, },\n-\t\t\t\t     list_modified());\n-\tif (@update) {\n-\t\tif (is_initial_commit()) {\n-\t\t\tsystem(qw(git rm --cached),\n-\t\t\t\tmap { $_->{VALUE} } @update);\n-\t\t}\n-\t\telse {\n-\t\t\tmy @lines = run_cmd_pipe(qw(git ls-tree HEAD --),\n-\t\t\t\t\t\t map { $_->{VALUE} } @update);\n-\t\t\tmy $fh;\n-\t\t\topen $fh, '| git update-index --index-info'\n-\t\t\t    or die;\n-\t\t\tfor (@lines) {\n-\t\t\t\tprint $fh $_;\n-\t\t\t}\n-\t\t\tclose($fh);\n-\t\t\tfor (@update) {\n-\t\t\t\tif ($_->{INDEX_ADDDEL} &&\n-\t\t\t\t    $_->{INDEX_ADDDEL} eq 'create') {\n-\t\t\t\t\tsystem(qw(git update-index --force-remove --),\n-\t\t\t\t\t       $_->{VALUE});\n-\t\t\t\t\tprintf(__(\"note: %s is untracked now.\\n\"), $_->{VALUE});\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\t\trefresh();\n-\t\tsay_n_paths('reverted', @update);\n-\t}\n-\tprint \"\\n\";\n-}\n-\n-sub add_untracked_cmd {\n-\tmy @add = list_and_choose({ PROMPT => __('Add untracked') },\n-\t\t\t\t  list_untracked());\n-\tif (@add) {\n-\t\tsystem(qw(git update-index --add --), @add);\n-\t\tsay_n_paths('added', @add);\n-\t} else {\n-\t\tprint __(\"No untracked files.\\n\");\n-\t}\n-\tprint \"\\n\";\n-}\n-\n-sub run_git_apply {\n-\tmy $cmd = shift;\n-\tmy $fh;\n-\topen $fh, '| git ' . $cmd . \" --allow-overlap\";\n-\tprint $fh @_;\n-\treturn close $fh;\n-}\n-\n-sub parse_diff {\n-\tmy ($path) = @_;\n-\tmy @diff_cmd = split(\" \", $patch_mode_flavour{DIFF});\n-\tif (defined $diff_algorithm) {\n-\t\tsplice @diff_cmd, 1, 0, \"--diff-algorithm=${diff_algorithm}\";\n-\t}\n-\tif (defined $patch_mode_revision) {\n-\t\tpush @diff_cmd, get_diff_reference($patch_mode_revision);\n-\t}\n-\tmy @diff = run_cmd_pipe(\"git\", @diff_cmd, qw(--no-color --), $path);\n-\tmy @colored = ();\n-\tif ($diff_use_color) {\n-\t\tmy @display_cmd = (\"git\", @diff_cmd, qw(--color --), $path);\n-\t\tif (defined $diff_filter) {\n-\t\t\t# quotemeta is overkill, but sufficient for shell-quoting\n-\t\t\tmy $diff = join(' ', map { quotemeta } @display_cmd);\n-\t\t\t@display_cmd = (\"$diff | $diff_filter\");\n-\t\t}\n-\n-\t\t@colored = run_cmd_pipe(@display_cmd);\n-\t}\n-\tmy (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };\n-\n-\tif (@colored && @colored != @diff) {\n-\t\tprint STDERR\n-\t\t  \"fatal: mismatched output from interactive.diffFilter\\n\",\n-\t\t  \"hint: Your filter must maintain a one-to-one correspondence\\n\",\n-\t\t  \"hint: between its input and output lines.\\n\";\n-\t\texit 1;\n-\t}\n-\n-\tfor (my $i = 0; $i < @diff; $i++) {\n-\t\tif ($diff[$i] =~ /^@@ /) {\n-\t\t\tpush @hunk, { TEXT => [], DISPLAY => [],\n-\t\t\t\tTYPE => 'hunk' };\n-\t\t}\n-\t\tpush @{$hunk[-1]{TEXT}}, $diff[$i];\n-\t\tpush @{$hunk[-1]{DISPLAY}},\n-\t\t\t(@colored ? $colored[$i] : $diff[$i]);\n-\t}\n-\treturn @hunk;\n-}\n-\n-sub parse_diff_header {\n-\tmy $src = shift;\n-\n-\tmy $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };\n-\tmy $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };\n-\tmy $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };\n-\tmy $addition;\n-\n-\tfor (my $i = 0; $i < @{$src->{TEXT}}; $i++) {\n-\t\tif ($src->{TEXT}->[$i] =~ /^new file/) {\n-\t\t\t$addition = 1;\n-\t\t\t$head->{TYPE} = 'addition';\n-\t\t}\n-\t\tmy $dest =\n-\t\t   $src->{TEXT}->[$i] =~ /^(old|new) mode (\\d+)$/ ? $mode :\n-\t\t   $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :\n-\t\t   $head;\n-\t\tpush @{$dest->{TEXT}}, $src->{TEXT}->[$i];\n-\t\tpush @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];\n-\t}\n-\treturn ($head, $mode, $deletion, $addition);\n-}\n-\n-sub hunk_splittable {\n-\tmy ($text) = @_;\n-\n-\tmy @s = split_hunk($text);\n-\treturn (1 < @s);\n-}\n-\n-sub parse_hunk_header {\n-\tmy ($line) = @_;\n-\tmy ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =\n-\t    $line =~ /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@/;\n-\t$o_cnt = 1 unless defined $o_cnt;\n-\t$n_cnt = 1 unless defined $n_cnt;\n-\treturn ($o_ofs, $o_cnt, $n_ofs, $n_cnt);\n-}\n-\n-sub format_hunk_header {\n-\tmy ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = @_;\n-\treturn (\"@@ -$o_ofs\" .\n-\t\t(($o_cnt != 1) ? \",$o_cnt\" : '') .\n-\t\t\" +$n_ofs\" .\n-\t\t(($n_cnt != 1) ? \",$n_cnt\" : '') .\n-\t\t\" @@\\n\");\n-}\n-\n-sub split_hunk {\n-\tmy ($text, $display) = @_;\n-\tmy @split = ();\n-\tif (!defined $display) {\n-\t\t$display = $text;\n-\t}\n-\t# If there are context lines in the middle of a hunk,\n-\t# it can be split, but we would need to take care of\n-\t# overlaps later.\n-\n-\tmy ($o_ofs, undef, $n_ofs) = parse_hunk_header($text->[0]);\n-\tmy $hunk_start = 1;\n-\n-      OUTER:\n-\twhile (1) {\n-\t\tmy $next_hunk_start = undef;\n-\t\tmy $i = $hunk_start - 1;\n-\t\tmy $this = +{\n-\t\t\tTEXT => [],\n-\t\t\tDISPLAY => [],\n-\t\t\tTYPE => 'hunk',\n-\t\t\tOLD => $o_ofs,\n-\t\t\tNEW => $n_ofs,\n-\t\t\tOCNT => 0,\n-\t\t\tNCNT => 0,\n-\t\t\tADDDEL => 0,\n-\t\t\tPOSTCTX => 0,\n-\t\t\tUSE => undef,\n-\t\t};\n-\n-\t\twhile (++$i < @$text) {\n-\t\t\tmy $line = $text->[$i];\n-\t\t\tmy $display = $display->[$i];\n-\t\t\tif ($line =~ /^\\\\/) {\n-\t\t\t\tpush @{$this->{TEXT}}, $line;\n-\t\t\t\tpush @{$this->{DISPLAY}}, $display;\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\tif ($line =~ /^ /) {\n-\t\t\t\tif ($this->{ADDDEL} &&\n-\t\t\t\t    !defined $next_hunk_start) {\n-\t\t\t\t\t# We have seen leading context and\n-\t\t\t\t\t# adds/dels and then here is another\n-\t\t\t\t\t# context, which is trailing for this\n-\t\t\t\t\t# split hunk and leading for the next\n-\t\t\t\t\t# one.\n-\t\t\t\t\t$next_hunk_start = $i;\n-\t\t\t\t}\n-\t\t\t\tpush @{$this->{TEXT}}, $line;\n-\t\t\t\tpush @{$this->{DISPLAY}}, $display;\n-\t\t\t\t$this->{OCNT}++;\n-\t\t\t\t$this->{NCNT}++;\n-\t\t\t\tif (defined $next_hunk_start) {\n-\t\t\t\t\t$this->{POSTCTX}++;\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\n-\t\t\t# add/del\n-\t\t\tif (defined $next_hunk_start) {\n-\t\t\t\t# We are done with the current hunk and\n-\t\t\t\t# this is the first real change for the\n-\t\t\t\t# next split one.\n-\t\t\t\t$hunk_start = $next_hunk_start;\n-\t\t\t\t$o_ofs = $this->{OLD} + $this->{OCNT};\n-\t\t\t\t$n_ofs = $this->{NEW} + $this->{NCNT};\n-\t\t\t\t$o_ofs -= $this->{POSTCTX};\n-\t\t\t\t$n_ofs -= $this->{POSTCTX};\n-\t\t\t\tpush @split, $this;\n-\t\t\t\tredo OUTER;\n-\t\t\t}\n-\t\t\tpush @{$this->{TEXT}}, $line;\n-\t\t\tpush @{$this->{DISPLAY}}, $display;\n-\t\t\t$this->{ADDDEL}++;\n-\t\t\tif ($line =~ /^-/) {\n-\t\t\t\t$this->{OCNT}++;\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\t$this->{NCNT}++;\n-\t\t\t}\n-\t\t}\n-\n-\t\tpush @split, $this;\n-\t\tlast;\n-\t}\n-\n-\tfor my $hunk (@split) {\n-\t\t$o_ofs = $hunk->{OLD};\n-\t\t$n_ofs = $hunk->{NEW};\n-\t\tmy $o_cnt = $hunk->{OCNT};\n-\t\tmy $n_cnt = $hunk->{NCNT};\n-\n-\t\tmy $head = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);\n-\t\tmy $display_head = $head;\n-\t\tunshift @{$hunk->{TEXT}}, $head;\n-\t\tif ($diff_use_color) {\n-\t\t\t$display_head = colored($fraginfo_color, $head);\n-\t\t}\n-\t\tunshift @{$hunk->{DISPLAY}}, $display_head;\n-\t}\n-\treturn @split;\n-}\n-\n-sub find_last_o_ctx {\n-\tmy ($it) = @_;\n-\tmy $text = $it->{TEXT};\n-\tmy ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]);\n-\tmy $i = @{$text};\n-\tmy $last_o_ctx = $o_ofs + $o_cnt;\n-\twhile (0 < --$i) {\n-\t\tmy $line = $text->[$i];\n-\t\tif ($line =~ /^ /) {\n-\t\t\t$last_o_ctx--;\n-\t\t\tnext;\n-\t\t}\n-\t\tlast;\n-\t}\n-\treturn $last_o_ctx;\n-}\n-\n-sub merge_hunk {\n-\tmy ($prev, $this) = @_;\n-\tmy ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) =\n-\t    parse_hunk_header($prev->{TEXT}[0]);\n-\tmy ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) =\n-\t    parse_hunk_header($this->{TEXT}[0]);\n-\n-\tmy (@line, $i, $ofs, $o_cnt, $n_cnt);\n-\t$ofs = $o0_ofs;\n-\t$o_cnt = $n_cnt = 0;\n-\tfor ($i = 1; $i < @{$prev->{TEXT}}; $i++) {\n-\t\tmy $line = $prev->{TEXT}[$i];\n-\t\tif ($line =~ /^\\+/) {\n-\t\t\t$n_cnt++;\n-\t\t\tpush @line, $line;\n-\t\t\tnext;\n-\t\t} elsif ($line =~ /^\\\\/) {\n-\t\t\tpush @line, $line;\n-\t\t\tnext;\n-\t\t}\n-\n-\t\tlast if ($o1_ofs <= $ofs);\n-\n-\t\t$o_cnt++;\n-\t\t$ofs++;\n-\t\tif ($line =~ /^ /) {\n-\t\t\t$n_cnt++;\n-\t\t}\n-\t\tpush @line, $line;\n-\t}\n-\n-\tfor ($i = 1; $i < @{$this->{TEXT}}; $i++) {\n-\t\tmy $line = $this->{TEXT}[$i];\n-\t\tif ($line =~ /^\\+/) {\n-\t\t\t$n_cnt++;\n-\t\t\tpush @line, $line;\n-\t\t\tnext;\n-\t\t} elsif ($line =~ /^\\\\/) {\n-\t\t\tpush @line, $line;\n-\t\t\tnext;\n-\t\t}\n-\t\t$ofs++;\n-\t\t$o_cnt++;\n-\t\tif ($line =~ /^ /) {\n-\t\t\t$n_cnt++;\n-\t\t}\n-\t\tpush @line, $line;\n-\t}\n-\tmy $head = format_hunk_header($o0_ofs, $o_cnt, $n0_ofs, $n_cnt);\n-\t@{$prev->{TEXT}} = ($head, @line);\n-}\n-\n-sub coalesce_overlapping_hunks {\n-\tmy (@in) = @_;\n-\tmy @out = ();\n-\n-\tmy ($last_o_ctx, $last_was_dirty);\n-\tmy $ofs_delta = 0;\n-\n-\tfor (@in) {\n-\t\tif ($_->{TYPE} ne 'hunk') {\n-\t\t\tpush @out, $_;\n-\t\t\tnext;\n-\t\t}\n-\t\tmy $text = $_->{TEXT};\n-\t\tmy ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =\n-\t\t\t\t\t\tparse_hunk_header($text->[0]);\n-\t\tunless ($_->{USE}) {\n-\t\t\t$ofs_delta += $o_cnt - $n_cnt;\n-\t\t\t# If this hunk has been edited then subtract\n-\t\t\t# the delta that is due to the edit.\n-\t\t\tif ($_->{OFS_DELTA}) {\n-\t\t\t\t$ofs_delta -= $_->{OFS_DELTA};\n-\t\t\t}\n-\t\t\tnext;\n-\t\t}\n-\t\tif ($ofs_delta) {\n-\t\t\tif ($patch_mode_flavour{IS_REVERSE}) {\n-\t\t\t\t$o_ofs -= $ofs_delta;\n-\t\t\t} else {\n-\t\t\t\t$n_ofs += $ofs_delta;\n-\t\t\t}\n-\t\t\t$_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,\n-\t\t\t\t\t\t\t     $n_ofs, $n_cnt);\n-\t\t}\n-\t\t# If this hunk was edited then adjust the offset delta\n-\t\t# to reflect the edit.\n-\t\tif ($_->{OFS_DELTA}) {\n-\t\t\t$ofs_delta += $_->{OFS_DELTA};\n-\t\t}\n-\t\tif (defined $last_o_ctx &&\n-\t\t    $o_ofs <= $last_o_ctx &&\n-\t\t    !$_->{DIRTY} &&\n-\t\t    !$last_was_dirty) {\n-\t\t\tmerge_hunk($out[-1], $_);\n-\t\t}\n-\t\telse {\n-\t\t\tpush @out, $_;\n-\t\t}\n-\t\t$last_o_ctx = find_last_o_ctx($out[-1]);\n-\t\t$last_was_dirty = $_->{DIRTY};\n-\t}\n-\treturn @out;\n-}\n-\n-sub reassemble_patch {\n-\tmy $head = shift;\n-\tmy @patch;\n-\n-\t# Include everything in the header except the beginning of the diff.\n-\tpush @patch, (grep { !/^[-+]{3}/ } @$head);\n-\n-\t# Then include any headers from the hunk lines, which must\n-\t# come before any actual hunk.\n-\twhile (@_ && $_[0] !~ /^@/) {\n-\t\tpush @patch, shift;\n-\t}\n-\n-\t# Then begin the diff.\n-\tpush @patch, grep { /^[-+]{3}/ } @$head;\n-\n-\t# And then the actual hunks.\n-\tpush @patch, @_;\n-\n-\treturn @patch;\n-}\n-\n-sub color_diff {\n-\treturn map {\n-\t\tcolored((/^@/  ? $fraginfo_color :\n-\t\t\t /^\\+/ ? $diff_new_color :\n-\t\t\t /^-/  ? $diff_old_color :\n-\t\t\t $diff_context_color),\n-\t\t\t$_);\n-\t} @_;\n-}\n-\n-my %edit_hunk_manually_modes = (\n-\tstage => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for staging.\"),\n-\tstash => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for stashing.\"),\n-\treset_head => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for unstaging.\"),\n-\treset_nothead => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for applying.\"),\n-\tcheckout_index => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for discarding.\"),\n-\tcheckout_head => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for discarding.\"),\n-\tcheckout_nothead => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for applying.\"),\n-\tworktree_head => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for discarding.\"),\n-\tworktree_nothead => N__(\n-\"If the patch applies cleanly, the edited hunk will immediately be\n-marked for applying.\"),\n-);\n-\n-sub recount_edited_hunk {\n-\tlocal $_;\n-\tmy ($oldtext, $newtext) = @_;\n-\tmy ($o_cnt, $n_cnt) = (0, 0);\n-\tfor (@{$newtext}[1..$#{$newtext}]) {\n-\t\tmy $mode = substr($_, 0, 1);\n-\t\tif ($mode eq '-') {\n-\t\t\t$o_cnt++;\n-\t\t} elsif ($mode eq '+') {\n-\t\t\t$n_cnt++;\n-\t\t} elsif ($mode eq ' ' or $mode eq \"\\n\") {\n-\t\t\t$o_cnt++;\n-\t\t\t$n_cnt++;\n-\t\t}\n-\t}\n-\tmy ($o_ofs, undef, $n_ofs, undef) =\n-\t\t\t\t\tparse_hunk_header($newtext->[0]);\n-\t$newtext->[0] = format_hunk_header($o_ofs, $o_cnt, $n_ofs, $n_cnt);\n-\tmy (undef, $orig_o_cnt, undef, $orig_n_cnt) =\n-\t\t\t\t\tparse_hunk_header($oldtext->[0]);\n-\t# Return the change in the number of lines inserted by this hunk\n-\treturn $orig_o_cnt - $orig_n_cnt - $o_cnt + $n_cnt;\n-}\n-\n-sub edit_hunk_manually {\n-\tmy ($oldtext) = @_;\n-\n-\tmy $hunkfile = $repo->repo_path . \"/addp-hunk-edit.diff\";\n-\tmy $fh;\n-\topen $fh, '>', $hunkfile\n-\t\tor die sprintf(__(\"failed to open hunk edit file for writing: %s\"), $!);\n-\tprint $fh Git::comment_lines __(\"Manual hunk edit mode -- see bottom for a quick guide.\\n\");\n-\tprint $fh @$oldtext;\n-\tmy $is_reverse = $patch_mode_flavour{IS_REVERSE};\n-\tmy ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', '-');\n-\tmy $comment_line_char = Git::get_comment_line_char;\n-\tprint $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, $remove_plus, $comment_line_char),\n----\n-To remove '%s' lines, make them ' ' lines (context).\n-To remove '%s' lines, delete them.\n-Lines starting with %s will be removed.\n-EOF\n-__($edit_hunk_manually_modes{$patch_mode}),\n-# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.\n-__ <<EOF2 ;\n-If it does not apply cleanly, you will be given an opportunity to\n-edit again.  If all lines of the hunk are removed, then the edit is\n-aborted and the hunk is left unchanged.\n-EOF2\n-\tclose $fh;\n-\n-\tchomp(my ($editor) = run_cmd_pipe(qw(git var GIT_EDITOR)));\n-\tsystem('sh', '-c', $editor.' \"$@\"', $editor, $hunkfile);\n-\n-\tif ($? != 0) {\n-\t\treturn undef;\n-\t}\n-\n-\topen $fh, '<', $hunkfile\n-\t\tor die sprintf(__(\"failed to open hunk edit file for reading: %s\"), $!);\n-\tmy @newtext = grep { !/^\\Q$comment_line_char\\E/ } <$fh>;\n-\tclose $fh;\n-\tunlink $hunkfile;\n-\n-\t# Abort if nothing remains\n-\tif (!grep { /\\S/ } @newtext) {\n-\t\treturn undef;\n-\t}\n-\n-\t# Reinsert the first hunk header if the user accidentally deleted it\n-\tif ($newtext[0] !~ /^@/) {\n-\t\tunshift @newtext, $oldtext->[0];\n-\t}\n-\treturn \\@newtext;\n-}\n-\n-sub diff_applies {\n-\treturn run_git_apply($patch_mode_flavour{APPLY_CHECK} . ' --check',\n-\t\t\t     map { @{$_->{TEXT}} } @_);\n-}\n-\n-sub _restore_terminal_and_die {\n-\tReadMode 'restore';\n-\tprint \"\\n\";\n-\texit 1;\n-}\n-\n-sub prompt_single_character {\n-\tif ($use_readkey) {\n-\t\tlocal $SIG{TERM} = \\&_restore_terminal_and_die;\n-\t\tlocal $SIG{INT} = \\&_restore_terminal_and_die;\n-\t\tReadMode 'cbreak';\n-\t\tmy $key = ReadKey 0;\n-\t\tReadMode 'restore';\n-\t\tif (defined $key) {\n-\t\t\tif ($use_termcap and $key eq \"\\e\") {\n-\t\t\t\twhile (!defined $term_escapes{$key}) {\n-\t\t\t\t\tmy $next = ReadKey 0.5;\n-\t\t\t\t\tlast if (!defined $next);\n-\t\t\t\t\t$key .= $next;\n-\t\t\t\t}\n-\t\t\t\t$key =~ s/\\e/^[/;\n-\t\t\t}\n-\t\t\tprint \"$key\";\n-\t\t}\n-\t\tprint \"\\n\";\n-\t\treturn $key;\n-\t} else {\n-\t\treturn <STDIN>;\n-\t}\n-}\n-\n-sub prompt_yesno {\n-\tmy ($prompt) = @_;\n-\twhile (1) {\n-\t\tprint colored $prompt_color, $prompt;\n-\t\tmy $line = prompt_single_character;\n-\t\treturn undef unless defined $line;\n-\t\treturn 0 if $line =~ /^n/i;\n-\t\treturn 1 if $line =~ /^y/i;\n-\t}\n-}\n-\n-sub edit_hunk_loop {\n-\tmy ($head, $hunks, $ix) = @_;\n-\tmy $hunk = $hunks->[$ix];\n-\tmy $text = $hunk->{TEXT};\n-\n-\twhile (1) {\n-\t\tmy $newtext = edit_hunk_manually($text);\n-\t\tif (!defined $newtext) {\n-\t\t\treturn undef;\n-\t\t}\n-\t\tmy $newhunk = {\n-\t\t\tTEXT => $newtext,\n-\t\t\tTYPE => $hunk->{TYPE},\n-\t\t\tUSE => 1,\n-\t\t\tDIRTY => 1,\n-\t\t};\n-\t\t$newhunk->{OFS_DELTA} = recount_edited_hunk($text, $newtext);\n-\t\t# If this hunk has already been edited then add the\n-\t\t# offset delta of the previous edit to get the real\n-\t\t# delta from the original unedited hunk.\n-\t\t$hunk->{OFS_DELTA} and\n-\t\t\t\t$newhunk->{OFS_DELTA} += $hunk->{OFS_DELTA};\n-\t\tif (diff_applies($head,\n-\t\t\t\t @{$hunks}[0..$ix-1],\n-\t\t\t\t $newhunk,\n-\t\t\t\t @{$hunks}[$ix+1..$#{$hunks}])) {\n-\t\t\t$newhunk->{DISPLAY} = [color_diff(@{$newtext})];\n-\t\t\treturn $newhunk;\n-\t\t}\n-\t\telse {\n-\t\t\tprompt_yesno(\n-\t\t\t\t# TRANSLATORS: do not translate [y/n]\n-\t\t\t\t# The program will only accept that input\n-\t\t\t\t# at this point.\n-\t\t\t\t# Consider translating (saying \"no\" discards!) as\n-\t\t\t\t# (saying \"n\" for \"no\" discards!) if the translation\n-\t\t\t\t# of the word \"no\" does not start with n.\n-\t\t\t\t__('Your edited hunk does not apply. Edit again '\n-\t\t\t\t   . '(saying \"no\" discards!) [y/n]? ')\n-\t\t\t\t) or return undef;\n-\t\t}\n-\t}\n-}\n-\n-my %help_patch_modes = (\n-\tstage => N__(\n-\"y - stage this hunk\n-n - do not stage this hunk\n-q - quit; do not stage this hunk or any of the remaining ones\n-a - stage this hunk and all later hunks in the file\n-d - do not stage this hunk or any of the later hunks in the file\"),\n-\tstash => N__(\n-\"y - stash this hunk\n-n - do not stash this hunk\n-q - quit; do not stash this hunk or any of the remaining ones\n-a - stash this hunk and all later hunks in the file\n-d - do not stash this hunk or any of the later hunks in the file\"),\n-\treset_head => N__(\n-\"y - unstage this hunk\n-n - do not unstage this hunk\n-q - quit; do not unstage this hunk or any of the remaining ones\n-a - unstage this hunk and all later hunks in the file\n-d - do not unstage this hunk or any of the later hunks in the file\"),\n-\treset_nothead => N__(\n-\"y - apply this hunk to index\n-n - do not apply this hunk to index\n-q - quit; do not apply this hunk or any of the remaining ones\n-a - apply this hunk and all later hunks in the file\n-d - do not apply this hunk or any of the later hunks in the file\"),\n-\tcheckout_index => N__(\n-\"y - discard this hunk from worktree\n-n - do not discard this hunk from worktree\n-q - quit; do not discard this hunk or any of the remaining ones\n-a - discard this hunk and all later hunks in the file\n-d - do not discard this hunk or any of the later hunks in the file\"),\n-\tcheckout_head => N__(\n-\"y - discard this hunk from index and worktree\n-n - do not discard this hunk from index and worktree\n-q - quit; do not discard this hunk or any of the remaining ones\n-a - discard this hunk and all later hunks in the file\n-d - do not discard this hunk or any of the later hunks in the file\"),\n-\tcheckout_nothead => N__(\n-\"y - apply this hunk to index and worktree\n-n - do not apply this hunk to index and worktree\n-q - quit; do not apply this hunk or any of the remaining ones\n-a - apply this hunk and all later hunks in the file\n-d - do not apply this hunk or any of the later hunks in the file\"),\n-\tworktree_head => N__(\n-\"y - discard this hunk from worktree\n-n - do not discard this hunk from worktree\n-q - quit; do not discard this hunk or any of the remaining ones\n-a - discard this hunk and all later hunks in the file\n-d - do not discard this hunk or any of the later hunks in the file\"),\n-\tworktree_nothead => N__(\n-\"y - apply this hunk to worktree\n-n - do not apply this hunk to worktree\n-q - quit; do not apply this hunk or any of the remaining ones\n-a - apply this hunk and all later hunks in the file\n-d - do not apply this hunk or any of the later hunks in the file\"),\n-);\n-\n-sub help_patch_cmd {\n-\tlocal $_;\n-\tmy $other = $_[0] . \",?\";\n-\tprint colored $help_color, __($help_patch_modes{$patch_mode}), \"\\n\",\n-\t\tmap { \"$_\\n\" } grep {\n-\t\t\tmy $c = quotemeta(substr($_, 0, 1));\n-\t\t\t$other =~ /,$c/\n-\t\t} split \"\\n\", __ <<EOF ;\n-g - select a hunk to go to\n-/ - search for a hunk matching the given regex\n-j - leave this hunk undecided, see next undecided hunk\n-J - leave this hunk undecided, see next hunk\n-k - leave this hunk undecided, see previous undecided hunk\n-K - leave this hunk undecided, see previous hunk\n-s - split the current hunk into smaller hunks\n-e - manually edit the current hunk\n-? - print help\n-EOF\n-}\n-\n-sub apply_patch {\n-\tmy $cmd = shift;\n-\tmy $ret = run_git_apply $cmd, @_;\n-\tif (!$ret) {\n-\t\tprint STDERR @_;\n-\t}\n-\treturn $ret;\n-}\n-\n-sub apply_patch_for_checkout_commit {\n-\tmy $reverse = shift;\n-\tmy $applies_index = run_git_apply 'apply '.$reverse.' --cached --check', @_;\n-\tmy $applies_worktree = run_git_apply 'apply '.$reverse.' --check', @_;\n-\n-\tif ($applies_worktree && $applies_index) {\n-\t\trun_git_apply 'apply '.$reverse.' --cached', @_;\n-\t\trun_git_apply 'apply '.$reverse, @_;\n-\t\treturn 1;\n-\t} elsif (!$applies_index) {\n-\t\tprint colored $error_color, __(\"The selected hunks do not apply to the index!\\n\");\n-\t\tif (prompt_yesno __(\"Apply them to the worktree anyway? \")) {\n-\t\t\treturn run_git_apply 'apply '.$reverse, @_;\n-\t\t} else {\n-\t\t\tprint colored $error_color, __(\"Nothing was applied.\\n\");\n-\t\t\treturn 0;\n-\t\t}\n-\t} else {\n-\t\tprint STDERR @_;\n-\t\treturn 0;\n-\t}\n-}\n-\n-sub patch_update_cmd {\n-\tmy @all_mods = list_modified($patch_mode_flavour{FILTER});\n-\terror_msg sprintf(__(\"ignoring unmerged: %s\\n\"), $_->{VALUE})\n-\t\tfor grep { $_->{UNMERGED} } @all_mods;\n-\t@all_mods = grep { !$_->{UNMERGED} } @all_mods;\n-\n-\tmy @mods = grep { !($_->{BINARY}) } @all_mods;\n-\tmy @them;\n-\n-\tif (!@mods) {\n-\t\tif (@all_mods) {\n-\t\t\tprint STDERR __(\"Only binary files changed.\\n\");\n-\t\t} else {\n-\t\t\tprint STDERR __(\"No changes.\\n\");\n-\t\t}\n-\t\treturn 0;\n-\t}\n-\tif ($patch_mode_only) {\n-\t\t@them = @mods;\n-\t}\n-\telse {\n-\t\t@them = list_and_choose({ PROMPT => __('Patch update'),\n-\t\t\t\t\t  HEADER => $status_head, },\n-\t\t\t\t\t@mods);\n-\t}\n-\tfor (@them) {\n-\t\treturn 0 if patch_update_file($_->{VALUE});\n-\t}\n-}\n-\n-# Generate a one line summary of a hunk.\n-sub summarize_hunk {\n-\tmy $rhunk = shift;\n-\tmy $summary = $rhunk->{TEXT}[0];\n-\n-\t# Keep the line numbers, discard extra context.\n-\t$summary =~ s/@@(.*?)@@.*/$1 /s;\n-\t$summary .= \" \" x (20 - length $summary);\n-\n-\t# Add some user context.\n-\tfor my $line (@{$rhunk->{TEXT}}) {\n-\t\tif ($line =~ m/^[+-].*\\w/) {\n-\t\t\t$summary .= $line;\n-\t\t\tlast;\n-\t\t}\n-\t}\n-\n-\tchomp $summary;\n-\treturn substr($summary, 0, 80) . \"\\n\";\n-}\n-\n-\n-# Print a one-line summary of each hunk in the array ref in\n-# the first argument, starting with the index in the 2nd.\n-sub display_hunks {\n-\tmy ($hunks, $i) = @_;\n-\tmy $ctr = 0;\n-\t$i ||= 0;\n-\tfor (; $i < @$hunks && $ctr < 20; $i++, $ctr++) {\n-\t\tmy $status = \" \";\n-\t\tif (defined $hunks->[$i]{USE}) {\n-\t\t\t$status = $hunks->[$i]{USE} ? \"+\" : \"-\";\n-\t\t}\n-\t\tprintf \"%s%2d: %s\",\n-\t\t\t$status,\n-\t\t\t$i + 1,\n-\t\t\tsummarize_hunk($hunks->[$i]);\n-\t}\n-\treturn $i;\n-}\n-\n-my %patch_update_prompt_modes = (\n-\tstage => {\n-\t\tmode => N__(\"Stage mode change [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Stage deletion [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Stage addition [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Stage this hunk [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tstash => {\n-\t\tmode => N__(\"Stash mode change [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Stash deletion [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Stash addition [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Stash this hunk [y,n,q,a,d%s,?]? \"),\n-\t},\n-\treset_head => {\n-\t\tmode => N__(\"Unstage mode change [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Unstage deletion [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Unstage addition [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Unstage this hunk [y,n,q,a,d%s,?]? \"),\n-\t},\n-\treset_nothead => {\n-\t\tmode => N__(\"Apply mode change to index [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Apply deletion to index [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Apply addition to index [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Apply this hunk to index [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tcheckout_index => {\n-\t\tmode => N__(\"Discard mode change from worktree [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Discard deletion from worktree [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Discard addition from worktree [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Discard this hunk from worktree [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tcheckout_head => {\n-\t\tmode => N__(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Discard deletion from index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Discard addition from index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Discard this hunk from index and worktree [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tcheckout_nothead => {\n-\t\tmode => N__(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Apply addition to index and worktree [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Apply this hunk to index and worktree [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tworktree_head => {\n-\t\tmode => N__(\"Discard mode change from worktree [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Discard deletion from worktree [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Discard addition from worktree [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Discard this hunk from worktree [y,n,q,a,d%s,?]? \"),\n-\t},\n-\tworktree_nothead => {\n-\t\tmode => N__(\"Apply mode change to worktree [y,n,q,a,d%s,?]? \"),\n-\t\tdeletion => N__(\"Apply deletion to worktree [y,n,q,a,d%s,?]? \"),\n-\t\taddition => N__(\"Apply addition to worktree [y,n,q,a,d%s,?]? \"),\n-\t\thunk => N__(\"Apply this hunk to worktree [y,n,q,a,d%s,?]? \"),\n-\t},\n-);\n-\n-sub patch_update_file {\n-\tmy $quit = 0;\n-\tmy ($ix, $num);\n-\tmy $path = shift;\n-\tmy ($head, @hunk) = parse_diff($path);\n-\t($head, my $mode, my $deletion, my $addition) = parse_diff_header($head);\n-\tfor (@{$head->{DISPLAY}}) {\n-\t\tprint;\n-\t}\n-\n-\tif (@{$mode->{TEXT}}) {\n-\t\tunshift @hunk, $mode;\n-\t}\n-\tif (@{$deletion->{TEXT}}) {\n-\t\tforeach my $hunk (@hunk) {\n-\t\t\tpush @{$deletion->{TEXT}}, @{$hunk->{TEXT}};\n-\t\t\tpush @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};\n-\t\t}\n-\t\t@hunk = ($deletion);\n-\t}\n-\n-\t$num = scalar @hunk;\n-\t$ix = 0;\n-\n-\twhile (1) {\n-\t\tmy ($prev, $next, $other, $undecided, $i);\n-\t\t$other = '';\n-\n-\t\tlast if ($ix and !$num);\n-\t\tif ($num <= $ix) {\n-\t\t\t$ix = 0;\n-\t\t}\n-\t\tfor ($i = 0; $i < $ix; $i++) {\n-\t\t\tif (!defined $hunk[$i]{USE}) {\n-\t\t\t\t$prev = 1;\n-\t\t\t\t$other .= ',k';\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t}\n-\t\tif ($ix) {\n-\t\t\t$other .= ',K';\n-\t\t}\n-\t\tfor ($i = $ix + 1; $i < $num; $i++) {\n-\t\t\tif (!defined $hunk[$i]{USE}) {\n-\t\t\t\t$next = 1;\n-\t\t\t\t$other .= ',j';\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t}\n-\t\tif ($ix < $num - 1) {\n-\t\t\t$other .= ',J';\n-\t\t}\n-\t\tif ($num > 1) {\n-\t\t\t$other .= ',g,/';\n-\t\t}\n-\t\tfor ($i = 0; $i < $num; $i++) {\n-\t\t\tif (!defined $hunk[$i]{USE}) {\n-\t\t\t\t$undecided = 1;\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t}\n-\t\tlast if (!$undecided && ($num || !$addition));\n-\n-\t\tif ($num) {\n-\t\t\tif ($hunk[$ix]{TYPE} eq 'hunk' &&\n-\t\t\t    hunk_splittable($hunk[$ix]{TEXT})) {\n-\t\t\t\t$other .= ',s';\n-\t\t\t}\n-\t\t\tif ($hunk[$ix]{TYPE} eq 'hunk') {\n-\t\t\t\t$other .= ',e';\n-\t\t\t}\n-\t\t\tfor (@{$hunk[$ix]{DISPLAY}}) {\n-\t\t\t\tprint;\n-\t\t\t}\n-\t\t}\n-\t\tmy $type = $num ? $hunk[$ix]{TYPE} : $head->{TYPE};\n-\t\tprint colored $prompt_color, \"(\", ($ix+1), \"/\", ($num ? $num : 1), \") \",\n-\t\t\tsprintf(__($patch_update_prompt_modes{$patch_mode}{$type}), $other);\n-\n-\t\tmy $line = prompt_single_character;\n-\t\tlast unless defined $line;\n-\t\tif ($line) {\n-\t\t\tif ($line =~ /^y/i) {\n-\t\t\t\tif ($num) {\n-\t\t\t\t\t$hunk[$ix]{USE} = 1;\n-\t\t\t\t} else {\n-\t\t\t\t\t$head->{USE} = 1;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\telsif ($line =~ /^n/i) {\n-\t\t\t\tif ($num) {\n-\t\t\t\t\t$hunk[$ix]{USE} = 0;\n-\t\t\t\t} else {\n-\t\t\t\t\t$head->{USE} = 0;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\telsif ($line =~ /^a/i) {\n-\t\t\t\tif ($num) {\n-\t\t\t\t\twhile ($ix < $num) {\n-\t\t\t\t\t\tif (!defined $hunk[$ix]{USE}) {\n-\t\t\t\t\t\t\t$hunk[$ix]{USE} = 1;\n-\t\t\t\t\t\t}\n-\t\t\t\t\t\t$ix++;\n-\t\t\t\t\t}\n-\t\t\t\t} else {\n-\t\t\t\t\t$head->{USE} = 1;\n-\t\t\t\t\t$ix++;\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^g(.*)/) {\n-\t\t\t\tmy $response = $1;\n-\t\t\t\tunless ($other =~ /g/) {\n-\t\t\t\t\terror_msg __(\"No other hunks to goto\\n\");\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t\tmy $no = $ix > 10 ? $ix - 10 : 0;\n-\t\t\t\twhile ($response eq '') {\n-\t\t\t\t\t$no = display_hunks(\\@hunk, $no);\n-\t\t\t\t\tif ($no < $num) {\n-\t\t\t\t\t\tprint __(\"go to which hunk (<ret> to see more)? \");\n-\t\t\t\t\t} else {\n-\t\t\t\t\t\tprint __(\"go to which hunk? \");\n-\t\t\t\t\t}\n-\t\t\t\t\t$response = <STDIN>;\n-\t\t\t\t\tif (!defined $response) {\n-\t\t\t\t\t\t$response = '';\n-\t\t\t\t\t}\n-\t\t\t\t\tchomp $response;\n-\t\t\t\t}\n-\t\t\t\tif ($response !~ /^\\s*\\d+\\s*$/) {\n-\t\t\t\t\terror_msg sprintf(__(\"Invalid number: '%s'\\n\"),\n-\t\t\t\t\t\t\t     $response);\n-\t\t\t\t} elsif (0 < $response && $response <= $num) {\n-\t\t\t\t\t$ix = $response - 1;\n-\t\t\t\t} else {\n-\t\t\t\t\terror_msg sprintf(__n(\"Sorry, only %d hunk available.\\n\",\n-\t\t\t\t\t\t\t      \"Sorry, only %d hunks available.\\n\", $num), $num);\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^d/i) {\n-\t\t\t\tif ($num) {\n-\t\t\t\t\twhile ($ix < $num) {\n-\t\t\t\t\t\tif (!defined $hunk[$ix]{USE}) {\n-\t\t\t\t\t\t\t$hunk[$ix]{USE} = 0;\n-\t\t\t\t\t\t}\n-\t\t\t\t\t\t$ix++;\n-\t\t\t\t\t}\n-\t\t\t\t} else {\n-\t\t\t\t\t$head->{USE} = 0;\n-\t\t\t\t\t$ix++;\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^q/i) {\n-\t\t\t\tif ($num) {\n-\t\t\t\t\tfor ($i = 0; $i < $num; $i++) {\n-\t\t\t\t\t\tif (!defined $hunk[$i]{USE}) {\n-\t\t\t\t\t\t\t$hunk[$i]{USE} = 0;\n-\t\t\t\t\t\t}\n-\t\t\t\t\t}\n-\t\t\t\t} elsif (!defined $head->{USE}) {\n-\t\t\t\t\t$head->{USE} = 0;\n-\t\t\t\t}\n-\t\t\t\t$quit = 1;\n-\t\t\t\tlast;\n-\t\t\t}\n-\t\t\telsif ($line =~ m|^/(.*)|) {\n-\t\t\t\tmy $regex = $1;\n-\t\t\t\tunless ($other =~ m|/|) {\n-\t\t\t\t\terror_msg __(\"No other hunks to search\\n\");\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t\tif ($regex eq \"\") {\n-\t\t\t\t\tprint colored $prompt_color, __(\"search for regex? \");\n-\t\t\t\t\t$regex = <STDIN>;\n-\t\t\t\t\tif (defined $regex) {\n-\t\t\t\t\t\tchomp $regex;\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t\tmy $search_string;\n-\t\t\t\teval {\n-\t\t\t\t\t$search_string = qr{$regex}m;\n-\t\t\t\t};\n-\t\t\t\tif ($@) {\n-\t\t\t\t\tmy ($err,$exp) = ($@, $1);\n-\t\t\t\t\t$err =~ s/ at .*git-add--interactive line \\d+, <STDIN> line \\d+.*$//;\n-\t\t\t\t\terror_msg sprintf(__(\"Malformed search regexp %s: %s\\n\"), $exp, $err);\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t\tmy $iy = $ix;\n-\t\t\t\twhile (1) {\n-\t\t\t\t\tmy $text = join (\"\", @{$hunk[$iy]{TEXT}});\n-\t\t\t\t\tlast if ($text =~ $search_string);\n-\t\t\t\t\t$iy++;\n-\t\t\t\t\t$iy = 0 if ($iy >= $num);\n-\t\t\t\t\tif ($ix == $iy) {\n-\t\t\t\t\t\terror_msg __(\"No hunk matches the given pattern\\n\");\n-\t\t\t\t\t\tlast;\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t\t$ix = $iy;\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^K/) {\n-\t\t\t\tif ($other =~ /K/) {\n-\t\t\t\t\t$ix--;\n-\t\t\t\t}\n-\t\t\t\telse {\n-\t\t\t\t\terror_msg __(\"No previous hunk\\n\");\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^J/) {\n-\t\t\t\tif ($other =~ /J/) {\n-\t\t\t\t\t$ix++;\n-\t\t\t\t}\n-\t\t\t\telse {\n-\t\t\t\t\terror_msg __(\"No next hunk\\n\");\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^k/) {\n-\t\t\t\tif ($other =~ /k/) {\n-\t\t\t\t\twhile (1) {\n-\t\t\t\t\t\t$ix--;\n-\t\t\t\t\t\tlast if (!$ix ||\n-\t\t\t\t\t\t\t !defined $hunk[$ix]{USE});\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t\telse {\n-\t\t\t\t\terror_msg __(\"No previous hunk\\n\");\n-\t\t\t\t}\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^j/) {\n-\t\t\t\tif ($other !~ /j/) {\n-\t\t\t\t\terror_msg __(\"No next hunk\\n\");\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\telsif ($line =~ /^s/) {\n-\t\t\t\tunless ($other =~ /s/) {\n-\t\t\t\t\terror_msg __(\"Sorry, cannot split this hunk\\n\");\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t\tmy @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});\n-\t\t\t\tif (1 < @split) {\n-\t\t\t\t\tprint colored $header_color, sprintf(\n-\t\t\t\t\t\t__n(\"Split into %d hunk.\\n\",\n-\t\t\t\t\t\t    \"Split into %d hunks.\\n\",\n-\t\t\t\t\t\t    scalar(@split)), scalar(@split));\n-\t\t\t\t}\n-\t\t\t\tsplice (@hunk, $ix, 1, @split);\n-\t\t\t\t$num = scalar @hunk;\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\telsif ($line =~ /^e/) {\n-\t\t\t\tunless ($other =~ /e/) {\n-\t\t\t\t\terror_msg __(\"Sorry, cannot edit this hunk\\n\");\n-\t\t\t\t\tnext;\n-\t\t\t\t}\n-\t\t\t\tmy $newhunk = edit_hunk_loop($head, \\@hunk, $ix);\n-\t\t\t\tif (defined $newhunk) {\n-\t\t\t\t\tsplice @hunk, $ix, 1, $newhunk;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\telse {\n-\t\t\t\thelp_patch_cmd($other);\n-\t\t\t\tnext;\n-\t\t\t}\n-\t\t\t# soft increment\n-\t\t\twhile (1) {\n-\t\t\t\t$ix++;\n-\t\t\t\tlast if ($ix >= $num ||\n-\t\t\t\t\t !defined $hunk[$ix]{USE});\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\t@hunk = coalesce_overlapping_hunks(@hunk) if ($num);\n-\n-\tmy $n_lofs = 0;\n-\tmy @result = ();\n-\tfor (@hunk) {\n-\t\tif ($_->{USE}) {\n-\t\t\tpush @result, @{$_->{TEXT}};\n-\t\t}\n-\t}\n-\n-\tif (@result or $head->{USE}) {\n-\t\tmy @patch = reassemble_patch($head->{TEXT}, @result);\n-\t\tmy $apply_routine = $patch_mode_flavour{APPLY};\n-\t\t&$apply_routine(@patch);\n-\t\trefresh();\n-\t}\n-\n-\tprint \"\\n\";\n-\treturn $quit;\n-}\n-\n-sub diff_cmd {\n-\tmy @mods = list_modified('index-only');\n-\t@mods = grep { !($_->{BINARY}) } @mods;\n-\treturn if (!@mods);\n-\tmy (@them) = list_and_choose({ PROMPT => __('Review diff'),\n-\t\t\t\t     IMMEDIATE => 1,\n-\t\t\t\t     HEADER => $status_head, },\n-\t\t\t\t   @mods);\n-\treturn if (!@them);\n-\tmy $reference = (is_initial_commit()) ? get_empty_tree() : 'HEAD';\n-\tsystem(qw(git diff -p --cached), $reference, '--',\n-\t\tmap { $_->{VALUE} } @them);\n-}\n-\n-sub quit_cmd {\n-\tprint __(\"Bye.\\n\");\n-\texit(0);\n-}\n-\n-sub help_cmd {\n-# TRANSLATORS: please do not translate the command names\n-# 'status', 'update', 'revert', etc.\n-\tprint colored $help_color, __ <<'EOF' ;\n-status        - show paths with changes\n-update        - add working tree state to the staged set of changes\n-revert        - revert staged set of changes back to the HEAD version\n-patch         - pick hunks and update selectively\n-diff          - view diff between HEAD and index\n-add untracked - add contents of untracked files to the staged set of changes\n-EOF\n-}\n-\n-sub process_args {\n-\treturn unless @ARGV;\n-\tmy $arg = shift @ARGV;\n-\tif ($arg =~ /--patch(?:=(.*))?/) {\n-\t\tif (defined $1) {\n-\t\t\tif ($1 eq 'reset') {\n-\t\t\t\t$patch_mode = 'reset_head';\n-\t\t\t\t$patch_mode_revision = 'HEAD';\n-\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\tif ($arg ne '--') {\n-\t\t\t\t\t$patch_mode_revision = $arg;\n-\n-\t\t\t\t\t# NEEDSWORK: Instead of comparing to the literal \"HEAD\",\n-\t\t\t\t\t# compare the commit objects instead so that other ways of\n-\t\t\t\t\t# saying the same thing (such as \"@\") are also handled\n-\t\t\t\t\t# appropriately.\n-\t\t\t\t\t#\n-\t\t\t\t\t# This applies to the cases below too.\n-\t\t\t\t\t$patch_mode = ($arg eq 'HEAD' ?\n-\t\t\t\t\t\t       'reset_head' : 'reset_nothead');\n-\t\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\t}\n-\t\t\t} elsif ($1 eq 'checkout') {\n-\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\tif ($arg eq '--') {\n-\t\t\t\t\t$patch_mode = 'checkout_index';\n-\t\t\t\t} else {\n-\t\t\t\t\t$patch_mode_revision = $arg;\n-\t\t\t\t\t$patch_mode = ($arg eq 'HEAD' ?\n-\t\t\t\t\t\t       'checkout_head' : 'checkout_nothead');\n-\t\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\t}\n-\t\t\t} elsif ($1 eq 'worktree') {\n-\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\tif ($arg eq '--') {\n-\t\t\t\t\t$patch_mode = 'checkout_index';\n-\t\t\t\t} else {\n-\t\t\t\t\t$patch_mode_revision = $arg;\n-\t\t\t\t\t$patch_mode = ($arg eq 'HEAD' ?\n-\t\t\t\t\t\t       'worktree_head' : 'worktree_nothead');\n-\t\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t\t}\n-\t\t\t} elsif ($1 eq 'stage' or $1 eq 'stash') {\n-\t\t\t\t$patch_mode = $1;\n-\t\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t\t} else {\n-\t\t\t\tdie sprintf(__(\"unknown --patch mode: %s\"), $1);\n-\t\t\t}\n-\t\t} else {\n-\t\t\t$patch_mode = 'stage';\n-\t\t\t$arg = shift @ARGV or die __(\"missing --\");\n-\t\t}\n-\t\tdie sprintf(__(\"invalid argument %s, expecting --\"),\n-\t\t\t       $arg) unless $arg eq \"--\";\n-\t\t%patch_mode_flavour = %{$patch_modes{$patch_mode}};\n-\t\t$patch_mode_only = 1;\n-\t}\n-\telsif ($arg ne \"--\") {\n-\t\tdie sprintf(__(\"invalid argument %s, expecting --\"), $arg);\n-\t}\n-}\n-\n-sub main_loop {\n-\tmy @cmd = ([ 'status', \\&status_cmd, ],\n-\t\t   [ 'update', \\&update_cmd, ],\n-\t\t   [ 'revert', \\&revert_cmd, ],\n-\t\t   [ 'add untracked', \\&add_untracked_cmd, ],\n-\t\t   [ 'patch', \\&patch_update_cmd, ],\n-\t\t   [ 'diff', \\&diff_cmd, ],\n-\t\t   [ 'quit', \\&quit_cmd, ],\n-\t\t   [ 'help', \\&help_cmd, ],\n-\t);\n-\twhile (1) {\n-\t\tmy ($it) = list_and_choose({ PROMPT => __('What now'),\n-\t\t\t\t\t     SINGLETON => 1,\n-\t\t\t\t\t     LIST_FLAT => 4,\n-\t\t\t\t\t     HEADER => __('*** Commands ***'),\n-\t\t\t\t\t     ON_EOF => \\&quit_cmd,\n-\t\t\t\t\t     IMMEDIATE => 1 }, @cmd);\n-\t\tif ($it) {\n-\t\t\teval {\n-\t\t\t\t$it->[1]->();\n-\t\t\t};\n-\t\t\tif ($@) {\n-\t\t\t\tprint \"$@\";\n-\t\t\t}\n-\t\t}\n-\t}\n-}\n-\n-process_args();\n-refresh();\n-if ($patch_mode_only) {\n-\tpatch_update_cmd();\n-}\n-else {\n-\tstatus_cmd();\n-\tmain_loop();\n-}\ndiff --git a/t/README b/t/README\nindex 979b2d4833d..29576c37488 100644\n--- a/t/README\n+++ b/t/README\n@@ -449,10 +449,6 @@ the --sparse command-line argument.\n GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path\n by overriding the minimum number of cache entries required per thread.\n \n-GIT_TEST_ADD_I_USE_BUILTIN=<boolean>, when false, disables the\n-built-in version of git add -i. See 'add.interactive.useBuiltin' in\n-git-config(1).\n-\n GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading\n of the index for the whole test suite by bypassing the default number of\n cache entries and thread minimums. Setting this to 1 will make the\ndiff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh\nindex a5822e41af2..747eb5563ec 100755\n--- a/t/t2016-checkout-patch.sh\n+++ b/t/t2016-checkout-patch.sh\n@@ -4,12 +4,6 @@ test_description='git checkout --patch'\n \n . ./lib-patch-mode.sh\n \n-if ! test_have_prereq ADD_I_USE_BUILTIN && ! test_have_prereq PERL\n-then\n-\tskip_all='skipping interactive add tests, PERL not set'\n-\ttest_done\n-fi\n-\n test_expect_success 'setup' '\n \tmkdir dir &&\n \techo parent > dir/foo &&\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex 5841f280fb2..3a99837d9b1 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -7,7 +7,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-terminal.sh\n \n-if test_have_prereq !ADD_I_USE_BUILTIN,!PERL\n+if test_have_prereq !PERL\n then\n \tskip_all='skipping add -i (scripted) tests, perl not available'\n \ttest_done\n@@ -46,6 +46,21 @@ force_color () {\n \t)\n }\n \n+test_expect_success 'warn about add.interactive.useBuiltin' '\n+\tcat >expect <<-\\EOF &&\n+\twarning: the add.interactive.useBuiltin setting has been removed!\n+\tSee its entry in '\\''git help config'\\'' for details.\n+\tNo changes.\n+\tEOF\n+\n+\tfor v in = =true =false\n+\tdo\n+\t\tgit -c \"add.interactive.useBuiltin$v\" add -p >out 2>actual &&\n+\t\ttest_must_be_empty out &&\n+\t\ttest_cmp expect actual || return 1\n+\tdone\n+'\n+\n test_expect_success 'setup (initial)' '\n \techo content >file &&\n \tgit add file &&\n@@ -547,15 +562,7 @@ test_expect_success 'split hunk \"add -p (edit)\"' '\n \t! grep \"^+15\" actual\n '\n \n-test_expect_success 'setup ADD_I_USE_BUILTIN check' '\n-\tresult=success &&\n-\tif ! test_have_prereq ADD_I_USE_BUILTIN\n-\tthen\n-\t\tresult=failure\n-\tfi\n-'\n-\n-test_expect_$result 'split hunk \"add -p (no, yes, edit)\"' '\n+test_expect_success 'split hunk \"add -p (no, yes, edit)\"' '\n \ttest_write_lines 5 10 20 21 30 31 40 50 60 >test &&\n \tgit reset &&\n \t# test sequence is s(plit), n(o), y(es), e(dit)\n@@ -579,7 +586,7 @@ test_expect_success 'split hunk with incomplete line at end' '\n \ttest_must_fail git grep --cached before\n '\n \n-test_expect_$result 'edit, adding lines to the first hunk' '\n+test_expect_success 'edit, adding lines to the first hunk' '\n \ttest_write_lines 10 11 20 30 40 50 51 60 >test &&\n \tgit reset &&\n \ttr _ \" \" >patch <<-EOF &&\ndiff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh\nindex cada952f9ae..9fdafeb1e90 100755\n--- a/t/t6132-pathspec-exclude.sh\n+++ b/t/t6132-pathspec-exclude.sh\n@@ -293,11 +293,7 @@ test_expect_success 'add with all negative' '\n \ttest_cmp expect actual\n '\n \n-test_lazy_prereq ADD_I_USE_BUILTIN_OR_PERL '\n-\ttest_have_prereq ADD_I_USE_BUILTIN || test_have_prereq PERL\n-'\n-\n-test_expect_success ADD_I_USE_BUILTIN_OR_PERL 'add -p with all negative' '\n+test_expect_success 'add -p with all negative' '\n \tH=$(git rev-parse HEAD) &&\n \tgit reset --hard $H &&\n \tgit clean -f &&\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 01e88781dd2..d272cca008b 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -1937,10 +1937,6 @@ test_lazy_prereq SHA1 '\n \tesac\n '\n \n-test_lazy_prereq ADD_I_USE_BUILTIN '\n-\ttest_bool_env GIT_TEST_ADD_I_USE_BUILTIN true\n-'\n-\n # Ensure that no test accidentally triggers a Git command\n # that runs the actual maintenance scheduler, affecting a user's\n # system permanently.\n-- \n2.39.1.1425.gac85d95d48c\n\n"},{"id":"471631","messageId":"patch-v2-3.3-09bece75c22-20230206T225639Z-avarab@gmail.com","threadId":"59189","inReplyTo":"cover-v2-0.3-00000000000-20230206T225639Z-avarab@gmail.com","subject":"[PATCH v2 3/3] docs & comments: replace mentions of \"git-add--interactive.perl\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-02-06T22:58:58Z","receivedAt":"2023-02-06T22:59:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that we've removed \"git-add--interactive.perl\" let's replace\nmentions of it with \"add-interactive.c\". In the case of the \"git add\"\ndocumentation we were using it as an example filename, so the mention\nwasn't wrong, but using a dead file is slightly confusing.\n\nThe \"borrowed\" comment here likewise isn't wrong, but let's mention\nthe successor file instead. In the case of pathspec.c the implied TODO\nitem should refer to the current code (and the comment may not even be\ncurrent, I didn't check).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/git-add.txt | 6 +++---\n builtin/clean.c           | 2 +-\n pathspec.c                | 2 +-\n 3 files changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-add.txt b/Documentation/git-add.txt\nindex a030d33c6e7..ed44c1cb31c 100644\n--- a/Documentation/git-add.txt\n+++ b/Documentation/git-add.txt\n@@ -274,7 +274,7 @@ status::\n ------------\n               staged     unstaged path\n      1:       binary      nothing foo.png\n-     2:     +403/-35        +1/-1 git-add--interactive.perl\n+     2:     +403/-35        +1/-1 add-interactive.c\n ------------\n +\n It shows that foo.png has differences from HEAD (but that is\n@@ -282,7 +282,7 @@ binary so line count cannot be shown) and there is no\n difference between indexed copy and the working tree\n version (if the working tree version were also different,\n 'binary' would have been shown in place of 'nothing').  The\n-other file, git-add{litdd}interactive.perl, has 403 lines added\n+other file, add-interactive.c, has 403 lines added\n and 35 lines deleted if you commit what is in the index, but\n working tree file has further modifications (one addition and\n one deletion).\n@@ -303,7 +303,7 @@ like this:\n ------------\n            staged     unstaged path\n   1:       binary      nothing foo.png\n-* 2:     +403/-35        +1/-1 git-add--interactive.perl\n+* 2:     +403/-35        +1/-1 add-interactive.c\n ------------\n +\n To remove selection, prefix the input with `-`\ndiff --git a/builtin/clean.c b/builtin/clean.c\nindex b2701a28158..1e6d491d3be 100644\n--- a/builtin/clean.c\n+++ b/builtin/clean.c\n@@ -560,7 +560,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,\n \n /*\n  * Implement a git-add-interactive compatible UI, which is borrowed\n- * from git-add--interactive.perl.\n+ * from add-interactive.c.\n  *\n  * Return value:\n  *\ndiff --git a/pathspec.c b/pathspec.c\nindex dbcfe7b3216..ab70fcbe613 100644\n--- a/pathspec.c\n+++ b/pathspec.c\n@@ -545,7 +545,7 @@ static void NORETURN unsupported_magic(const char *pattern,\n \t}\n \t/*\n \t * We may want to substitute \"this command\" with a command\n-\t * name. E.g. when add--interactive dies when running\n+\t * name. E.g. when \"git add -p\" or \"git add -i\" dies when running\n \t * \"checkout -p\"\n \t */\n \tdie(_(\"%s: pathspec magic not supported by this command: %s\"),\n-- \n2.39.1.1425.gac85d95d48c\n\n"},{"id":"471633","messageId":"patch-v2-2.3-9299f22cc2c-20230206T225639Z-avarab@gmail.com","threadId":"59189","inReplyTo":"cover-v2-0.3-00000000000-20230206T225639Z-avarab@gmail.com","subject":"[PATCH v2 2/3] add API: remove run_add_interactive() wrapper function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-02-06T22:58:57Z","receivedAt":"2023-02-06T23:00:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Now that the Perl \"git-add--interactive\" has gone away in the\npreceding commit we don't need to pass along our desire for a mode as\na string, and can instead directly use the \"enum add_p_mode\", see\nd2a233cb8b9 (built-in add -p: prepare for patch modes other than\n\"stage\", 2019-12-21) for its introduction.\n\nAs a result of that the run_add_interactive() function would become a\ntrivial wrapper which would only run run_add_i() if a 0 (or now,\n\"NULL\") \"patch_mode\" was provided. Let's instead remove it, and have\nthe one callsite that wanted the \"NULL\" case (interactive_add())\nhandle it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/add.c      | 31 ++++---------------------------\n builtin/checkout.c | 12 +++++++-----\n builtin/reset.c    |  4 +++-\n builtin/stash.c    |  3 ++-\n commit.h           |  2 --\n 5 files changed, 16 insertions(+), 36 deletions(-)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 9f5b6e26a02..61dd386d109 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -238,30 +238,6 @@ static int refresh(int verbose, const struct pathspec *pathspec)\n \treturn ret;\n }\n \n-int run_add_interactive(const char *revision, const char *patch_mode,\n-\t\t\tconst struct pathspec *pathspec)\n-{\n-\tenum add_p_mode mode;\n-\n-\tif (!patch_mode)\n-\t\treturn !!run_add_i(the_repository, pathspec);\n-\n-\tif (!strcmp(patch_mode, \"--patch\"))\n-\t\tmode = ADD_P_ADD;\n-\telse if (!strcmp(patch_mode, \"--patch=stash\"))\n-\t\tmode = ADD_P_STASH;\n-\telse if (!strcmp(patch_mode, \"--patch=reset\"))\n-\t\tmode = ADD_P_RESET;\n-\telse if (!strcmp(patch_mode, \"--patch=checkout\"))\n-\t\tmode = ADD_P_CHECKOUT;\n-\telse if (!strcmp(patch_mode, \"--patch=worktree\"))\n-\t\tmode = ADD_P_WORKTREE;\n-\telse\n-\t\tdie(\"'%s' not supported\", patch_mode);\n-\n-\treturn !!run_add_p(the_repository, mode, revision, pathspec);\n-}\n-\n int interactive_add(const char **argv, const char *prefix, int patch)\n {\n \tstruct pathspec pathspec;\n@@ -277,9 +253,10 @@ int interactive_add(const char **argv, const char *prefix, int patch)\n \t\t       PATHSPEC_PREFIX_ORIGIN,\n \t\t       prefix, argv);\n \n-\treturn run_add_interactive(NULL,\n-\t\t\t\t   patch ? \"--patch\" : NULL,\n-\t\t\t\t   &pathspec);\n+\tif (patch)\n+\t\treturn !!run_add_p(the_repository, ADD_P_ADD, NULL, &pathspec);\n+\telse\n+\t\treturn !!run_add_i(the_repository, &pathspec);\n }\n \n static int edit_patch(int argc, const char **argv, const char *prefix)\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5963e1b74b8..a5155cf55c1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -29,6 +29,7 @@\n #include \"xdiff-interface.h\"\n #include \"entry.h\"\n #include \"parallel-checkout.h\"\n+#include \"add-interactive.h\"\n \n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n@@ -499,7 +500,7 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\t    \"--merge\", \"--conflict\", \"--staged\");\n \n \tif (opts->patch_mode) {\n-\t\tconst char *patch_mode;\n+\t\tenum add_p_mode patch_mode;\n \t\tconst char *rev = new_branch_info->name;\n \t\tchar rev_oid[GIT_MAX_HEXSZ + 1];\n \n@@ -517,15 +518,16 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\t\trev = oid_to_hex_r(rev_oid, &new_branch_info->commit->object.oid);\n \n \t\tif (opts->checkout_index && opts->checkout_worktree)\n-\t\t\tpatch_mode = \"--patch=checkout\";\n+\t\t\tpatch_mode = ADD_P_CHECKOUT;\n \t\telse if (opts->checkout_index && !opts->checkout_worktree)\n-\t\t\tpatch_mode = \"--patch=reset\";\n+\t\t\tpatch_mode = ADD_P_RESET;\n \t\telse if (!opts->checkout_index && opts->checkout_worktree)\n-\t\t\tpatch_mode = \"--patch=worktree\";\n+\t\t\tpatch_mode = ADD_P_WORKTREE;\n \t\telse\n \t\t\tBUG(\"either flag must have been set, worktree=%d, index=%d\",\n \t\t\t    opts->checkout_worktree, opts->checkout_index);\n-\t\treturn run_add_interactive(rev, patch_mode, &opts->pathspec);\n+\t\treturn !!run_add_p(the_repository, patch_mode, rev,\n+\t\t\t\t   &opts->pathspec);\n \t}\n \n \trepo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);\ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex fea20a9ba0b..4b59aa9aeae 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -26,6 +26,7 @@\n #include \"submodule.h\"\n #include \"submodule-config.h\"\n #include \"dir.h\"\n+#include \"add-interactive.h\"\n \n #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n \n@@ -390,7 +391,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\tif (reset_type != NONE)\n \t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--patch\", \"--{hard,mixed,soft}\");\n \t\ttrace2_cmd_mode(\"patch-interactive\");\n-\t\treturn run_add_interactive(rev, \"--patch=reset\", &pathspec);\n+\t\treturn !!run_add_p(the_repository, ADD_P_RESET, rev,\n+\t\t\t\t   &pathspec);\n \t}\n \n \t/* git reset tree [--] paths... can be used to\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 839569a9803..9fcd2e5d993 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -18,6 +18,7 @@\n #include \"diffcore.h\"\n #include \"exec-cmd.h\"\n #include \"reflog.h\"\n+#include \"add-interactive.h\"\n \n #define INCLUDE_ALL_FILES 2\n \n@@ -1229,7 +1230,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,\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+\tret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps);\n \n \tthe_repository->index_file = old_repo_index_file;\n \tif (old_index_env && *old_index_env)\ndiff --git a/commit.h b/commit.h\nindex fa39202fa6b..cc2c5da7bdb 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -274,8 +274,6 @@ struct ref;\n int for_each_commit_graft(each_commit_graft_fn, void *);\n \n int interactive_add(const char **argv, const char *prefix, int patch);\n-int run_add_interactive(const char *revision, const char *patch_mode,\n-\t\t\tconst struct pathspec *pathspec);\n \n struct commit_extra_header {\n \tstruct commit_extra_header *next;\n-- \n2.39.1.1425.gac85d95d48c\n\n"},{"id":"471636","messageId":"xmqqbkm62y6m.fsf@gitster.g","threadId":"59189","inReplyTo":"cover-v2-0.3-00000000000-20230206T225639Z-avarab@gmail.com","subject":"Re: [PATCH v2 0/3] add: remove Perl version of \"git add -[pi]\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-02-06T23:04:17Z","receivedAt":"2023-02-06T23:04:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"With Philippe's fix-up, this round looks good to me.\n\nThanks, will queue.\n"},{"id":"471673","messageId":"25ce4764-177b-c961-9df2-1543a4e9e149@dunelm.org.uk","threadId":"59189","inReplyTo":"cover-v2-0.3-00000000000-20230206T225639Z-avarab@gmail.com","subject":"Re: [PATCH v2 0/3] add: remove Perl version of \"git add -[pi]\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2023-02-07T10:21:13Z","receivedAt":"2023-02-07T10:21:18Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 06/02/2023 22:58, Ævar Arnfjörð Bjarmason wrote:\n> We've given the \"git add -i\" migration away from Perl to a built-in\n> enough time to remove the old Perl code.\n> \n> For v1, see:\n> https://lore.kernel.org/git/cover-0.3-00000000000-20230203T125859Z-avarab@gmail.com/\n> \n> Branch & CI for this at:\n> https://github.com/avar/git/tree/avar/nuke-perl-add--interactive-2\n> \n> The only change from v1 is to squash in a suggested change from\n> Philippe Blain, thanks!\n\nThanks for doing this, it will be nice to only have a single \nimplementation to worry about.\n\nBest Wishes\n\nPhillip\n\n\n> Ævar Arnfjörð Bjarmason (3):\n>    add: remove \"add.interactive.useBuiltin\" & Perl \"git add--interactive\"\n>    add API: remove run_add_interactive() wrapper function\n>    docs & comments: replace mentions of \"git-add--interactive.perl\"\n> \n>   .gitignore                   |    1 -\n>   Documentation/config/add.txt |    7 +-\n>   Documentation/git-add.txt    |    6 +-\n>   INSTALL                      |    2 +-\n>   Makefile                     |    1 -\n>   builtin/add.c                |   61 +-\n>   builtin/checkout.c           |   12 +-\n>   builtin/clean.c              |    2 +-\n>   builtin/reset.c              |    4 +-\n>   builtin/stash.c              |    3 +-\n>   ci/run-build-and-tests.sh    |    1 -\n>   commit.h                     |    2 -\n>   git-add--interactive.perl    | 1920 ----------------------------------\n>   pathspec.c                   |    2 +-\n>   t/README                     |    4 -\n>   t/t2016-checkout-patch.sh    |    6 -\n>   t/t3701-add-interactive.sh   |   29 +-\n>   t/t6132-pathspec-exclude.sh  |    6 +-\n>   t/test-lib.sh                |    4 -\n>   19 files changed, 50 insertions(+), 2023 deletions(-)\n>   delete mode 100755 git-add--interactive.perl\n> \n> Range-diff against v1:\n> 1:  f39dde93011 ! 1:  71c7922b25f add: remove \"add.interactive.useBuiltin\" & Perl \"git add--interactive\"\n>      @@ Documentation/config/add.txt: add.ignore-errors (deprecated)::\n>       +\tinteractive mode, which then became the default in Git\n>       +\tversions v2.37.0 to v2.39.0.\n>       \n>      + ## INSTALL ##\n>      +@@ INSTALL: Issues of note:\n>      + \t  for everyday use (e.g. \"bisect\", \"request-pull\").\n>      +\n>      + \t- \"Perl\" version 5.8 or later is needed to use some of the\n>      +-\t  features (e.g. preparing a partial commit using \"git add -i/-p\",\n>      ++\t  features (e.g. sending patches using \"git send-email\",\n>      + \t  interacting with svn repositories with \"git svn\").  If you can\n>      + \t  live without these, use NO_PERL.  Note that recent releases of\n>      + \t  Redhat/Fedora are reported to ship Perl binary package with some\n>      +\n>        ## Makefile ##\n>       @@ Makefile: SCRIPT_LIB += git-mergetool--lib\n>        SCRIPT_LIB += git-sh-i18n\n> 2:  fb0dafaf4a4 = 2:  9299f22cc2c add API: remove run_add_interactive() wrapper function\n> 3:  6ee56de1371 = 3:  09bece75c22 docs & comments: replace mentions of \"git-add--interactive.perl\"\n"}]}